diff --git a/.detoxrc.js b/.detoxrc.js new file mode 100644 index 000000000..dbdf65a98 --- /dev/null +++ b/.detoxrc.js @@ -0,0 +1,88 @@ +/** @type {Detox.DetoxConfig} */ +module.exports = { + testRunner: { + args: { + $0: 'jest', + config: 'e2e/jest.config.js', + }, + jest: { + setupTimeout: 120000, + }, + }, + apps: { + 'ios.debug': { + type: 'ios.app', + binaryPath: + 'example/ios/build/Build/Products/Debug-iphonesimulator/example.app', + build: + 'xcodebuild -workspace example/ios/example.xcworkspace -scheme example -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build', + }, + 'ios.release': { + type: 'ios.app', + binaryPath: + 'example/ios/build/Build/Products/Release-iphonesimulator/example.app', + build: + 'xcodebuild -workspace example/ios/example.xcworkspace -scheme example -configuration Release -sdk iphonesimulator -derivedDataPath ios/build', + }, + 'android.debug': { + type: 'android.apk', + binaryPath: 'example/android/app/build/outputs/apk/debug/app-debug.apk', + build: + 'cd example/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', + reversePorts: [8081], + }, + 'android.release': { + type: 'android.apk', + binaryPath: + 'example/android/app/build/outputs/apk/release/app-release.apk', + build: + 'cd example/android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release', + }, + }, + devices: { + simulator: { + type: 'ios.simulator', + device: { + type: 'iPhone 14', + }, + }, + attached: { + type: 'android.attached', + device: { + adbName: '.*', + }, + }, + emulator: { + type: 'android.emulator', + device: { + avdName: 'Pixel_3a_API_30_x86', + }, + }, + }, + configurations: { + 'ios.sim.debug': { + device: 'simulator', + app: 'ios.debug', + }, + 'ios.sim.release': { + device: 'simulator', + app: 'ios.release', + }, + 'android.att.debug': { + device: 'attached', + app: 'android.debug', + }, + 'android.att.release': { + device: 'attached', + app: 'android.release', + }, + 'android.emu.debug': { + device: 'emulator', + app: 'android.debug', + }, + 'android.emu.release': { + device: 'emulator', + app: 'android.release', + }, + }, +}; diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..b62996a06 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + root: true, + extends: '@react-native', + ignorePatterns: 'lib', // ts outdir + settings: { + react: { + version: '18.3.1', + }, + }, +}; diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index 8eadd339f..000000000 --- a/.flowconfig +++ /dev/null @@ -1,63 +0,0 @@ -[ignore] - -# We fork some components by platform. -.*/*.web.js -.*/*.android.js - -# Some modules have their own node_modules with overlap -.*/node_modules/node-haste/.* - -# Ugh -.*/node_modules/babel.* -.*/node_modules/babylon.* -.*/node_modules/invariant.* - -# Ignore react and fbjs where there are overlaps, but don't ignore -# anything that react-native relies on -.*/node_modules/fbjs-haste/.*/__tests__/.* -.*/node_modules/fbjs-haste/__forks__/Map.js -.*/node_modules/fbjs-haste/__forks__/Promise.js -.*/node_modules/fbjs-haste/__forks__/fetch.js -.*/node_modules/fbjs-haste/core/ExecutionEnvironment.js -.*/node_modules/fbjs-haste/core/isEmpty.js -.*/node_modules/fbjs-haste/crypto/crc32.js -.*/node_modules/fbjs-haste/stubs/ErrorUtils.js -.*/node_modules/react-haste/React.js -.*/node_modules/react-haste/renderers/dom/ReactDOM.js -.*/node_modules/react-haste/renderers/shared/event/eventPlugins/ResponderEventPlugin.js - -# Ignore commoner tests -.*/node_modules/commoner/test/.* - -# See https://github.com/facebook/flow/issues/442 -.*/react-tools/node_modules/commoner/lib/reader.js - -# Ignore jest -.*/node_modules/jest-cli/.* - -# Ignore Website -.*/website/.* - -[include] - -[libs] -node_modules/react-native/Libraries/react-native/react-native-interface.js - -[options] -module.system=haste - -munge_underscores=true - -module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' -module.name_mapper='^[./a-zA-Z0-9$_-]+\.png$' -> 'RelativeImageStub' - -suppress_type=$FlowIssue -suppress_type=$FlowFixMe -suppress_type=$FixMe - -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-8]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-8]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy - -[version] -0.18.1 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..afc76168e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +/.github/workflows/ @Simon-TechForm @christopherdro @salah-ghanim @GETAWAY-DEV +/.github/CODEOWNERS @Simon-TechForm @christopherdro @salah-ghanim @GETAWAY-DEV diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..5fd12193b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,138 @@ +name: "\U0001F41B Bug Report" +description: Report a reproducible bug or regression in this library. +labels: [bug] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + - type: textarea + id: summary + attributes: + label: Summary + description: Provide a clear and concise description of what the bug is. + validations: + required: true + + - type: textarea + id: reproducible-code + attributes: + label: Reproducible sample code + render: TSX + description: | + [Minimum amount of code](https://stackoverflow.com/help/minimal-reproducible-example) to reproduce the bug. + Please spend the time needed to trim down your code as much as possible and make sure the code you provide runs "as is", + as failing to do so will make reproducing every bug very time consuming for the maintainers. + **This will be automatically formatted into code, so no need for backticks.** + + placeholder: | + import React from 'react'; + import MapView from 'react-native-maps'; + + export default function App() { + return ( + + ); + } + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to reproduce + description: Explain the steps we need to take to reproduce the issue + validations: + required: true + + - type: textarea + id: exptected + attributes: + label: Expected result + description: How did you expect your project to behave? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual result + description: | + How did it actually behave? + If you find that it would help understand the issue, please attach screenshot(s). + This is mostly relevant for issues regarding UI bugs. + validations: + required: true + + - type: input + id: version + attributes: + label: React Native Maps Version + description: What version of react-native-maps are you running? + placeholder: 1.1.0 + validations: + required: true + + - type: dropdown + id: platforms + attributes: + label: What platforms are you seeing the problem on? + description: You can select more than one option + multiple: true + options: + - Android + - iOS (Apple Maps) + - iOS (Google Maps) + validations: + required: true + + - type: input + id: rn-version + attributes: + label: React Native Version + description: What version of react-native are you running? + placeholder: 0.69.2 + validations: + required: true + + - type: dropdown + id: expo + attributes: + label: What version of Expo are you using? + options: + - 'Not using Expo' + - 'SDK 45' + - 'SDK 46' + - 'SDK 47' + - 'SDK 48' + - 'SDK 49' + - 'SDK 50' + - 'SDK 51' + - 'SDK 52' + - 'SDK 53' + validations: + required: true + + - type: input + id: device + attributes: + label: Device(s) + description: What device(s) are you able to reproduce this on? + placeholder: iPhone 14 Pro (12.4), Samsung Galaxy S10 (Android 11) + validations: + required: true + + - type: textarea + id: additional + attributes: + label: Additional information + description: Anything else you want us to know about the bug? diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..3ba13e0ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..040aaebf4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,53 @@ + + +### Does any other open PR do the same thing? + + + +(please answer here) + +### What issue is this PR fixing? + +(please link the issue here) + +### How did you test this PR? + + + +(please answer here) + + diff --git a/.github/workflows/androidBuild.yml b/.github/workflows/androidBuild.yml new file mode 100644 index 000000000..7646865ab --- /dev/null +++ b/.github/workflows/androidBuild.yml @@ -0,0 +1,43 @@ +name: Android build + +on: + pull_request: + +jobs: + build-android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup JDK 17 + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + + - name: Setup Android SDK + uses: amyu/setup-android@v4 + with: + cache-disabled: true + sdk-version: 34 + build-tools-version: 33.0.2 + + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Clear Gradle Cache + run: rm -rf $HOME/.gradle/caches/ + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + cache: yarn + + - name: Install dependencies + run: yarn --cwd example --frozen-lockfile + + - name: Write local.properties + run: echo MAPS_API_KEY=your_api_key_here >> example/android/local.properties + + - name: Build Android + run: cd example/android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug diff --git a/.github/workflows/iosBuild.yml b/.github/workflows/iosBuild.yml new file mode 100644 index 000000000..b75abcda8 --- /dev/null +++ b/.github/workflows/iosBuild.yml @@ -0,0 +1,37 @@ +name: iOS build + +on: + pull_request: + +jobs: + build-ios: + runs-on: macos-latest + + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + cache: yarn + + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.2' + bundler-cache: true + working-directory: example + + - name: Install dependencies + run: yarn --cwd example --frozen-lockfile + + - name: Install pods + run: yarn pod-install + + - name: Write Config.xcconfig + run: echo MAPS_API_KEY=your_api_key_here >> example/ios/Config.xcconfig + + - name: Build iOS + run: xcodebuild -workspace example/ios/rnmshowcase.xcworkspace -scheme rnmshowcase -configuration Debug -sdk iphonesimulator diff --git a/.github/workflows/iosFrameworksBuild.yml b/.github/workflows/iosFrameworksBuild.yml new file mode 100644 index 000000000..6d43ecdb7 --- /dev/null +++ b/.github/workflows/iosFrameworksBuild.yml @@ -0,0 +1,38 @@ +name: iOS frameworks build + +on: + pull_request: + +jobs: + build-ios-frameworks: + runs-on: macos-latest + + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + cache: yarn + + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2.2' + bundler-cache: true + working-directory: example + + - name: Install dependencies + run: yarn --cwd example --frozen-lockfile + + - name: Install pods + run: USE_FRAMEWORKS=static RCT_NEW_ARCH_ENABLED=0 NO_FLIPPER=1 bundle exec pod install + working-directory: example/ios + + - name: Write Config.xcconfig + run: echo MAPS_API_KEY=your_api_key_here >> example/ios/Config.xcconfig + + - name: Build iOS + run: xcodebuild -workspace example/ios/rnmshowcase.xcworkspace -scheme rnmshowcase -configuration Debug -sdk iphonesimulator diff --git a/.github/workflows/pullRequest.yml b/.github/workflows/pullRequest.yml new file mode 100644 index 000000000..fc23b9aa0 --- /dev/null +++ b/.github/workflows/pullRequest.yml @@ -0,0 +1,37 @@ +name: Pull Request + +on: + pull_request: + +jobs: + lint-test: + runs-on: ubuntu-latest + + steps: + - name: Check out repository code + uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + cache: yarn + + - name: Install dependencies + run: yarn --frozen-lockfile + + - name: Install example dependencies + run: yarn --frozen-lockfile + working-directory: example + + - name: Lint + run: yarn lint + + - name: TsCheck + run: yarn tscheck + + - name: Check formatting + run: yarn format-check + + - name: Test + run: yarn test diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 000000000..112012a45 --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,44 @@ +name: Push +concurrency: + group: push +on: + push: + branches: + - master + - beta +jobs: + release: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + env: + HUSKY: 0 + steps: + - name: Check out repository code + uses: actions/checkout@v3 + with: + persist-credentials: false + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: 'lts/*' + cache: yarn + + - name: Install dependencies + run: yarn --frozen-lockfile + + - name: Build + run: yarn build + + # Latest version of npm needed for provenance + - name: Install latest npm + run: npm install -g npm@latest + + - name: Release + env: + GITHUB_TOKEN: ${{secrets.CI_GITHUB_TOKEN}} + NPM_TOKEN: ${{secrets.NPM_TOKEN}} + NPM_CONFIG_PROVENANCE: true + run: yarn release diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 000000000..f99cf7342 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,19 @@ +name: 'Run stale action' +on: + schedule: + - cron: '30 1 * * *' + +jobs: + stale: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v7 + with: + exempt-issue-labels: reproducible + days-before-stale: 90 + days-before-close: 14 + stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If the issue remains relevant, simply comment `Still relevant` and the issue will remain open. Thank you for your contributions.' + stale-pr-message: 'This PR has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If the PR remains relevant, simply comment `Still relevant` and the PR will remain open. Thank you for your contributions.' diff --git a/.gitignore b/.gitignore index 0253e8835..dabd601e5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,15 +21,67 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace +Pods/ +AirMapsExplorer.xcworkspace/ +**/.xcode.env.local -# Android/IJ +# Android/IntelliJ # +build/ .idea -*.iml .gradle local.properties +*.iml +*.hprof +.cxx/ +*.keystore +!debug.keystore # node.js # node_modules/ npm-debug.log +yarn-error.log + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +**/fastlane/report.xml +**/fastlane/Preview.html +**/fastlane/screenshots +**/fastlane/test_output + +# Bundle artifact +*.jsbundle + +# VSCode workspace settings +.vscode + +# ts outDir +lib/ + +example/vendor/bundle/ + +example/ios/Config.xcconfig + +# Ruby / CocoaPods +**/Pods/ +/vendor/bundle/ + +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* + +# testing +/coverage + +# Yarn +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 000000000..44c73fd9f --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..70cf2d488 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,7 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint +yarn tscheck +yarn format-check +yarn test diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 33a9488b1..000000000 --- a/.npmignore +++ /dev/null @@ -1 +0,0 @@ -example diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..30a9f102e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +# Add files here to ignore them from prettier formatting + +CHANGELOG.md +example/ios +example/android +android/build +example/vendor/bundle/ +ios/DerivedData diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..fe440a6a0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "arrowParens": "avoid", + "bracketSameLine": true, + "bracketSpacing": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 000000000..87f0e4481 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,21 @@ +{ + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/changelog", + { + "changelogTitle": "# Changelog" + } + ], + "@semantic-release/npm", + [ + "@semantic-release/git", + { + "assets": ["CHANGELOG.md"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + "@semantic-release/github" + ] +} diff --git a/.watchmanconfig b/.watchmanconfig deleted file mode 100644 index 9e26dfeeb..000000000 --- a/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 93d415781..d37724fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,1429 @@ -# Change Log +# Changelog + +## [1.20.1](https://github.com/react-native-maps/react-native-maps/compare/v1.20.0...v1.20.1) (2024-11-22) + + +### Bug Fixes + +* **ios:** color update in AIRGoogleMap ([#5227](https://github.com/react-native-maps/react-native-maps/issues/5227)) ([f445546](https://github.com/react-native-maps/react-native-maps/commit/f44554637a93de5f505bbf199d2a5fee84c76695)) + +# [1.20.0](https://github.com/react-native-maps/react-native-maps/compare/v1.19.1...v1.20.0) (2024-11-10) + + +### Features + +* **android:** add support for disabling PoIClick ([#5210](https://github.com/react-native-maps/react-native-maps/issues/5210)) ([d92e283](https://github.com/react-native-maps/react-native-maps/commit/d92e28365ac42fda4b298f0a6b352cc297566660)) + +## [1.19.1](https://github.com/react-native-maps/react-native-maps/compare/v1.19.0...v1.19.1) (2024-11-10) + + +### Bug Fixes + +* Remove unstable_reactLegacyComponentNames ([#5209](https://github.com/react-native-maps/react-native-maps/issues/5209)) ([33112be](https://github.com/react-native-maps/react-native-maps/commit/33112be8bd4d8c817df5875e08131f3286a10477)) + +# [1.19.0](https://github.com/react-native-maps/react-native-maps/compare/v1.18.4...v1.19.0) (2024-11-09) + + +### Features + +* add onRegionChangeStart event to MapView ([#5144](https://github.com/react-native-maps/react-native-maps/issues/5144)) ([eeb56f9](https://github.com/react-native-maps/react-native-maps/commit/eeb56f9f08c636eea928f383ce63d3ed1521ff8b)) + +## [1.18.4](https://github.com/react-native-maps/react-native-maps/compare/v1.18.3...v1.18.4) (2024-11-09) + + +### Bug Fixes + +* **Android:** fix multiple padding related issues on Android ([#5150](https://github.com/react-native-maps/react-native-maps/issues/5150), [#5152](https://github.com/react-native-maps/react-native-maps/issues/5152), [#5153](https://github.com/react-native-maps/react-native-maps/issues/5153), [#5154](https://github.com/react-native-maps/react-native-maps/issues/5154)) ([#5151](https://github.com/react-native-maps/react-native-maps/issues/5151)) ([0009170](https://github.com/react-native-maps/react-native-maps/commit/0009170f26735eda47f1d7316a0642de3c9a952c)) + +## [1.18.3](https://github.com/react-native-maps/react-native-maps/compare/v1.18.2...v1.18.3) (2024-11-09) + + +### Bug Fixes + +* **iOS:** Refactor onPress(Marker) to return nativeEvent.position for consistency with Android ([#5196](https://github.com/react-native-maps/react-native-maps/issues/5196)) ([956783f](https://github.com/react-native-maps/react-native-maps/commit/956783f90df7053536240a2f0bc51fee094a4b1e)), closes [#4996](https://github.com/react-native-maps/react-native-maps/issues/4996) + +## [1.18.2](https://github.com/react-native-maps/react-native-maps/compare/v1.18.1...v1.18.2) (2024-10-14) + + +### Bug Fixes + +* correctly check for iOS OS version before using cameraZoomRange ([#5185](https://github.com/react-native-maps/react-native-maps/issues/5185)) ([4efd881](https://github.com/react-native-maps/react-native-maps/commit/4efd881ffb4995f77d7f325455053d30c50cd429)) + +## [1.18.1](https://github.com/react-native-maps/react-native-maps/compare/v1.18.0...v1.18.1) (2024-10-12) + + +### Bug Fixes + +* documentation on README.md ([#5174](https://github.com/react-native-maps/react-native-maps/issues/5174)) ([b853f3a](https://github.com/react-native-maps/react-native-maps/commit/b853f3ae28e72aa107eb61db79a90adb00f03492)) + +# [1.18.0](https://github.com/react-native-maps/react-native-maps/compare/v1.17.3...v1.18.0) (2024-08-18) + + +### Features + +* add anchor and centerOffset prop to GeoJson component to be passed to Marker ([#5140](https://github.com/react-native-maps/react-native-maps/issues/5140)) ([266be79](https://github.com/react-native-maps/react-native-maps/commit/266be79e924298633e60aed5c0b86d077fa479cc)), closes [#5139](https://github.com/react-native-maps/react-native-maps/issues/5139) + +## [1.17.3](https://github.com/react-native-maps/react-native-maps/compare/v1.17.2...v1.17.3) (2024-07-27) + + +### Bug Fixes + +* **Android:** Added A11y for Map and Marker ([#5091](https://github.com/react-native-maps/react-native-maps/issues/5091)) ([f3f5a4e](https://github.com/react-native-maps/react-native-maps/commit/f3f5a4e45cb4a27624c87999d366710c17d4daee)) + +## [1.17.2](https://github.com/react-native-maps/react-native-maps/compare/v1.17.1...v1.17.2) (2024-07-27) + + +### Bug Fixes + +* export PolylinePressEvent ([#5125](https://github.com/react-native-maps/react-native-maps/issues/5125)) ([27fec3a](https://github.com/react-native-maps/react-native-maps/commit/27fec3a993af8fc5a21b9a303b34d73bc34f1914)) + +## [1.17.1](https://github.com/react-native-maps/react-native-maps/compare/v1.17.0...v1.17.1) (2024-07-21) + + +### Bug Fixes + +* **iOS:** ensure onPress(Marker) returns nativeEvent.position [#4996](https://github.com/react-native-maps/react-native-maps/issues/4996) ([#5092](https://github.com/react-native-maps/react-native-maps/issues/5092)) ([b2d2fd8](https://github.com/react-native-maps/react-native-maps/commit/b2d2fd8cddb8808a41ae881f87a7295ac78f5b06)) + +# [1.17.0](https://github.com/react-native-maps/react-native-maps/compare/v1.16.0...v1.17.0) (2024-07-21) + + +### Features + +* **android:** add dash pattern for Android Polygon ([#5115](https://github.com/react-native-maps/react-native-maps/issues/5115)) ([#5121](https://github.com/react-native-maps/react-native-maps/issues/5121)) ([4001ae8](https://github.com/react-native-maps/react-native-maps/commit/4001ae89dd82e6f0a247d3255f3cdaba253a0fcd)) + +# [1.16.0](https://github.com/react-native-maps/react-native-maps/compare/v1.15.7...v1.16.0) (2024-07-20) + + +### Features + +* **ios:** allow use of light compass theme with satellite/hybrid map… ([#5099](https://github.com/react-native-maps/react-native-maps/issues/5099)) ([befb86e](https://github.com/react-native-maps/react-native-maps/commit/befb86e0aa4e8f517301d01bc084246e21c08c0b)) + +## [1.15.7](https://github.com/react-native-maps/react-native-maps/compare/v1.15.6...v1.15.7) (2024-07-20) + + +### Bug Fixes + +* **ios:** Implement dash options for geojson polygon ([#5115](https://github.com/react-native-maps/react-native-maps/issues/5115)) ([5dc9381](https://github.com/react-native-maps/react-native-maps/commit/5dc9381ae600e239b5aa8a818f94a88dc161d8fb)) + +## [1.15.6](https://github.com/react-native-maps/react-native-maps/compare/v1.15.5...v1.15.6) (2024-05-30) + + +### Bug Fixes + +* **iOS:** iOS PrivacyInfo fix ([#5078](https://github.com/react-native-maps/react-native-maps/issues/5078)) ([9f458d9](https://github.com/react-native-maps/react-native-maps/commit/9f458d956ba2c16ee3f2a9e8849d9330dcefdda2)) + +## [1.15.5](https://github.com/react-native-maps/react-native-maps/compare/v1.15.4...v1.15.5) (2024-05-30) + + +### Bug Fixes + +* **android:** UIManagerModule fix for Bridgeless 0.74 ([#5061](https://github.com/react-native-maps/react-native-maps/issues/5061)) ([f194f99](https://github.com/react-native-maps/react-native-maps/commit/f194f9977ea7c6ab547b53e15b4d9a3480c8baa7)) + +## [1.15.4](https://github.com/react-native-maps/react-native-maps/compare/v1.15.3...v1.15.4) (2024-05-25) + + +### Bug Fixes + +* **ios:** resolve issue with Pods installation ([#5065](https://github.com/react-native-maps/react-native-maps/issues/5065)) ([9992c36](https://github.com/react-native-maps/react-native-maps/commit/9992c36054b0a8a056f0ce97d4687fcdf18c2ca5)) + +## [1.15.3](https://github.com/react-native-maps/react-native-maps/compare/v1.15.2...v1.15.3) (2024-05-24) + + +### Bug Fixes + +* **android:** move package namespace from Manifest to gradle ([d4916bb](https://github.com/react-native-maps/react-native-maps/commit/d4916bb6fff96f811d40c4d3b3bff8840442929d)) + +## [1.15.2](https://github.com/react-native-maps/react-native-maps/compare/v1.15.1...v1.15.2) (2024-05-20) + + +### Bug Fixes + +* use initialProps to set zoomTapEnabled in google-maps-ios ([#5059](https://github.com/react-native-maps/react-native-maps/issues/5059)) ([524194f](https://github.com/react-native-maps/react-native-maps/commit/524194ff62859cd82d78cbcca53c6f8e2da29bb7)) + +## [1.15.1](https://github.com/react-native-maps/react-native-maps/compare/v1.15.0...v1.15.1) (2024-05-05) + + +### Bug Fixes + +* **android:** fix non working tile update ([#5041](https://github.com/react-native-maps/react-native-maps/issues/5041)) ([29107d5](https://github.com/react-native-maps/react-native-maps/commit/29107d516ac6f6cb0ecbd604a5ebe43dfae339a2)) + +# [1.15.0](https://github.com/react-native-maps/react-native-maps/compare/v1.14.0...v1.15.0) (2024-05-04) + + +### Features + +* **ios:** Add possibility to use both MKMarkerAnnotationView and MKPinAnnotationView ([#5005](https://github.com/react-native-maps/react-native-maps/issues/5005)) ([6e4f49e](https://github.com/react-native-maps/react-native-maps/commit/6e4f49ead61557ca0eed6fd764317a848981e353)) + +# [1.14.0](https://github.com/react-native-maps/react-native-maps/compare/v1.13.2...v1.14.0) (2024-04-14) + + +### Bug Fixes + +* **android:** map initialization as expected ([b57d22f](https://github.com/react-native-maps/react-native-maps/commit/b57d22f887f352d7be636b3cc9b60be8254ab850)) +* **android:** map initialization as expected ([8ba7608](https://github.com/react-native-maps/react-native-maps/commit/8ba7608e90d188ee8bad50392968c60c5a2f15be)) +* **android:** map initialization as expected ([7e1dd58](https://github.com/react-native-maps/react-native-maps/commit/7e1dd58e3bf3ea174e0a526926b14ef38340a195)) +* **android:** map initialization as expected ([73640a6](https://github.com/react-native-maps/react-native-maps/commit/73640a6232f7e523c8962f6ab315bc9772ae154c)) +* **android:** map initialization as expected ([6554793](https://github.com/react-native-maps/react-native-maps/commit/6554793a0287060dd946edae0a92de3482c2b14d)) + + +### Features + +* add support for new React Native architecture ([630b72e](https://github.com/react-native-maps/react-native-maps/commit/630b72ef90c2bcb8611ce907c4f359eefe40c555)) +* add support for new React Native architecture ([48665da](https://github.com/react-native-maps/react-native-maps/commit/48665da2a581d300557d40fe740b8a28bb1cdc43)) +* add support for new React Native architecture ([794c64f](https://github.com/react-native-maps/react-native-maps/commit/794c64f9288310a8da0bb5450011a8ef6236b952)) +* add support for new React Native architecture ([1310985](https://github.com/react-native-maps/react-native-maps/commit/1310985e6e2c0d5b915d7364cf52a99cca43ec8f)) +* add support for new React Native architecture ([39fd4e6](https://github.com/react-native-maps/react-native-maps/commit/39fd4e6e57cad141e640e4baf4ed3f60eccd6db4)) +* Enable new arch for the example project ([1a21f86](https://github.com/react-native-maps/react-native-maps/commit/1a21f862b3b781707ae88e44db501f7453e95ac1)) + +## [1.13.2](https://github.com/react-native-maps/react-native-maps/compare/v1.13.1...v1.13.2) (2024-04-13) + + +### Bug Fixes + +* **android:** googleMapOptions / initialising multiple maps on android is buggy ([#5034](https://github.com/react-native-maps/react-native-maps/issues/5034)) ([be28937](https://github.com/react-native-maps/react-native-maps/commit/be2893701f4d92e6e7d1b3917626c74352f75303)) + +## [1.13.1](https://github.com/react-native-maps/react-native-maps/compare/v1.13.0...v1.13.1) (2024-04-13) + + +### Bug Fixes + +* **android:** onSelect was crashing android ([#5032](https://github.com/react-native-maps/react-native-maps/issues/5032)) ([b3ce3fc](https://github.com/react-native-maps/react-native-maps/commit/b3ce3fc4731b86107ea205d9ddb4743443c9523b)) + +# [1.13.0](https://github.com/react-native-maps/react-native-maps/compare/v1.12.0...v1.13.0) (2024-04-01) + + +### Features + +* **google-init:** Improve Google Maps Initialisation on Android ([a1be51b](https://github.com/react-native-maps/react-native-maps/commit/a1be51bd882933da93dc50fcc7ae2219a1df58b4)) + +# [1.12.0](https://github.com/react-native-maps/react-native-maps/compare/v1.11.3...v1.12.0) (2024-03-29) + + +### Features + +* **google-maps:** add onSelect/onDeselect support fo google maps ([#4990](https://github.com/react-native-maps/react-native-maps/issues/4990)) ([b9fbe31](https://github.com/react-native-maps/react-native-maps/commit/b9fbe31bb1feca4281a3131cc2a78dd64cca96d3)) + +## [1.11.3](https://github.com/react-native-maps/react-native-maps/compare/v1.11.2...v1.11.3) (2024-03-11) + + +### Bug Fixes + +* AnimatedRegion types ([#4909](https://github.com/react-native-maps/react-native-maps/issues/4909)) ([5fd9ca6](https://github.com/react-native-maps/react-native-maps/commit/5fd9ca6dfb655145cfbf41f7f977855d8265b1f1)) + +## [1.11.2](https://github.com/react-native-maps/react-native-maps/compare/v1.11.1...v1.11.2) (2024-03-11) + + +### Bug Fixes + +* **iOS:** removing polylines on iOS with googleProvider ([#4973](https://github.com/react-native-maps/react-native-maps/issues/4973)) ([6603060](https://github.com/react-native-maps/react-native-maps/commit/66030603a1808ef2c055f0abe2ab02f5e47cdf4c)) + +## [1.11.1](https://github.com/react-native-maps/react-native-maps/compare/v1.11.0...v1.11.1) (2024-03-10) + + +### Bug Fixes + +* **android:** custom maker performance improvements when view tracking ([#4969](https://github.com/react-native-maps/react-native-maps/issues/4969)) ([f30c9d7](https://github.com/react-native-maps/react-native-maps/commit/f30c9d7624aa2f50afb7da97b6749fc45eb3125c)) + +# [1.11.0](https://github.com/react-native-maps/react-native-maps/compare/v1.10.4...v1.11.0) (2024-03-10) + + +### Features + +* **android:** add bridgeless support ([#4985](https://github.com/react-native-maps/react-native-maps/issues/4985)) ([3ad0265](https://github.com/react-native-maps/react-native-maps/commit/3ad0265cd2e1fc357713ae2b8de71249c8425b50)) + +## [1.10.4](https://github.com/react-native-maps/react-native-maps/compare/v1.10.3...v1.10.4) (2024-03-10) + + +### Bug Fixes + +* **android:** remove dangling map marker views causing memory leak ([#4992](https://github.com/react-native-maps/react-native-maps/issues/4992)) ([02ed7c0](https://github.com/react-native-maps/react-native-maps/commit/02ed7c04ec51789462a234cbddf0483d2c8116cd)) + +## [1.10.3](https://github.com/react-native-maps/react-native-maps/compare/v1.10.2...v1.10.3) (2024-02-18) + + +### Bug Fixes + +* **animation:** Marker Animation using reanimated ([#4974](https://github.com/react-native-maps/react-native-maps/issues/4974)) ([7455ed0](https://github.com/react-native-maps/react-native-maps/commit/7455ed022117cbb45d232bc0f2a8ac5982bb8fd6)) + +## [1.10.2](https://github.com/react-native-maps/react-native-maps/compare/v1.10.1...v1.10.2) (2024-02-10) + + +### Bug Fixes + +* **AIRMap:** support iOS MapKit zoomConstraints for better zoom handling especially for 3d maps ([#4905](https://github.com/react-native-maps/react-native-maps/issues/4905)) ([d83e1a9](https://github.com/react-native-maps/react-native-maps/commit/d83e1a9f4e5e93e0826ec810a35688eb6b4b4026)) + +## [1.10.1](https://github.com/react-native-maps/react-native-maps/compare/v1.10.0...v1.10.1) (2024-02-04) + + +### Bug Fixes + +* EdgePadding types ([#4956](https://github.com/react-native-maps/react-native-maps/issues/4956)) ([de9e205](https://github.com/react-native-maps/react-native-maps/commit/de9e205c0955aee1951426bb9bf78440acac7119)) + +# [1.10.0](https://github.com/react-native-maps/react-native-maps/compare/v1.9.1...v1.10.0) (2024-01-21) + + +### Features + +* **map:** add numberOfTouches to onPanDrag event on iOS ([#4934](https://github.com/react-native-maps/react-native-maps/issues/4934)) ([13f3903](https://github.com/react-native-maps/react-native-maps/commit/13f39030e35472ec639733ba3c469ae51283d806)) + +## [1.9.1](https://github.com/react-native-maps/react-native-maps/compare/v1.9.0...v1.9.1) (2024-01-05) + + +### Bug Fixes + +* crash due to casting subview in iOS AIRGoogleMapMarker.m ([#4930](https://github.com/react-native-maps/react-native-maps/issues/4930)) ([4f38bd5](https://github.com/react-native-maps/react-native-maps/commit/4f38bd54e61bd93e26708c4d317ced3fa632cea9)) + +# [1.9.0](https://github.com/react-native-maps/react-native-maps/compare/v1.8.4...v1.9.0) (2024-01-02) + + +### Features + +* **googleMaps:** add support for the new Google's cloud based maps / styling via googleMapId prop ([77610e9](https://github.com/react-native-maps/react-native-maps/commit/77610e96360a7cba3df72e8082d0eb4cae310d38)) + +## [1.8.4](https://github.com/react-native-maps/react-native-maps/compare/v1.8.3...v1.8.4) (2023-12-15) + + +### Bug Fixes + +* **AIRMap:** fix location change timestamp ([7e5fb71](https://github.com/react-native-maps/react-native-maps/commit/7e5fb712f6f38973b666b406d904f51934f0fb55)) + +## [1.8.3](https://github.com/react-native-maps/react-native-maps/compare/v1.8.2...v1.8.3) (2023-12-07) + + +### Bug Fixes + +* add missing subThoroughfare to Address type ([435798b](https://github.com/react-native-maps/react-native-maps/commit/435798b58cb7907cb43caf75fec6286fe0840d28)) + +## [1.8.2](https://github.com/react-native-maps/react-native-maps/compare/v1.8.1...v1.8.2) (2023-12-07) + + +### Bug Fixes + +* **ios:** update google-maps-ios-utils version to 4.2.2 ([28f59c9](https://github.com/react-native-maps/react-native-maps/commit/28f59c9891ddd9f4f4774e1b4104f44d26f466f3)) + +## [1.8.1](https://github.com/react-native-maps/react-native-maps/compare/v1.8.0...v1.8.1) (2023-12-06) + + +### Bug Fixes + +* **example:** fix typo in AndroidManifest.xml for ACCESS_COARSE_LOCATION permission ([a4a0f0d](https://github.com/react-native-maps/react-native-maps/commit/a4a0f0d91b9098b18081aab493a70008b0ca1436)) + +# [1.8.0](https://github.com/react-native-maps/react-native-maps/compare/v1.7.1...v1.8.0) (2023-10-09) + + +### Features + +* **android:** Add android namespace to support react-native 0.73 ([#4859](https://github.com/react-native-maps/react-native-maps/issues/4859)) ([1c6c13d](https://github.com/react-native-maps/react-native-maps/commit/1c6c13d05705ed73c5ffdaf9f26648b44b7cb523)) + +## [1.7.1](https://github.com/react-native-maps/react-native-maps/compare/v1.7.0...v1.7.1) (2023-04-23) + + +### Bug Fixes + +* **android:** crash when removing feature belonging to collection ([#4707](https://github.com/react-native-maps/react-native-maps/issues/4707)) ([ae6fe90](https://github.com/react-native-maps/react-native-maps/commit/ae6fe90d3f0c727441dd2cdc84c1800e18f18d04)), closes [#4706](https://github.com/react-native-maps/react-native-maps/issues/4706) + +# [1.7.0](https://github.com/react-native-maps/react-native-maps/compare/v1.6.0...v1.7.0) (2023-04-23) + + +### Bug Fixes + +* **ios:** followsUserLocation changes zoom level ([#4696](https://github.com/react-native-maps/react-native-maps/issues/4696)) ([3b9491e](https://github.com/react-native-maps/react-native-maps/commit/3b9491e39529f11b32b4da9eb4ef36353c0033d9)), closes [#4585](https://github.com/react-native-maps/react-native-maps/issues/4585) + + +### Features + +* **android:** bump android-maps-utils to 3.4.0 ([#4699](https://github.com/react-native-maps/react-native-maps/issues/4699)) ([6b26c23](https://github.com/react-native-maps/react-native-maps/commit/6b26c235a26e2708497e5caf31176d8599441d9e)) + +# [1.7.0-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.6.1-beta.1...v1.7.0-beta.1) (2023-04-21) + + +### Features + +* **android:** bump android-maps-utils to 3.4.0 ([#4699](https://github.com/react-native-maps/react-native-maps/issues/4699)) ([6b26c23](https://github.com/react-native-maps/react-native-maps/commit/6b26c235a26e2708497e5caf31176d8599441d9e)) + +## [1.6.1-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.6.0...v1.6.1-beta.1) (2023-04-21) + + +### Bug Fixes + +* **ios:** followsUserLocation changes zoom level ([#4696](https://github.com/react-native-maps/react-native-maps/issues/4696)) ([3b9491e](https://github.com/react-native-maps/react-native-maps/commit/3b9491e39529f11b32b4da9eb4ef36353c0033d9)), closes [#4585](https://github.com/react-native-maps/react-native-maps/issues/4585) + +# [1.6.0](https://github.com/react-native-maps/react-native-maps/compare/v1.5.0...v1.6.0) (2023-04-20) + + +### Bug Fixes + +* **ios:** crash on Apple Maps when loading large polylines ([#4468](https://github.com/react-native-maps/react-native-maps/issues/4468)) ([e48e1af](https://github.com/react-native-maps/react-native-maps/commit/e48e1afc9e045fc97044a4c2e8623ad2173ef7b6)) +* **ios:** support for use_frameworks! :linkage => :static ([b0c2d42](https://github.com/react-native-maps/react-native-maps/commit/b0c2d42454ef72ff2037103218f799801aa83ebf)) +* **marker:** remove spamming warning from MapMarker ([#4644](https://github.com/react-native-maps/react-native-maps/issues/4644)) ([8825312](https://github.com/react-native-maps/react-native-maps/commit/8825312bd267b4ed94a21c8793839146e3dd5563)), closes [#4536](https://github.com/react-native-maps/react-native-maps/issues/4536) +* **types:** missing PolygonPressEvent type export ([#4410](https://github.com/react-native-maps/react-native-maps/issues/4410)) ([d3557a3](https://github.com/react-native-maps/react-native-maps/commit/d3557a39b9356c9d762c631bab6050f33967a510)) + + +### Features + +* enable npm provenance ([#4686](https://github.com/react-native-maps/react-native-maps/issues/4686)) ([3498c3f](https://github.com/react-native-maps/react-native-maps/commit/3498c3f8d9bf2184abdc01d00ff4a3ffb8ab28d3)) +* **ios:** bump googlemaps to 7.4.0 ([#4679](https://github.com/react-native-maps/react-native-maps/issues/4679)) ([661cddb](https://github.com/react-native-maps/react-native-maps/commit/661cddb4b21ebe6aecb90d7f4e49be14285f1ec7)) + +# [1.6.0-beta.3](https://github.com/react-native-maps/react-native-maps/compare/v1.6.0-beta.2...v1.6.0-beta.3) (2023-04-20) + + +### Features + +* enable npm provenance ([#4686](https://github.com/react-native-maps/react-native-maps/issues/4686)) ([3498c3f](https://github.com/react-native-maps/react-native-maps/commit/3498c3f8d9bf2184abdc01d00ff4a3ffb8ab28d3)) + +# [1.6.0-beta.3](https://github.com/react-native-maps/react-native-maps/compare/v1.6.0-beta.2...v1.6.0-beta.3) (2023-04-20) + + +### Features + +* enable npm provenance ([#4686](https://github.com/react-native-maps/react-native-maps/issues/4686)) ([3498c3f](https://github.com/react-native-maps/react-native-maps/commit/3498c3f8d9bf2184abdc01d00ff4a3ffb8ab28d3)) + +# [1.6.0-beta.2](https://github.com/react-native-maps/react-native-maps/compare/v1.6.0-beta.1...v1.6.0-beta.2) (2023-04-18) + + +### Bug Fixes + +* **marker:** remove spamming warning from MapMarker ([#4644](https://github.com/react-native-maps/react-native-maps/issues/4644)) ([8825312](https://github.com/react-native-maps/react-native-maps/commit/8825312bd267b4ed94a21c8793839146e3dd5563)), closes [#4536](https://github.com/react-native-maps/react-native-maps/issues/4536) + +# [1.6.0-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.5.1-beta.3...v1.6.0-beta.1) (2023-04-18) + + +### Features + +* **ios:** bump googlemaps to 7.4.0 ([#4679](https://github.com/react-native-maps/react-native-maps/issues/4679)) ([661cddb](https://github.com/react-native-maps/react-native-maps/commit/661cddb4b21ebe6aecb90d7f4e49be14285f1ec7)) + +## [1.5.1-beta.3](https://github.com/react-native-maps/react-native-maps/compare/v1.5.1-beta.2...v1.5.1-beta.3) (2023-04-18) + + +### Bug Fixes + +* **ios:** support for use_frameworks! :linkage => :static ([b0c2d42](https://github.com/react-native-maps/react-native-maps/commit/b0c2d42454ef72ff2037103218f799801aa83ebf)) + +## [1.5.1-beta.2](https://github.com/react-native-maps/react-native-maps/compare/v1.5.1-beta.1...v1.5.1-beta.2) (2023-04-16) + + +### Bug Fixes + +* **ios:** crash on Apple Maps when loading large polylines ([#4468](https://github.com/react-native-maps/react-native-maps/issues/4468)) ([e48e1af](https://github.com/react-native-maps/react-native-maps/commit/e48e1afc9e045fc97044a4c2e8623ad2173ef7b6)) + +## [1.5.1-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.5.0...v1.5.1-beta.1) (2023-04-16) + + +### Bug Fixes + +* **types:** missing PolygonPressEvent type export ([#4410](https://github.com/react-native-maps/react-native-maps/issues/4410)) ([d3557a3](https://github.com/react-native-maps/react-native-maps/commit/d3557a39b9356c9d762c631bab6050f33967a510)) + +# [1.5.0](https://github.com/react-native-maps/react-native-maps/compare/v1.4.0...v1.5.0) (2023-04-15) + + +### Features + +* **android:** pin androidx.work version ([73f21c7](https://github.com/react-native-maps/react-native-maps/commit/73f21c7ef447340fb8632ea597232d01ab9ae3d7)) + +# [1.5.0-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.4.0...v1.5.0-beta.1) (2023-04-15) + + +### Features + +* **android:** pin androidx.work version ([73f21c7](https://github.com/react-native-maps/react-native-maps/commit/73f21c7ef447340fb8632ea597232d01ab9ae3d7)) + +# [1.4.0](https://github.com/react-native-maps/react-native-maps/compare/v1.3.2...v1.4.0) (2023-01-25) + + +### Bug Fixes + +* **ios:** remove custom podspec xcconfig ([7f6ef12](https://github.com/react-native-maps/react-native-maps/commit/7f6ef121aeea4c4a0f662c017b0515f4ed92ac14)) + +### Features + +* **ios:** support apple silicon macs without rosetta ([da7ba81](https://github.com/react-native-maps/react-native-maps/commit/da7ba81c5d7bd202e10ab2e443a3ba4c2f0eba63)) + +# [1.4.0-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.3.2...v1.4.0-beta.1) (2022-10-10) + + +### Features + +* **android:** pin androidx.work version ([73f21c7](https://github.com/react-native-maps/react-native-maps/commit/73f21c7ef447340fb8632ea597232d01ab9ae3d7)) + +## [1.3.2](https://github.com/react-native-maps/react-native-maps/compare/v1.3.1...v1.3.2) (2022-09-26) + + +### Bug Fixes + +* **android:** initialCamera delay ([b4c20b0](https://github.com/react-native-maps/react-native-maps/commit/b4c20b0882730e7a98477fb1e279120a103dc455)), closes [#4442](https://github.com/react-native-maps/react-native-maps/issues/4442) +* **ios:** add header paths required for use_frameworks compilation ([e98be83](https://github.com/react-native-maps/react-native-maps/commit/e98be837ab256c6b7f593182e754579779aebd92)), closes [/github.com/react-native-maps/react-native-maps/discussions/4389#discussioncomment-3658882](https://github.com//github.com/react-native-maps/react-native-maps/discussions/4389/issues/discussioncomment-3658882) + +## [1.3.2-beta.2](https://github.com/react-native-maps/react-native-maps/compare/v1.3.2-beta.1...v1.3.2-beta.2) (2022-09-21) + + +### Bug Fixes + +* **android:** initialCamera delay ([b4c20b0](https://github.com/react-native-maps/react-native-maps/commit/b4c20b0882730e7a98477fb1e279120a103dc455)), closes [#4442](https://github.com/react-native-maps/react-native-maps/issues/4442) + +## [1.3.2-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.3.1...v1.3.2-beta.1) (2022-09-18) + + +### Bug Fixes + +* **ios:** add header paths required for use_frameworks compilation ([e98be83](https://github.com/react-native-maps/react-native-maps/commit/e98be837ab256c6b7f593182e754579779aebd92)), closes [/github.com/react-native-maps/react-native-maps/discussions/4389#discussioncomment-3658882](https://github.com//github.com/react-native-maps/react-native-maps/discussions/4389/issues/discussioncomment-3658882) + +## [1.3.1](https://github.com/react-native-maps/react-native-maps/compare/v1.3.0...v1.3.1) (2022-08-29) + + +### Bug Fixes + +* **MapView:** invalid parameter passed to nativemodule methods ([#4423](https://github.com/react-native-maps/react-native-maps/issues/4423)) ([e4c6b86](https://github.com/react-native-maps/react-native-maps/commit/e4c6b8691375fb120abcc39bd20f0bf6703f10f1)), closes [#4415](https://github.com/react-native-maps/react-native-maps/issues/4415) + +# [1.3.0](https://github.com/react-native-maps/react-native-maps/compare/v1.2.0...v1.3.0) (2022-08-22) + + +### Bug Fixes + +* **android:** onLayout not being called ([#4404](https://github.com/react-native-maps/react-native-maps/issues/4404)) ([0596061](https://github.com/react-native-maps/react-native-maps/commit/0596061fc0712b732f0530403d489429110cc9a8)), closes [#4402](https://github.com/react-native-maps/react-native-maps/issues/4402) +* **ios-google:** circle & polygon props not being applied ([ecc1a9a](https://github.com/react-native-maps/react-native-maps/commit/ecc1a9a33d9399eaf3c8a3bed7d68247c12fc8d9)), closes [#3057](https://github.com/react-native-maps/react-native-maps/issues/3057) +* make viewRef for native commands NonNullable ([d35000e](https://github.com/react-native-maps/react-native-maps/commit/d35000e92528082bd4f16499bdd8278a2587bb3c)) + + +### Features + +* **MapMarker:** migrate off dispatchViewManagerCommand ([7b01625](https://github.com/react-native-maps/react-native-maps/commit/7b0162553a7a57e902f38d2b473116e62e8789e5)) +* **MapView:** migrate off dispatchViewManagerCommand ([794727e](https://github.com/react-native-maps/react-native-maps/commit/794727e59b53cf1a647098139ef0093ad30a74f9)) +* migrate off findNodeHandle ([08ffc4a](https://github.com/react-native-maps/react-native-maps/commit/08ffc4ac398b38e0efe7c4d8da52e56f924f028d)) + +# [1.3.0-beta.3](https://github.com/react-native-maps/react-native-maps/compare/v1.3.0-beta.2...v1.3.0-beta.3) (2022-08-18) + + +### Bug Fixes + +* **android:** onLayout not being called ([#4404](https://github.com/react-native-maps/react-native-maps/issues/4404)) ([0596061](https://github.com/react-native-maps/react-native-maps/commit/0596061fc0712b732f0530403d489429110cc9a8)), closes [#4402](https://github.com/react-native-maps/react-native-maps/issues/4402) + +# [1.3.0-beta.2](https://github.com/react-native-maps/react-native-maps/compare/v1.3.0-beta.1...v1.3.0-beta.2) (2022-08-12) + + +### Bug Fixes + +* **ios-google:** circle & polygon props not being applied ([ecc1a9a](https://github.com/react-native-maps/react-native-maps/commit/ecc1a9a33d9399eaf3c8a3bed7d68247c12fc8d9)), closes [#3057](https://github.com/react-native-maps/react-native-maps/issues/3057) +* make viewRef for native commands NonNullable ([d35000e](https://github.com/react-native-maps/react-native-maps/commit/d35000e92528082bd4f16499bdd8278a2587bb3c)) + + +### Features + +* migrate off findNodeHandle ([08ffc4a](https://github.com/react-native-maps/react-native-maps/commit/08ffc4ac398b38e0efe7c4d8da52e56f924f028d)) + +# [1.3.0-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.2.0...v1.3.0-beta.1) (2022-08-11) + + +### Features + +* **MapMarker:** migrate off dispatchViewManagerCommand ([7b01625](https://github.com/react-native-maps/react-native-maps/commit/7b0162553a7a57e902f38d2b473116e62e8789e5)) +* **MapView:** migrate off dispatchViewManagerCommand ([794727e](https://github.com/react-native-maps/react-native-maps/commit/794727e59b53cf1a647098139ef0093ad30a74f9)) + +# [1.2.0](https://github.com/react-native-maps/react-native-maps/compare/v1.1.0...v1.2.0) (2022-08-05) + + +### Bug Fixes + +* **ci:** changelog.md generation ([c842a2b](https://github.com/react-native-maps/react-native-maps/commit/c842a2bb7cbbbc0ccee8322b9790c7d6829d253b)) + + +### Features + +* **android:** code cleanup ([#4352](https://github.com/react-native-maps/react-native-maps/issues/4352)) ([e56c787](https://github.com/react-native-maps/react-native-maps/commit/e56c7873c990113cb4a92436297a56be1c194961)) +* **geojson:** support for tracksViewChanges in Geojson component ([#4347](https://github.com/react-native-maps/react-native-maps/issues/4347)) ([d7046bb](https://github.com/react-native-maps/react-native-maps/commit/d7046bbc64afb66d12776fab79930c546f2d6674)) + +# [1.2.0-beta.2](https://github.com/react-native-maps/react-native-maps/compare/v1.2.0-beta.1...v1.2.0-beta.2) (2022-08-05) + + +### Features + +* **geojson:** support for tracksViewChanges in Geojson component ([#4347](https://github.com/react-native-maps/react-native-maps/issues/4347)) ([d7046bb](https://github.com/react-native-maps/react-native-maps/commit/d7046bbc64afb66d12776fab79930c546f2d6674)) + +# [1.2.0-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.1.1-beta.1...v1.2.0-beta.1) (2022-07-24) + + +### Features + +* **android:** code cleanup ([#4352](https://github.com/react-native-maps/react-native-maps/issues/4352)) ([e56c787](https://github.com/react-native-maps/react-native-maps/commit/e56c7873c990113cb4a92436297a56be1c194961)) + +## [1.1.1-beta.1](https://github.com/react-native-maps/react-native-maps/compare/v1.1.0...v1.1.1-beta.1) (2022-07-22) + + +### Bug Fixes + +* **ci:** changelog.md generation ([c842a2b](https://github.com/react-native-maps/react-native-maps/commit/c842a2bb7cbbbc0ccee8322b9790c7d6829d253b)) + +# [1.1.0](https://github.com/react-native-maps/react-native-maps/compare/v1.0.3...v1.1.0) (2022-07-20) + + +### Bug Fixes + +* **mapview:** re-add setNativeProps helper function ([c6e34ea](https://github.com/react-native-maps/react-native-maps/commit/c6e34ea3109784e1632981df6a00156904f9ce7c)) + + +### Features + +* **android:** bump play-services-base to 18.1.0 ([f90a3f6](https://github.com/react-native-maps/react-native-maps/commit/f90a3f66680988de0cadd04c71d062627fa66ce2)) +* **android:** bump play-services-location to 20.0.0 ([440f5b3](https://github.com/react-native-maps/react-native-maps/commit/440f5b332020a7e8909cb1658bc786b377b42dc4)) +* **ios-google:** enable metal renderer ([#4336](https://github.com/react-native-maps/react-native-maps/issues/4336)) ([d8770fa](https://github.com/react-native-maps/react-native-maps/commit/d8770faa36ddb8c8bb3057f95d3bf220c889ba47)), closes [#4130](https://github.com/react-native-maps/react-native-maps/issues/4130) +* **mapview:** remove componentDidUpdate logic ([625e765](https://github.com/react-native-maps/react-native-maps/commit/625e765c132ef551588dcd25249c118702def751)) +* **mapview:** remove use of setNativeProps for customMapStyleString ([7d9ebf8](https://github.com/react-native-maps/react-native-maps/commit/7d9ebf8b3248518da6a2bf11b3d474cebc51abb6)) +* **mapview:** stop intercepting onLayout ([0554cdc](https://github.com/react-native-maps/react-native-maps/commit/0554cdcc666f8ace40b5e94de17598c2f1b5d528)) +* **mapview:** stop setting region and initialregion in onMapReady js side ([13817ca](https://github.com/react-native-maps/react-native-maps/commit/13817cad34062fe8bf997855367a140daaca87a9)) + +## [1.0.3](https://github.com/react-native-maps/react-native-maps/compare/v1.0.2...v1.0.3) (2022-07-19) + + +### Bug Fixes + +* **android:** animateToRegion not working with duration 0 ([#4339](https://github.com/react-native-maps/react-native-maps/issues/4339)) ([a6d9ce2](https://github.com/react-native-maps/react-native-maps/commit/a6d9ce2ec79f7ab256bfedb0f5f092f389b725be)), closes [#4338](https://github.com/react-native-maps/react-native-maps/issues/4338) + +## [1.0.2](https://github.com/react-native-maps/react-native-maps/compare/v1.0.1...v1.0.2) (2022-07-15) + + +### Bug Fixes + +* **types:** missing marker-press action for MapPressEvent ([#4330](https://github.com/react-native-maps/react-native-maps/issues/4330)) ([7461e48](https://github.com/react-native-maps/react-native-maps/commit/7461e48409d6667a38f219bafc7b77a281cca35c)), closes [#4320](https://github.com/react-native-maps/react-native-maps/issues/4320) + +## [1.0.1](https://github.com/react-native-maps/react-native-maps/compare/v1.0.0...v1.0.1) (2022-07-15) + + +### Bug Fixes + +* **ios-google:** crash when setting initialCamera prop ([2b80944](https://github.com/react-native-maps/react-native-maps/commit/2b809446286a0b922bb7f5643c69792eaf6cc90c)), closes [#4321](https://github.com/react-native-maps/react-native-maps/issues/4321) + +# [1.0.0](https://github.com/react-native-maps/react-native-maps/compare/v0.31.1...v1.0.0) (2022-07-07) + + +### Bug Fixes + +* **android:** longLogTag debug error ([fa452ec](https://github.com/react-native-maps/react-native-maps/commit/fa452eca5559f79c73afec0afe387ca2f23cfd10)), closes [#4233](https://github.com/react-native-maps/react-native-maps/issues/4233) +* **android:** wrong coordinates on press polyline ([1868b42](https://github.com/react-native-maps/react-native-maps/commit/1868b42e5003d99314b7570c0a2cdbc94b73b318)) +* **ios:** camera being set repeatedly in didPrepareMap ([46b52d4](https://github.com/react-native-maps/react-native-maps/commit/46b52d40b70e0ac146c92b24932e5e76fb839ef6)), closes [/github.com/react-native-maps/react-native-maps/pull/4241#issuecomment-1142160942](https://github.com//github.com/react-native-maps/react-native-maps/pull/4241/issues/issuecomment-1142160942) +* **ios:** icon flash glitch from [#3657](https://github.com/react-native-maps/react-native-maps/issues/3657) and [#3418](https://github.com/react-native-maps/react-native-maps/issues/3418) ([#4229](https://github.com/react-native-maps/react-native-maps/issues/4229)) ([b734a5a](https://github.com/react-native-maps/react-native-maps/commit/b734a5a28ece0b21eef9b5ef7df8d9d25ab5cdde)) +* **ios:** initialRegion on Google Maps ([d6e0ce7](https://github.com/react-native-maps/react-native-maps/commit/d6e0ce7e2087022fe7d0922a008a02100fc65eab)), closes [#4244](https://github.com/react-native-maps/react-native-maps/issues/4244) +* **ios:** onMapReady not always called with google provider ([e035c8d](https://github.com/react-native-maps/react-native-maps/commit/e035c8dc16361412f9b42674e96bca152a158d99)), closes [#4066](https://github.com/react-native-maps/react-native-maps/issues/4066) +* **ios:** wrong coordinates on pressing polyline ([#4304](https://github.com/react-native-maps/react-native-maps/issues/4304)) ([27cc80a](https://github.com/react-native-maps/react-native-maps/commit/27cc80a046d84a88636b9e6af1c6ff40dd9c2eb0)) +* **mapkit:** onRegionChangeComplete called before region change completed ([7ea9658](https://github.com/react-native-maps/react-native-maps/commit/7ea9658c87ac0382157df47873444d803bd51dfe)), closes [#4265](https://github.com/react-native-maps/react-native-maps/issues/4265) +* missing context provider ([b43c62e](https://github.com/react-native-maps/react-native-maps/commit/b43c62e4eb45e4368b635046bc9d05a3414da892)) +* **types:** export types that were present before v1 ([#4267](https://github.com/react-native-maps/react-native-maps/issues/4267)) ([fd798c9](https://github.com/react-native-maps/react-native-maps/commit/fd798c9826a9edfa936716e8ed78d40c07ccb9c7)) +* **types:** showsBuilding prop on MapView ([#4283](https://github.com/react-native-maps/react-native-maps/issues/4283)) ([593768f](https://github.com/react-native-maps/react-native-maps/commit/593768fae7a5ae2d8dcb863cc879078d8a24e4f7)) + + +### Features + +* **android:** remove internet & location permissions from AndroidManifest ([849b1bc](https://github.com/react-native-maps/react-native-maps/commit/849b1bc4859cf3667ae374fc597a4b9ebf67e865)) +* **android:** support files from AssetManager in LocalTile ([f885d4b](https://github.com/react-native-maps/react-native-maps/commit/f885d4b9b6ab09656cef3df008afe79665cd4f72)), closes [#4153](https://github.com/react-native-maps/react-native-maps/issues/4153) +* **ios-google:** bump maps sdk to 7.0.0 ([32eb294](https://github.com/react-native-maps/react-native-maps/commit/32eb294ad551955efcbb7b9add380d3234930052)) +* **ios-google:** move initial region logic to layoutSubviews ([d8f20cd](https://github.com/react-native-maps/react-native-maps/commit/d8f20cdac7861ef3dc276c050e45a43bcb8df534)) +* **ios-google:** stop setting the camera if layoutSubviews wasn't triggered ([5b6b3bc](https://github.com/react-native-maps/react-native-maps/commit/5b6b3bc651eeff2fb4858273789337bbeea5b8c0)) +* **ios:** bump google maps sdk to 6.2.1 ([b1df2e2](https://github.com/react-native-maps/react-native-maps/commit/b1df2e2339e97c07011e72dad5566897d180d08e)) +* **ios:** remove deprecated snapshot method ([c0bb8f3](https://github.com/react-native-maps/react-native-maps/commit/c0bb8f37cea1aa515beb54a2a0070360d55d1c1c)) +* **ios:** stop requesting location permissions in showsUserLocation ([1d0e381](https://github.com/react-native-maps/react-native-maps/commit/1d0e381e4a2803f8e41e7d73516fff57a1b14cd5)), closes [#4166](https://github.com/react-native-maps/react-native-maps/issues/4166) +* migrate to typescript ([adc7670](https://github.com/react-native-maps/react-native-maps/commit/adc76701cf3a0c076680441aa1cac7827a4d9f42)) +* remove deprecated mapview methods ([d053fd2](https://github.com/react-native-maps/react-native-maps/commit/d053fd2e7736e3e2ea9baf7376a947c3b0b92a58)) +* require react-native >= 0.64.3 ([8b852c1](https://github.com/react-native-maps/react-native-maps/commit/8b852c17b923e00970be8f462e76802eec8a1a3f)) +* use package.json as source of truth for podspecs ([d48fab5](https://github.com/react-native-maps/react-native-maps/commit/d48fab5c2527722e6fd19206cd49f4e9470dd0c3)) + + +### BREAKING CHANGES + +* **ios:** polyline onPress returns tapLocation instead of the first point in the polyline +* **ios-google:** Using Google Maps on iOS now requires iOS >= 13.0 and XCode >= 13 +* **android:** polyline onPress returns tapLocation instead of the first point in the polyline +* **ios:** Requesting location permissions manually is now needed for showsUserLocation. +* **android:** internet & location permissions removed from AndroidManifest +* **ios:** legacy iOS snapshot method removed +* removed animateTo{Navigation,Coordinate,Bearing,ViewingAngle}. +* types are now being emitted automatically and some might have changed. +* react-native-maps now requires react-native >= 0.64.3. +* Components are no longer exported as members of MapView. Import the components you need, e.g. `import MapView, {Marker} from 'react-native-maps';` instead. + +## [0.30.2](https://github.com/react-native-maps/react-native-maps/compare/v0.30.1...v0.30.2) (2022-05-17) + + +### Bug Fixes + +* **android:** android 12 crash ([7cffb97](https://github.com/react-native-maps/react-native-maps/commit/7cffb9737244edf28b6682520cc28e1ef98160e3)) +* migrate to deprecated-react-native-prop-types ([4bfb8a1](https://github.com/react-native-maps/react-native-maps/commit/4bfb8a1f561d77dabf0e81ef4ff6a208fe3f5ae5)) + +## [0.31.1](https://github.com/react-native-maps/react-native-maps/compare/v0.31.0...v0.31.1) (2022-04-30) + + +### Bug Fixes + +* **ios:** pod install failing with deployment target 11.0 ([f7c8903](https://github.com/react-native-maps/react-native-maps/commit/f7c8903501f4adbf982462d605c5c8d548e777ff)) + +# [0.31.0](https://github.com/react-native-maps/react-native-maps/compare/v0.30.0...v0.31.0) (2022-04-24) + + +### Bug Fixes + +* **android:** add null checks for map in MapView ([585d89e](https://github.com/react-native-maps/react-native-maps/commit/585d89e84ba52f2e463300451693b53c3758e649)), closes [#4074](https://github.com/react-native-maps/react-native-maps/issues/4074) +* **android:** android 12 crash ([7ccc65d](https://github.com/react-native-maps/react-native-maps/commit/7ccc65d83b409735034912279d72a7c6dc232066)) +* **android:** initialRegion delay ([609571f](https://github.com/react-native-maps/react-native-maps/commit/609571fa2dedd7670bd85596a60e26f9c0f27293)), closes [#4060](https://github.com/react-native-maps/react-native-maps/issues/4060) +* **android:** opacity is ignored on first render of overlay ([#4141](https://github.com/react-native-maps/react-native-maps/issues/4141)) ([9a7621c](https://github.com/react-native-maps/react-native-maps/commit/9a7621c2ba957e574eb6f96b266a40dd76f7e262)) +* **android:** remove use of lambda expression ([4592499](https://github.com/react-native-maps/react-native-maps/commit/4592499c153804de5dca5a17c2015338baf2767b)), closes [#4160](https://github.com/react-native-maps/react-native-maps/issues/4160) +* remove upper react version limit ([70e34a1](https://github.com/react-native-maps/react-native-maps/commit/70e34a13a9e7bb51f21a67df8f123ad930281212)), closes [#4167](https://github.com/react-native-maps/react-native-maps/issues/4167) + + +### Features + +* **android:** add support for new google maps renderer ([#4055](https://github.com/react-native-maps/react-native-maps/issues/4055)) ([bb08557](https://github.com/react-native-maps/react-native-maps/commit/bb0855714895e5264f4f6b3ebc9f3cdc6350b39a)) +* **ios:** bump google maps pods ([73e6ef6](https://github.com/react-native-maps/react-native-maps/commit/73e6ef6cd3b1e448f1395cdec80cd19f354ab8f6)), closes [#4157](https://github.com/react-native-maps/react-native-maps/issues/4157) +* migrate to deprecated-react-native-prop-types ([5bc1cc6](https://github.com/react-native-maps/react-native-maps/commit/5bc1cc67ab1a110e53bde238ccbb40d7dfef547f)) + +## 0.30.1 - (January 3, 2022) +* Common: [#4011](https://github.com/react-native-maps/react-native-maps/pull/4011) Feature fix AnimatedRegion getAnimatedValue Issue + +## 0.30.0 - (November 10, 2021) +* Common: [#3789](https://github.com/react-native-maps/react-native-maps/pull/3789) URLTile tile caching +* Common: [#3938](https://github.com/react-native-maps/react-native-maps/pull/3938) Add URLTile & WMSTile opacity property +* Common: [#3996](https://github.com/react-native-maps/react-native-maps/pull/3996) Allow wider version range for react-native-web peer dependency +* Common: [#3977](https://github.com/react-native-maps/react-native-maps/pull/3977) Upgrade example app to React Native 0.65.1 +* Common: [#3972](https://github.com/react-native-maps/react-native-maps/pull/3972) Fix default color and stroke width properties for Geojson component +* Common: [#3965](https://github.com/react-native-maps/react-native-maps/pull/3965) GeoJSON Marker Title Prop & A bit Refactor +* Common: [#3744](https://github.com/react-native-maps/react-native-maps/pull/3744) Updates AnimatedRegion component +* Common: [#3966](https://github.com/react-native-maps/react-native-maps/pull/3966) Fix polygon on press wrong coordinates +* iOS: [#3997](https://github.com/react-native-maps/react-native-maps/pull/3997) Allow markers to be un-tappable on ios gmaps +* iOS: [#3944](https://github.com/react-native-maps/react-native-maps/pull/3944) Add heading to onUserLocationChange +* iOS: [#3956](https://github.com/react-native-maps/react-native-maps/pull/3956) Add missing export for AIRMapMarker isPreselected view property +* iOS: [#3973](https://github.com/react-native-maps/react-native-maps/pull/3973) Version bump googleMaps and Google-Maps-iOS-Utils +* Android: [#3945](https://github.com/react-native-maps/react-native-maps/pull/3945) Bump minSDKVersion to 21 + +## 0.29.4 - (November 10, 2021) +* Common: [#3996](https://github.com/react-native-maps/react-native-maps/pull/3996) Allow wider version range for react-native-web peer dependency + +## 0.29.3 - (August 29, 2021) + +* Common: [#3920](https://github.com/react-native-maps/react-native-maps/pull/3920) GeoJSON onPress Crash Fix and Marker Customization Options +* Common: [#3930](https://github.com/react-native-maps/react-native-maps/pull/3930) Updated peer deps to maintain compatibility with expo init + +## 0.29.2 - (July 5, 2021) + +* Android: **Revert** [#3797](https://github.com/react-native-maps/react-native-maps/pull/3797) Allow a previously dashed line to be made solid + +## 0.29.1 - (July 5, 2021) + +* Common: [#3855](https://github.com/react-native-maps/react-native-maps/pull/3855) Fix crashes related to getAddressFromCoordinates for android and iOS +* Common: [#3871](https://github.com/react-native-maps/react-native-maps/pull/3871) Allow React 17 peer dependency + +## 0.29.0 - (June 8, 2021) + +* Common: [#3779](https://github.com/react-native-maps/react-native-maps/pull/3779) Polyline tappable feature on GeoJson +* Common: [#3784](https://github.com/react-native-maps/react-native-maps/pull/3784) Implemented scrollDuringRotateOrZoom control for Google Maps +* Common: [#3739](https://github.com/react-native-maps/react-native-maps/pull/3739) Use Default Geocoder to convert Coordinates by getting address +* Common: [#3728](https://github.com/react-native-maps/react-native-maps/pull/3728) Add `onPress` for GeoJSON +* Common: [#3737](https://github.com/react-native-maps/react-native-maps/pull/3737) Added support for overlay rotations in Google Maps API +* iOS: [#3754](https://github.com/react-native-maps/react-native-maps/pull/3754) Fix indoor events on iOS for multiple MapViews +* iOS: [#3762](https://github.com/react-native-maps/react-native-maps/pull/3762) Add support for disabling userLocation callout +* iOS: [#3746](https://github.com/react-native-maps/react-native-maps/pull/3746) Fix in WMSTile: typings & component removal on iOS +* iOS: [#3764](https://github.com/react-native-maps/react-native-maps/pull/3764) Provide a way to override mapkit style +* Android: [#3726](https://github.com/react-native-maps/react-native-maps/pull/3726) Use double on `setCamera` to fix zoom precision. +* Android: [#3308](https://github.com/react-native-maps/react-native-maps/pull/3308) Fix fitToCoordinates with preset mapPadding +* Android: [#3797](https://github.com/react-native-maps/react-native-maps/pull/3797) Allow a previously dashed line to be made solid +* Docs: [#3768](https://github.com/react-native-maps/react-native-maps/pull/3768) Clean up old installation instructions +* Docs: [#3771](https://github.com/react-native-maps/react-native-maps/pull/3771) Better docs for custom markers +* Docs: [#3716](https://github.com/react-native-maps/react-native-maps/pull/3716) Update Mapview docs +* Docs: [#3793](https://github.com/react-native-maps/react-native-maps/pull/3793) Update Polygon docs + +## 0.28.1 - (November 10, 2021) +* Common: [#3996](https://github.com/react-native-maps/react-native-maps/pull/3996) Allow wider version range for react-native-web peer dependency + + +## 0.28.0 (April 20, 2021) + +* Common: [#3705](https://github.com/react-native-maps/react-native-maps/pull/3705) Update example project +* Common: [#3424](https://github.com/react-native-maps/react-native-maps/pull/3424) Bugfix for the "require cycles" warning +* Common: [#3452](https://github.com/react-native-maps/react-native-maps/pull/3452) Ability to pass a Marker image as Geojson prop +* Common: [#3516](https://github.com/react-native-maps/react-native-maps/pull/3516) Polyline props to obtain dashed/dotted lines in Geojson component +* Common: [#3358](https://github.com/react-native-maps/react-native-maps/pull/3358) onRegionChange sends a boolean indicating if the move was from the user (true) or an animation (false) +* Common: [#3658](https://github.com/react-native-maps/react-native-maps/pull/#3658) Geojson stroke color, stroke width, fill color, and marker color properties preserved +* Common: [#3695](https://github.com/react-native-maps/react-native-maps/pull/3695) AnimateCamera duration bugfixed +* iOS: [#3383](https://github.com/react-native-maps/react-native-maps/pull/3383) Added tintColor prop for setting the color of the map +* Android: [#3414](https://github.com/react-native-maps/react-native-maps/pull/3414) Play services version updated + +## 0.27.1 (March 19, 2020) +* Android: [#3345](https://github.com/react-native-maps/react-native-maps/pull/3345) Protect against null Location in FusedLocationSource +* Android: [#3351](https://github.com/react-native-maps/react-native-maps/pull/3351) Revert "android google map callout support + +## 0.27.0 (March 4, 2020) + +* iOS: [#3115](https://github.com/react-native-maps/react-native-maps/pull/3115) Expose isAccessibilityElement as a prop +* iOS: [#3119](https://github.com/react-native-maps/react-native-maps/pull/3119) Add `mapPadding` prop +* iOS: [#3134](https://github.com/react-native-maps/react-native-maps/pull/3134) Add `isPreselected` prop +* iOS: [#3120](https://github.com/react-native-maps/react-native-maps/pull/3120) Keep current mapType when taking snapshot +* iOS: [#2889](https://github.com/react-native-maps/react-native-maps/pull/2889) Implement onUserLocationChange +* iOS: [#3136](https://github.com/react-native-maps/react-native-maps/pull/3136) Use RCTImageLoaderProtocol for RN v0.61.0 +* iOS: [#3125](https://github.com/react-native-maps/react-native-maps/pull/3125) Replace deprecated bridge.imageLoader calls with moduleForClass API +* iOS: [#3309](https://github.com/react-native-maps/react-native-maps/pull/3309) Fixes for polygon custom styles not being applied when provider is Google maps +* iOS: [#3104](https://github.com/react-native-maps/react-native-maps/pull/3104) Only handle cacheEnable when app is active +* Android: [#2993](https://github.com/react-native-maps/react-native-maps/pull/2993) Improved user location +* Common: [#3163](https://github.com/react-native-maps/react-native-maps/pull/3163) Opacity prop for MapOverlay on Android and iOS (Google Maps) +* Common: [#3131](https://github.com/react-native-maps/react-native-maps/pull/3131) AnimatedRegion.addListener() property setting + +## 0.26.1 (October 3, 2019) + +* Android: [#3103](https://github.com/react-native-maps/react-native-maps/pull/3103) Support for RN 0.60.5 +* Android: [#3103](https://github.com/react-native-maps/react-native-maps/pull/3103) "AIRMap" was not found in the UIManager + +## 0.26.0 (September 30, 2019) + +* iOS: [#2999](https://github.com/react-native-maps/react-native-maps/pull/2999) Update GoogleMaps pod to 3.2.0 +* iOS: [#2395](https://github.com/react-native-maps/react-native-maps/pull/2395) Allow using onPanDrag while scrollEnabled=true +* iOS: [#3051](https://github.com/react-native-maps/react-native-maps/pull/3051) Add flat property binding on Marker for iOS +* Android: [#3007](https://github.com/react-native-maps/react-native-maps/pull/3007) Add Overlay onPress event +* Android: [#3001](https://github.com/react-native-maps/react-native-maps/pull/3001) Add @ReactModule annotation to help turbo modules processor +* Common: [#3045](https://github.com/react-native-maps/react-native-maps/pull/3045) Add heading to the onUserLocationChange listener +* Common: [#2937](https://github.com/react-native-maps/react-native-maps/pull/2937) Add onDoublePress callback +* Common: [#2960](https://github.com/react-native-maps/react-native-maps/pull/2959) Heatmaps for Android and iOS +* Common: [#2959](https://github.com/react-native-maps/react-native-maps/pull/2959) Added GeoJSON support by default +* Common: [#2975](https://github.com/react-native-maps/react-native-maps/pull/2975) Convert to new react native config format (RN 0.60) +* Common: [#2973](https://github.com/react-native-maps/react-native-maps/pull/2973) Fix select annotation when show/hide callout view + +## 0.25.0 (July 11, 2019) +* Android: [#2941](https://github.com/react-native-maps/react-native-maps/pull/2941) Fix build gradle to allow jettifier to run correctly +* Android: [#2741](https://github.com/react-native-maps/react-native-maps/pull/2741) Use a shared image icon for markers when they share the same image URI +* Android: [#2557](https://github.com/react-native-maps/react-native-maps/pull/2557) Fix bug when changing subview of Marker to icon +* Android: [#2392](https://github.com/react-native-maps/react-native-maps/pull/2392) Add support for loading base64 encoded image +* iOS: [#2423](https://github.com/react-native-maps/react-native-maps/pull/2423) Handle annotations without images on iOS map snapshot +* iOS: [#2881](https://github.com/react-native-maps/react-native-maps/pull/2881) Update podspec to use GoogleMaps 3.1.0 +* iOS: [#2253](https://github.com/react-native-maps/react-native-maps/pull/2253) TestID's for e2e automation +* iOS: [#2826](https://github.com/react-native-maps/react-native-maps/pull/2826) Follow up for [#2253](https://github.com/react-native-maps/react-native-maps/pull/2253) +* iOS: [#2397](https://github.com/react-native-maps/react-native-maps/pull/2397) Add compass location offsets for iOS maps +* Common: [#2568](https://github.com/react-native-maps/react-native-maps/pull/2568) Support for WMS Layer support +* Common: [#2017](https://github.com/react-native-maps/react-native-maps/pull/2017) Update the Google Maps custom map style if changed after initialization +* Common: [#2903](https://github.com/react-native-maps/react-native-maps/pull/2903) Fix react snapshot undefined warning + +## 0.24.2 (April 17, 2019) +* iOS: [#2818](https://github.com/react-native-maps/react-native-maps/pull/2818) Google Maps - Weak refs to gesture targets to address memory leak + +## 0.24.1 (April 16, 2019) +* iOS: [#2815](https://github.com/react-native-maps/react-native-maps/pull/2815) Google Maps - Free instance variable in getActionForTarget to address memory leak + +## 0.24.0 (April 11, 2019) +* Common: [#2740](https://github.com/react-native-maps/react-native-maps/pull/2740) Fix deprecated UIManager usage when accessing component names +* Common: [#2393](https://github.com/react-native-maps/react-native-maps/pull/2393) add typings for pointForCoordinate & coordinateForPoint +* Common: [#2732](https://github.com/react-native-maps/react-native-maps/pull/2732) Implement ability to flip y coordinate for Google Map tiles. +* Android: [#2765](https://github.com/react-native-maps/react-native-maps/pull/2765) Allow setting of play-services version through ext +* Android: [#2702](https://github.com/react-native-maps/react-native-maps/pull/2702) Enable RN projects to define the Android AppCompat Library version +* Android: [#2720](https://github.com/react-native-maps/react-native-maps/pull/2720) Fix Android dependencies and build errors +* Android: [#2682](https://github.com/react-native-maps/react-native-maps/pull/2682) Implement 'tappable' prop on polyline for Android +* Android: [#2417](https://github.com/react-native-maps/react-native-maps/pull/2417) Support for lineCap and lineDash pattern +* Android: [#2727](https://github.com/react-native-maps/react-native-maps/pull/2727) fix build: only apply mvn push gradle plugin if POM_ARTIFACT_ID is set +* iOS: [#2446](https://github.com/react-native-maps/react-native-maps/pull/2446) fix iOS GoogleMaps camera always animate +* iOS: [#2746](https://github.com/react-native-maps/react-native-maps/pull/2746) onPanDrag support for iOS +* iOS: [#2581](https://github.com/react-native-maps/react-native-maps/pull/2581) Custom callout improvements 🎉 +* iOS: [#2794](https://github.com/react-native-maps/react-native-maps/pull/2794) Fix CalloutSubview on Apple maps +* iOS: [#2716](https://github.com/react-native-maps/react-native-maps/pull/2716) Fix Memory Leaks +* Docs: [#2675](https://github.com/react-native-maps/react-native-maps/pull/2675) [#2685](https://github.com/react-native-maps/react-native-maps/pull/2685) [#2707](https://github.com/react-native-maps/react-native-maps/pull/2707) [#2704](https://github.com/react-native-maps/react-native-maps/pull/2704) +* Example: [#2792](https://github.com/react-native-maps/react-native-maps/pull/2792) Upgrade Example to react-native to 0.59.3 +* TypeScript: [#2705](https://github.com/react-native-maps/react-native-maps/pull/2705) Add Marker icon property introduced in [#2650](https://github.com/react-native-maps/react-native-maps/pull/2650) to index.d.ts + +## 0.23.0 (January 17, 2019) +* Common: [#2651](https://github.com/react-native-maps/react-native-maps/pull/2651) Use `resolveAssetSource` method from Image +* Common: [#2576](https://github.com/react-native-maps/react-native-maps/pull/2576) Fix import error for `MapMarker` and `MapOverlay` +* Common: [#2615](https://github.com/react-native-maps/react-native-maps/pull/2615) Added helper method for calculating bounding box from region +* Common: [#2607](https://github.com/react-native-maps/react-native-maps/pull/2607) Fix camera type definition error +* Common: [#2563](https://github.com/react-native-maps/react-native-maps/pull/2563) Added camera system and deprecate `animateTo` methods +* Common: [#2571](https://github.com/react-native-maps/react-native-maps/pull/2571) Added `getMapBoundaries` to `MapView` +* Common/iOS: [#2650](https://github.com/react-native-maps/react-native-maps/pull/2650) Added `icon` prop for `MapMarker` +* iOS: [#2414](https://github.com/react-native-maps/react-native-maps/pull/2414) Fix path for yoga in Podfile +* iOS: [#2627](https://github.com/react-native-maps/react-native-maps/pull/2627) Added `tileSize` prop for `MapUrlTile` +* iOS: [#2608](https://github.com/react-native-maps/react-native-maps/pull/2608) Fix `animateToCamera` +* Android: [#2653](https://github.com/react-native-maps/react-native-maps/pull/2653) Defaults to the map services version instead of play services +* Android: [#2587](https://github.com/react-native-maps/react-native-maps/pull/2587) Allow specifying a different version for base and maps on android +* Android: [#2598](https://github.com/react-native-maps/react-native-maps/pull/2598) Fix crash for cannot getActiveLevelIndex +* Docs: [#2639](https://github.com/react-native-maps/react-native-maps/pull/2639) Added note about recursive framework search paths +* Docs: [#2631](https://github.com/react-native-maps/react-native-maps/pull/2631) Added notes for Google Play Services + +## 0.22.1 (November 8, 2018) +* Common: [#2548](https://github.com/react-community/react-native-maps/pull/2548) Moved `babel-plugin-module-resolver` and `babel-preset-react-native` from dependencies to devDependencies +* Android: [#2555](https://github.com/react-community/react-native-maps/pull/2555) Fixed [#2507](https://github.com/react-community/react-native-maps/issues/2507) +* Android: [#2545](https://github.com/react-community/react-native-maps/pull/2545) Fixed “The specified child already has a parent” +* Docs: [#2541](https://github.com/react-community/react-native-maps/pull/2541) Improve installation docs +* Docs: [#2550](https://github.com/react-community/react-native-maps/pull/2550) Specify how to use Google Maps +* Docs: [#2559](https://github.com/react-community/react-native-maps/pull/2559) Clarify cacheEnabled is apple maps only + +## 0.22.0 (October 11, 2018) +* Common: [#2049](https://github.com/react-community/react-native-maps/pull/2049) Added `animateToNavigation` method to `MapView` +* Common: [#2207](https://github.com/react-community/react-native-maps/pull/2207), [#2232](https://github.com/react-community/react-native-maps/pull/2232) Added `timestamp` property to `onUserLocationChange` event callback +* Common: [#2479](https://github.com/react-community/react-native-maps/pull/2479), [#2524](https://github.com/react-community/react-native-maps/pull/2524) Added `edgePadding` to `fitToSuppliedMarkers` function +* Common: [#2448](https://github.com/react-community/react-native-maps/pull/2448) Added custom indoor picker level +* Common: [#2238](https://github.com/react-community/react-native-maps/pull/2238) Support the `asset://` scheme for images +* Common: [#2136](https://github.com/react-community/react-native-maps/pull/2136), [#2184](https://github.com/react-community/react-native-maps/pull/2184) Modifications/Enhancements to MapView.UrlTile +* Common: [#2039](https://github.com/react-community/react-native-maps/pull/2039) Fix for `pointForCoordinate` and `coordinateForPoint` +* Common: [#2217](https://github.com/react-community/react-native-maps/pull/2217) Using `ColorPropType` to validate all color props more accurately +* iOS: [#2396](https://github.com/react-community/react-native-maps/pull/2396) Added installation for iOS via `react-native link` +* iOS: [#2243](https://github.com/react-community/react-native-maps/pull/2243) Added support of `lineDashPattern` polyline props to iOS Google Maps +* iOS: [#2149](https://github.com/react-community/react-native-maps/pull/2149) Added `paddingAdjustmentBehavior` for Google Maps on iOS +* iOS: [#2231](https://github.com/react-community/react-native-maps/pull/2231) Prefix DummyView class +* iOS: [#2229](https://github.com/react-community/react-native-maps/pull/2229) Use global imports for new Pods dependencies in AIRGoogleMap +* iOS: [#2248](https://github.com/react-community/react-native-maps/pull/2248) Make tiles display at the same physical size regardless of pixel density on iOS devices +* iOS: [#2306](https://github.com/react-community/react-native-maps/pull/2306) Prefix or eliminate globals in AIRMapMarker +* iOS: [#2351](https://github.com/react-community/react-native-maps/pull/2351) Added support for `calloutAnchor` with Google Maps on iOS +* iOS: [#2501](https://github.com/react-community/react-native-maps/pull/2501) Fixed issue that app crashes after trigger Marker `onDragEnd` +* iOS: [#2359](https://github.com/react-community/react-native-maps/pull/2359) Fixed zIndex didn't work on map moving on iOS 11 +* iOS: [#2185](https://github.com/react-community/react-native-maps/pull/2185) Fixed Xcode warnings for format, pointer type, unused var +* iOS: [#2154](https://github.com/react-community/react-native-maps/pull/2154) Fixed CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF warnings +* iOS: [#2341](https://github.com/react-community/react-native-maps/pull/2341) Fixed warnings about `self` +* Android: [#2224](https://github.com/react-community/react-native-maps/pull/2224) Respect `tappable` prop on Android +* Android: [#2390](https://github.com/react-community/react-native-maps/pull/2390) Consider pixel density in coordinate<->point conversion +* Android: [#2477](https://github.com/react-community/react-native-maps/pull/2477), [#2487](https://github.com/react-community/react-native-maps/pull/2487) Implemented `tracksViewChanges` on Android +* Android: [#2478](https://github.com/react-community/react-native-maps/pull/2478) Let `onVisibilityChanged()` be called on children (mainly fixes `Image` issues) +* Android: [#2375](https://github.com/react-community/react-native-maps/pull/2375) Fixed `lineCap` of Polyline +* Android: [#2320](https://github.com/react-community/react-native-maps/pull/2320) Fixed custom marker updates on Android +* Android: [#2197](https://github.com/react-community/react-native-maps/pull/2197) Fixed overlay removal bug +* Android: [#2317](https://github.com/react-community/react-native-maps/pull/2317) Fixed disabling the toolbar and my location button +* Android: [#2472](https://github.com/react-community/react-native-maps/pull/2472) Fixed compilation error due to minSDK in manifest +* Android: [#2172](https://github.com/react-community/react-native-maps/pull/2172) Fixed crash for Android API level below 18 on isFromMockProvider +* Internal: [#2462](https://github.com/react-community/react-native-maps/pull/2462) Fixed packager script path in `pacakage.json` +* Internal: [#2480](https://github.com/react-community/react-native-maps/pull/2480) Fix peer dependencies +* TypeScript: [#2165](https://github.com/react-community/react-native-maps/pull/2165) Typings improvements & fixes +* Docs: [#2541](https://github.com/react-community/react-native-maps/pull/2541) Vastly improved installation guideline +* Docs: [#2171](https://github.com/react-community/react-native-maps/pull/2171) Add 'none' option to docs for `mapType` of `MapView` +* Docs: [#2174](https://github.com/react-community/react-native-maps/pull/2174) Add opacity to `Marker` API docs +* Docs: [#2181](https://github.com/react-community/react-native-maps/pull/2181), [#2219](https://github.com/react-community/react-native-maps/pull/2219) Add note about mandatory `NSLocationWhenInUseUsageDescription` +* Docs: [#2381](https://github.com/react-community/react-native-maps/pull/2381), [#2358](https://github.com/react-community/react-native-maps/pull/2358), [#2363](https://github.com/react-community/react-native-maps/pull/2363), [#2429](https://github.com/react-community/react-native-maps/pull/2429), [#2425](https://github.com/react-community/react-native-maps/pull/2425) Documentation improvements + +## 0.21.0 (March 31, 2018) +* Common: [#2030](https://github.com/react-community/react-native-maps/pull/2030) Broadened peer-dependency support +* Common: [#2035](https://github.com/react-community/react-native-maps/pull/2035), [#2113](https://github.com/react-community/react-native-maps/pull/2113), & [#2141](https://github.com/react-community/react-native-maps/pull/2141) Typescript improvements and fixes +* Common: [#2011](https://github.com/react-community/react-native-maps/pull/2011) Add suport for KML file (Only Markers) +* Common: [#2053](https://github.com/react-community/react-native-maps/pull/2053) Fix 'module undefined' for React Native >= 0.54 +* Common: [#2131](https://github.com/react-community/react-native-maps/pull/2131) Fix initialRegion for React Native >= 0.54 +* Common: [#2115](https://github.com/react-community/react-native-maps/pull/2115) Upgrade React Native peer dependency to 0.54 +* Common: [#2032](https://github.com/react-community/react-native-maps/pull/2032) Add onMyLocationChange event +* Common: [#2039](https://github.com/react-community/react-native-maps/pull/2039) Fixed problem with pointForCoordinate and coordinateForPoint methods +* Common: [#2050](https://github.com/react-community/react-native-maps/pull/2050) Add support for onPoiClick +* iOS: [#2022](https://github.com/react-community/react-native-maps/pull/2022) Add support for Map.Overlay +* iOS: [#2068](https://github.com/react-community/react-native-maps/pull/2068) Prevent marker press from calling MapView onPress +* iOS: [#2057](https://github.com/react-community/react-native-maps/pull/2057) Fixed polygon and polyline not re-rendering when changing tile URL (AirMaps) +* iOS: [#2101](https://github.com/react-community/react-native-maps/pull/2101) Fixed re-render not updating MapView.Circle component in UI when radius or center coordinates change (AirMaps) +* Android: [#2111](https://github.com/react-community/react-native-maps/pull/2111) Allow vector drawables to be used as markers +* Android: [#2132](https://github.com/react-community/react-native-maps/pull/2132) Add mock-provider boolean on each location update +* Android: [#2047](https://github.com/react-community/react-native-maps/pull/2047) Check for presence of project-wide (ext) Gradle configuration properties `compileSdkVersion`, `targetSdkVersion`, `buildToolsVersion`, `supportLibVersion`, `googlePlayServicesVersion`, and `androidMapsUtilsVersion`. This provides a better mechanism for aligning the requirements of the module with that of the host project. +* Android: [#2096](https://github.com/react-community/react-native-maps/pull/2096) Updated gradle configuration for gradle 3.0.0+ + +## 0.20.1 (February 13, 2018) +* Common: [hotfix PROVIDER_GOOGLE](https://github.com/react-community/react-native-maps/commit/cd868ea7b33a04c8bdd5e909cf134a133b2cb316) +* iOS: [#2019](https://github.com/airbnb/react-native-maps/pull/2019) Exposing the maximumZ property to AIRMapUrlTile + +## 0.20.0 (February 9, 2018) +* Common: [#1889](https://github.com/airbnb/react-native-maps/pull/1889) Fix for 'Animated.Region undefined constructor' in recent react-native version. +* iOS: [#1853](https://github.com/airbnb/react-native-maps/pull/1853) Fixed onMapReady no longer getting called on iOS +* Android: [#1906](https://github.com/airbnb/react-native-maps/pull/1906) Manage Zoom Controls visibility on the map +* iOS: [#1911](https://github.com/airbnb/react-native-maps/pull/1911) Add gradient/multi-color polyline support for iOS (MapKit) +* Android: [#1918](https://github.com/airbnb/react-native-maps/pull/1918) Ground Overlay Support +* Common: [#1851](https://github.com/airbnb/react-native-maps/pull/1851) New methods to convert between LatLng and Point +* iOS: [#1846](https://github.com/airbnb/react-native-maps/pull/1846) Fix callouts appearing behind markers +* iOS: [#1969](https://github.com/airbnb/react-native-maps/pull/1969) Added tracksInfoWindowChanges property to iOS Google Maps +* iOS: [#1960](https://github.com/airbnb/react-native-maps/pull/1960) Fixed gradient polyline not always fully drawn + stability issues +* iOS: [#1953](https://github.com/airbnb/react-native-maps/pull/1953) Fix onMapReady not getting called after first time, initialRegion lat/lng delta not setting properly, setRegion method getting called even when map is not ready and prevent onRegionChange/ onRegionChangeComplete event until initialRegion or region is set. +* Android: [#1781](https://github.com/airbnb/react-native-maps/pull/1781) Polygon holes support +* Android: [#1976](https://github.com/airbnb/react-native-maps/pull/1976) Add native animation for Markers on Android + +## 0.19.0 (December 14, 2017) +* Common: [#1715](https://github.com/airbnb/react-native-maps/pull/1715) Fixed region/initialRegion null overrides of this.props +* Common: [#1876](https://github.com/airbnb/react-native-maps/pull/1876) Added support for locally stored tile overlay +* iOS: [#1854](https://github.com/airbnb/react-native-maps/pull/1854) Update GoogleMaps dependency to 2.5.0 + +## 0.18.3 (November 30, 2017) +* Android: [#1839](https://github.com/airbnb/react-native-maps/pull/1839) [AirGoogleMapManager] Use RCTDirectEventBlock for onMarkerPress + +## 0.18.2 (November 29, 2017) +* Android: [#1835](https://github.com/airbnb/react-native-maps/pull/1835) [AirMapView] Null check map instance on view methods + +## 0.18.1 (November 28, 2017) +* Android: [#1828](https://github.com/airbnb/react-native-maps/pull/1828) [AirMapManager] Update MapBuilder for getCommandsMap to support all entires + +## 0.18.0 (November 28, 2017) +* Android/iOS: [#1587](https://github.com/airbnb/react-native-maps/pull/1750) Add support to set map boundaries +* Android/iOS: [#1750](https://github.com/airbnb/react-native-maps/pull/1750) Add mapPadding property +* Common: [#1792](https://github.com/airbnb/react-native-maps/pull/1792) Make all components use ViewPropTypes || View.propTypes +* iOS: [#1774](https://github.com/airbnb/react-native-maps/pull/1774) Added missing parameters to google map screenshot +* iOS: [#1824](https://github.com/airbnb/react-native-maps/pull/1824) Add new iOS `mutedStandard` map-type +* iOS: [#1705](https://github.com/airbnb/react-native-maps/pull/1705) Enable control of Google Maps Marker tracksViewChanges property. +* Android: [#1710](https://github.com/airbnb/react-native-maps/pull/1710) Added support for new Android camera movement APIs +* iOS: [#1741](https://github.com/airbnb/react-native-maps/pull/1741) Fixed iOS google MapView.onMarkerPress not receiving the marker identifier +* iOS: [#1816](https://github.com/airbnb/react-native-maps/pull/1816) Fix The name of the given podspec ‘yoga' doesn't match the expected one ‘Yoga' +* iOS: [#1797](https://github.com/airbnb/react-native-maps/pull/1797) Fixed onMapReady event on iOS to resemble onMapReady on Android +* Common: [#1817](https://github.com/airbnb/react-native-maps/pull/1817) Allow fitToCoordinates to be called without options parameter + +## 0.17.1 (October 18, 2017) +* Common: [#1687](https://github.com/airbnb/react-native-maps/pull/1687) Fixed TypeScript definitions + +## 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: [#1601](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 + +* Restructure project #1164 + +* Add showsIndoorLevelPicker -> setIndoorLevelPickerEnabled to MapView #1019 +[#1188](https://github.com/airbnb/react-native-maps/pull/1188) + +* iOS - Added onPress support for Polygons on Google Maps +[#1024](https://github.com/airbnb/react-native-maps/pull/1024) + +* Add customized user location annotation text +[#1049](https://github.com/airbnb/react-native-maps/pull/1049) + +* iOS - Google Maps - Add `showsMyLocationButton` support +[#1157](https://github.com/airbnb/react-native-maps/pull/1157) + + +## Patches + +* Fix getResources() null crash in mapview +[#1188](https://github.com/airbnb/react-native-maps/pull/1188) + +* Rename MapKit category to avoid conflicts with the one in RN +[#1172](https://github.com/airbnb/react-native-maps/pull/1172) + +* Upgrade GMS dependencies to 10.2.0 +[#1169](https://github.com/airbnb/react-native-maps/pull/1169) + +* fix multiple-instance memory leak +[#1130](https://github.com/airbnb/react-native-maps/pull/1130) + +* fix onSelected event for markers with custom view +[#1079](https://github.com/airbnb/react-native-maps/pull/1079) + +* Crash in our App fix +[#1096](https://github.com/airbnb/react-native-maps/pull/1096) + +* Use local RCTConvert+MapKit instead of the one in React Native +[#1138](https://github.com/airbnb/react-native-maps/pull/1138) + + +## 0.13.1 (March 21, 2017) + + +## Enhancements + +* Add id identifier to marker-press event on Android +[#1008](https://github.com/airbnb/react-native-maps/pull/1008) + (@stan229) + +* setNativeProps, marker opacity, nested components +[#940](https://github.com/airbnb/react-native-maps/pull/940) + (@unboundfire) + + +## Patches + +* Update the android buildToolsVersion to 25.0.0 +[#1152](https://github.com/airbnb/react-native-maps/pull/1152) + (@markusguenther) + +* use `provided` for RN gradle dependency +[#1151](https://github.com/airbnb/react-native-maps/pull/1151) + (@gpeal) + +* fix null activity crash +[#1150](https://github.com/airbnb/react-native-maps/pull/1150) + (@lelandrichardson) + +* Updated Google play services and gradle build plugin +[#1023](https://github.com/airbnb/react-native-maps/pull/1023) + (@chris-at-translate) + +* Sets the map value for the AirMapUrlTile so that it can be updated properly +[#992](https://github.com/airbnb/react-native-maps/pull/992) + (@jschloer) + +* onPress and onCalloutPress doesn't trigger on markers in iOS +[#954](https://github.com/airbnb/react-native-maps/pull/954) + (@RajkumarPunchh) + + + +## 0.13.0 (January 6, 2017) + +### Breaking Changes + +* Update iOS header imports and JS SyntheticEvent import for RN 0.40 + [#923](https://github.com/airbnb/react-native-maps/pull/923) + (@ide) + +### Patches + +* Fix issue where callouts sometimes overlap or don't appear + [#936](https://github.com/airbnb/react-native-maps/pull/936) + (@RajkumarPunchh) + +## 0.12.3 (January 6, 2017) + +### Patches + +* Fix "Animating with MapViews" example – fixes #763 + [#888](https://github.com/airbnb/react-native-maps/pull/888) + (@javiercr) +* [iOS] Fix "Option 2" method of building Google Maps + [#900](https://github.com/airbnb/react-native-maps/pull/900) + (@vjeranc) +* [Android] Fix exception when animating region during initialization + [#901](https://github.com/airbnb/react-native-maps/pull/901) + (@mlanter) +* Updated documentation + [#902](https://github.com/airbnb/react-native-maps/pull/902), + [#904](https://github.com/airbnb/react-native-maps/pull/904), + [#910](https://github.com/airbnb/react-native-maps/pull/910) + (@anami, @dboydor, @ali-alamine) + + +## 0.12.2 (December 9, 2016) + +### Patches + +* [Android] Added support for taking snapshots on Android + [#625](https://github.com/airbnb/react-native-maps/pull/625) + (@IjzerenHein) +* [iOS] Allow legalLabelInsets to be changed and animated + [#873](https://github.com/airbnb/react-native-maps/pull/873) + (@scarlac) +* Added rotation attribute documentation + [#871](https://github.com/airbnb/react-native-maps/pull/871) + (@Arman92) +* Update mapview.md documentation + [#866](https://github.com/airbnb/react-native-maps/pull/866) + (@dccarmo) + + +## 0.12.1 (December 6, 2016) + +This release only corrects the version in package.json. + +## 0.12.0 (December 6, 2016) + +NOTE: This version was not published because package.json was not properly updated + +### Breaking Changes + +* [android] If we've disabled scrolling within the map, then don't capture the touch events + [#664](https://github.com/airbnb/react-native-maps/pull/664) + (@mikelambert) +* [android] Use latest Google Play Services + [#731](https://github.com/airbnb/react-native-maps/pull/731) + (@mlanter) +* [android] update google play services + [#805](https://github.com/airbnb/react-native-maps/pull/805) + (@lrivera) + +### Patches + +* [iOS] Support iOS SDK < 10 ( XCode < 8 ) + [#708](https://github.com/airbnb/react-native-maps/pull/708) + (@rops) +* [iOS] Added showsUserLocation property support for Google Maps + [#721](https://github.com/airbnb/react-native-maps/pull/721) + (@julien-rodrigues) +* [iOS] Added Google Maps Circle, Polygon, Polyline, MapType Support + [#722](https://github.com/airbnb/react-native-maps/pull/722) + (@unboundfire) +* [iOS] Fix Anchor point on Google Maps iOS + [#734](https://github.com/airbnb/react-native-maps/pull/734) + (@btoueg) +* [Google Maps iOS] Marker init with image props. + [#738](https://github.com/airbnb/react-native-maps/pull/738) + (@btoueg) +* [iOS] Fix dynamic imageSrc removal + [#737](https://github.com/airbnb/react-native-maps/pull/737) + (@btoueg) +* [iOS] implement fitToSuppliedMarkers and fitToCoordinates for google + [#750](https://github.com/airbnb/react-native-maps/pull/750) + (@gilbox) +* [iOS][android] Add onPress for polygons and polylines on iOS and Android + [#760](https://github.com/airbnb/react-native-maps/pull/760) + (@frankrowe) +* [iOS] Fix flicker of map pins on state change + [#728](https://github.com/airbnb/react-native-maps/pull/728) + (@mlanter) +* [iOS] Set region only when view has width&height + [#785](https://github.com/airbnb/react-native-maps/pull/785) + (@gilbox) +* [iOS] Implements animateToRegion for Google + [#779](https://github.com/airbnb/react-native-maps/pull/779) + (@btoueg) +* [iOS] Google Maps Custom Tile Support + [#770](https://github.com/airbnb/react-native-maps/pull/770) + (@unboundfire) +* [android] Map Styling for android + [#808](https://github.com/airbnb/react-native-maps/pull/808) + (@ali-alamine using @azt3k code) +* [iOS] IOS Google Map styling + [#817](https://github.com/airbnb/react-native-maps/pull/817) + (@ali-alamine using @azt3k code) +* [iOS] Add support for polygon holes for Apple Maps and Google Maps on iOS + [#801](https://github.com/airbnb/react-native-maps/pull/801) + (@therealgilles) +* [iOS] Fixes #470. Support legalLabelInsets on Apple Maps + [#840](https://github.com/airbnb/react-native-maps/pull/840) + (@scarlac) + +## 0.11.0 (October 16, 2016) + +NOTE: `0.10.4` was released *after* this version, and it's possible +`0.11.0` does not include everything in `0.10.4`. (see #851) + +### Breaking Changes + +* Update example app for RN 0.35, fix Gmaps bug for 0.35 + [#695](https://github.com/airbnb/react-native-maps/pull/695) + (@spikebrehm) +* Upgraded to RN 0.35 + [#680](https://github.com/airbnb/react-native-maps/pull/680) + (@eugenehp) + +### Patches + +* Update installation.md + [#696](https://github.com/airbnb/react-native-maps/pull/696) + (@securingsincity) +* [android] Fixes crash during Activity onPause() + [#694](https://github.com/airbnb/react-native-maps/pull/694) + (@felipecsl) +* Included MapUrlTile usage in README.md + [#687](https://github.com/airbnb/react-native-maps/pull/687) + (@ochanje210) +* [android] Add parameter to disable the moving on marker press + [#676](https://github.com/airbnb/react-native-maps/pull/676) + (@mlanter) +* Add support for setting zIndex on markers + [#675](https://github.com/airbnb/react-native-maps/pull/675) + (@mlanter) + +## 0.10.4 (October 31, 2016) + +### Patches + +* [iOS] implement fitToSuppliedMarkers and fitToCoordinates for google maps + [#750](https://github.com/airbnb/react-native-maps/pull/750) + (@gilbox) +* [android] If we've disabled scrolling within the map, then don't capture the touch events + [#664](https://github.com/airbnb/react-native-maps/pull/664) + (@mikelambert) +* [iOS] Fix Anchor point on Google Maps iOS + [#734](https://github.com/airbnb/react-native-maps/pull/734) + (@btoueg) +* [iOS] Added showsUserLocation property support for Google Maps + [#721](https://github.com/airbnb/react-native-maps/pull/721) + (@julien-rodrigues) +* [iOS][android] Add support for setting zIndex on markers + [#675](https://github.com/airbnb/react-native-maps/pull/675) + (@mlanter) +* [android] Add parameter to disable the moving on marker press + [#676](https://github.com/airbnb/react-native-maps/pull/676) + (@mlanter) +* NOTE: v0.10.3 was not published + +## 0.10.2 (October 19, 2016) + +### Patches + +* [android] Fixes crash during Activity onPause() (fixes #414) + [#694](https://github.com/airbnb/react-native-maps/pull/694) + (@felipecsl) + +## 0.10.1 (October 10, 2016) + +This release fixes issue [#656](https://github.com/airbnb/react-native-maps/issues/656) + +### Patches + +* [android] fix gradle build setup for explorer, bump to gradle 2.2.0 + [#666](https://github.com/airbnb/react-native-maps/pull/666) + (@gilbox) +* [android] fix getAirMapName to fix ref-based commands + [#665](https://github.com/airbnb/react-native-maps/pull/665) + (@gilbox) + +## 0.10.0 (October 5, 2016) + +### Breaking Changes + +* Upgrade to `react-native@0.33.0` + +## 0.9.0 (September 28, 2016) + +As of this release, this repository has moved from +`lelandrichardson/react-native-maps` to `airbnb/react-native-maps`. + +### Breaking Changes + +* [iOS] Support Google Maps on iOS + [#548](https://github.com/airbnb/react-native-maps/pull/548) + (@gilbox) + +### Patches + +* Added support for AnimatedRegion without modifying the AnimatedImplementation.js of react-native + [#608](https://github.com/airbnb/react-native-maps/pull/608) + (@IjzerenHein) +* [iOS] Remove pod stuff. Fix AirMaps.xcodeproj related to missing pod stuff + [#620](https://github.com/airbnb/react-native-maps/pull/620) + (@gilbox) +* [iOS] Fix import of AIRMapSnapshot + [#622](https://github.com/airbnb/react-native-maps/pull/622) + (@spikebrehm) +* [iOS] Fix nullability issue + [#614](https://github.com/airbnb/react-native-maps/pull/614) + (@simonmitchell) +* [iOS] Added support for drawing polylines on snapshots on iOS + [#615](https://github.com/airbnb/react-native-maps/pull/615) + (@IjzerenHein) +* Add `fitToCoordinates` method + [#545](https://github.com/airbnb/react-native-maps/pull/545) + (@naoufal) +* [Android] Fix list of examples on Android + [#597](https://github.com/airbnb/react-native-maps/pull/597) + (@spikebrehm) +* [Android] Fix overlapping map issue + [#589](https://github.com/airbnb/react-native-maps/pull/589) + (@fdnhkj) +* Add tile overlay support + [#595](https://github.com/airbnb/react-native-maps/pull/595) + (@cascadian, @spikebrehm) +* [Android] Support Android LiteMode + [#546](https://github.com/airbnb/react-native-maps/pull/546) + (@rops) +* s/lelandrichardson/airbnb/ + [#573](https://github.com/airbnb/react-native-maps/pull/573) + (@spikebrehm) +* [Android] Don't fit to elements if no positions added + [#567](https://github.com/airbnb/react-native-maps/pull/567) + (@ryankask) +* [iOS] Add class prefix to EmptyCalloutBackgroundView + [#563](https://github.com/airbnb/react-native-maps/pull/563) + (@terribleben) +* [Android] Minor code cleanup + [#564](https://github.com/airbnb/react-native-maps/pull/564) + (@felipecsl) +* Documentation updates + [#566](https://github.com/airbnb/react-native-maps/pull/566), + [#591](https://github.com/airbnb/react-native-maps/pull/591), + [#601](https://github.com/airbnb/react-native-maps/pull/601), + [#602](https://github.com/airbnb/react-native-maps/pull/602), + [#624](https://github.com/airbnb/react-native-maps/pull/624) + (@felipecsl, @Alastairm, @Keksike, @bbodenmiller) + +## 0.8.2 (September 8, 2016) + +We realized immediately after publishing 0.8.1 that the NPM package contained +some test code in the `example2/` directory that contained a copy of the +`react-native` package, causing this packager error: + +``` +Failed to build DependencyGraph: @providesModule naming collision: + Duplicate module name: String.prototype.es6 + Paths: /Users//node_modules/react-native-maps/example2/node_modules/react-native/packager/react-packager/src/Resolver/polyfills/String.prototype.es6.js collides with /Users//node_modules/react-native/packager/react-packager/src/Resolver/polyfills/String.prototype.es6.js + +This error is caused by a @providesModule declaration with the same name across two different files. +``` + +0.8.2 is identical to 0.8.1, except with the offending code removed from the NPM package. + + +## 0.8.1 (September 8, 2016) *[DEPRECATED]* + +#### *NOTE: 0.8.1 has been unpublished from NPM because it was faulty. Please use 0.8.2.* + +### Patches + +- [Android] Use latest available (wildcard version) of RN to build Android ([PR #547](https://github.com/airbnb/react-native-maps/pull/547)) +- [Android] Use `Activity` to call `MapsInitialier.initialize()` ([PR #449](https://github.com/airbnb/react-native-maps/pull/449)) +- [Android] Fix file path for `AirMapModule` ([PR #526](https://github.com/airbnb/react-native-maps/pull/526)) +- [Android] Fix path to React Native in `node_modules` ([PR #527](https://github.com/airbnb/react-native-maps/pull/527)) +- [Android] Bump Google Play Services dependency to `9.4.0` ([PR #533](https://github.com/airbnb/react-native-maps/pull/533)) +- [iOS] Fix a few warnings ([PR #534](https://github.com/airbnb/react-native-maps/pull/534)) +- [JS] Fix ESLint violations ([PR #515](https://github.com/airbnb/react-native-maps/pull/515)) + +## 0.8.0 (August 30, 2016) + +### Breaking Changes + +- Upgrade to `react-native@0.32.0`, and update Android code to match ([#502](https://github.com/airbnb/react-native-maps/pull/502)) + +### Patches + +- [android] Add `showsMyLocationButton` prop ([#382](https://github.com/airbnb/react-native-maps/pull/382)) + +- Add `fitToSuppliedMarkers()` method ([#386](https://github.com/airbnb/react-native-maps/pull/386)) + +- [ios] Update AirMapMarker to use loadImageWithURLRequest ([#389](https://github.com/airbnb/react-native-maps/pull/389)) + +- Improvements to watch and copy script ([#445](https://github.com/airbnb/react-native-maps/pull/445)) + +- [ios] Added check on marker class in predicate ([#485](https://github.com/airbnb/react-native-maps/pull/485)) + +- Use `StyleSheet.absoluteFillObject` where appropriate ([#500](https://github.com/airbnb/react-native-maps/pull/500)) and ([#493](https://github.com/airbnb/react-native-maps/pull/493)) + +- Add ESLint and fix a number of linting violations ([#501](https://github.com/airbnb/react-native-maps/pull/501)) + +- Remove unused `NativeMethodsMixin` for compat with RN 0.32 ([#511](https://github.com/airbnb/react-native-maps/pull/511)) + ## 0.7.1 (July 9, 2016) ### Patches -- Fix iOS CocoaPods Issue ([#308](https://github.com/lelandrichardson/react-native-maps/pull/308)) +- Fix iOS CocoaPods Issue ([#308](https://github.com/airbnb/react-native-maps/pull/308)) @@ -12,18 +1431,18 @@ ### Breaking Changes -- RN 0.29 compatibility changes ([#363](https://github.com/lelandrichardson/react-native-maps/pull/363) and [#370](https://github.com/lelandrichardson/react-native-maps/pull/370)) +- RN 0.29 compatibility changes ([#363](https://github.com/airbnb/react-native-maps/pull/363) and [#370](https://github.com/airbnb/react-native-maps/pull/370)) ### Patches -- Fixing scrolling map inside a scrollView ([#343](https://github.com/lelandrichardson/react-native-maps/pull/343)) +- Fixing scrolling map inside a scrollView ([#343](https://github.com/airbnb/react-native-maps/pull/343)) -- Fix shouldUsePinView ([#344](https://github.com/lelandrichardson/react-native-maps/pull/344)) +- Fix shouldUsePinView ([#344](https://github.com/airbnb/react-native-maps/pull/344)) -- Not calling setLoadingIndicatorColor when null ([#337](https://github.com/lelandrichardson/react-native-maps/pull/337)) +- Not calling setLoadingIndicatorColor when null ([#337](https://github.com/airbnb/react-native-maps/pull/337)) -- Fixes `Undefined symbols for architecture x86_64: “std::terminate()”` ([#329](https://github.com/lelandrichardson/react-native-maps/pull/329)) +- Fixes `Undefined symbols for architecture x86_64: “std::terminate()”` ([#329](https://github.com/airbnb/react-native-maps/pull/329)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3aef90d46 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +Thank you for helping this project become a better library :) + +- [Triage](#triage) +- [Reporting Bugs](#reporting-bugs) +- [Providing a feature request](#providing-a-feature-request) +- [Pull requests](#pull-requests) + +## Triage + +This is one of the easiest and most effective ways of helping out. If you see an open issue, try and reproduce the bug yourself, and comment with the result. If the issue is lacking any information to reproduce the bug, let the author know. + +## Reporting Bugs + +Open an issue, making sure to follow the bug report template. + +## Providing a feature request + +Open an issue, making sure to follow the Feature request template. + +## Pull requests + +### Getting started + +- If there isn't one already, open an issue describing the bug or feature request that you are going to solve in your pull request. +- Create a fork of react-native-maps + - If you already have a fork, make sure it is up to date +- Git clone your fork and run `yarn` in the base of the cloned repo to setup your local development environment. +- Create a branch from the master branch to start working on your changes. + +### Committing + +- When you made your changes, run `yarn lint` & `yarn test` to make sure the code you introduced doesn't cause any obvious issues. +- When you are ready to commit your changes, use the [Angular conventional commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/#summary) convention for you commit messages, as we use your commits when releasing new versions. +- Use present tense: "add awesome component" not "added awesome component" +- Limit the first line of the commit message to 100 characters +- Reference issues and pull requests before committing + +### Creating the pull request + +- The title of the PR needs to follow the same conventions as your commit messages, as it might be used in case of a squash merge. +- Create the pull request against the beta branch. diff --git a/LICENSE b/LICENSE index f6981d805..6659955b0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Leland Richardson +Copyright (c) 2015 Airbnb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6b36fb930..5e7fbeded 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,59 @@ -# react-native-maps +# react-native-maps [![npm version](https://img.shields.io/npm/v/react-native-maps.svg?style=flat)](https://www.npmjs.com/package/react-native-maps) React Native Map components for iOS + Android +## Contributing + +This project is being maintained by a small group of people, and any help with issues and pull requests are always appreciated. If you are able and willing to contribute, please read the [guidelines](./CONTRIBUTING.md). + ## Installation See [Installation Instructions](docs/installation.md). +See [Setup Instructions for the Included Example Project](docs/examples-setup.md). + ## Compatibility -Due to the rapid changes being made in the React Native ecosystem, we are not officially going to -support this module on anything but the latest version of React Native. With that said, we will do -our best to stay compatible with older versions as much that is practical, and the peer dependency -of this requirement is set to `"react-native": "*"` explicitly for this reason. If you are using -an older version of React Native with this module though, some features may be buggy. +## React Native Compatibility + +### Important Notes: + +- **Fabric is not yet supported**: + This feature is currently under development. Fabric is enabled by default in React Native `0.76` and above, so **please disable it** for now. + Follow updates on this issue here: [react-native-maps/issues/5206](https://github.com/react-native-maps/react-native-maps/issues/5206). + Kindly refrain from opening duplicate tickets regarding this matter. -### Note about React requires +### Version Requirements: + +- **Version `1.14.0` and above**: Requires **React Native `>= 0.74`**. +- **Versions below `1.14.0`**: Require **React Native `>= 0.64.3`**. + +## Component API + +[`` Component API](docs/mapview.md) -Since react-native 0.25.0, `React` should be required from `node_modules`. -React Native versions from 0.18 should be working out of the box, for lower -versions you should add `react` as a dependency in your `package.json`. +[`` Component API](docs/marker.md) + +[`` Component API](docs/callout.md) + +[`` Component API](docs/polygon.md) + +[`` Component API](docs/polyline.md) + +[`` Component API](docs/circle.md) + +[`` Component API](docs/overlay.md) + +[`` Component API](docs/heatmap.md) + +[`` Component API](docs/geojson.md) ## General Usage ```js import MapView from 'react-native-maps'; ``` + or ```js @@ -38,15 +67,16 @@ declaratively controlling features on the map. ### Rendering a Map with an initial region ## MapView + ```jsx - + ``` ### Using a MapView while controlling the region as state @@ -80,61 +110,211 @@ render() { ### Rendering a list of markers on a map ```jsx - - {this.state.markers.map(marker => ( - + {this.state.markers.map((marker, index) => ( + ))} - +; ``` -### Rendering a Marker with a custom view +### Rendering a Marker with a custom image + +1. You need to generate an `png` image with various resolution (lets call them `custom_pin`) - for more information go to [Android](https://developer.android.com/studio/write/resource-manager#import), [iOS](https://developer.apple.com/documentation/xcode/adding-images-to-your-xcode-project) +2. put all images in Android drawables and iOS assets dir +3. Now you can use the following code: ```jsx - - - + ``` -### Rendering a Marker with a custom image +Note: You can also pass the image binary data like `image={require('custom_pin.png')}`, but this will not scale good with the different screen sizes. + +### Rendering a Marker with a custom view + +Note: This has performance implications, if you wish for a simpler solution go with a custom image (save your self the headache) ```jsx - + + + ``` ### Rendering a custom Marker with a custom Callout ```jsx - +import {Callout} from 'react-native-maps'; + + - + - - + +; ``` ### Draggable Markers ```jsx - this.setState({ x: e.nativeEvent.coordinate })} /> ``` +### Using a custom Tile Overlay + +#### Tile Overlay using tile server + +```jsx +import {UrlTile} from 'react-native-maps'; + + + +; +``` + +For Android: add the following line in your AndroidManifest.xml + +```xml + +``` + +For IOS: configure [App Transport Security](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) in your app + +## React Native Configuration for Fabric / New Architecture + +This library works with Fabric using the [New Renderer Interop Layer](https://github.com/reactwg/react-native-new-architecture/discussions/135) + +There is a warning message that those steps are not necessary; but we couldn't get the example working without them so far. + +### Configuration Steps + +1. **Open your configuration file**: Locate the `react-native-config` file in your project directory. + +2. **Add the following configuration**: Include the `unstable_reactLegacyComponentNames` array for both Android and iOS platforms as shown below: + +```javascript +module.exports = { + project: { + android: { + unstable_reactLegacyComponentNames: [ + 'AIRMap', + 'AIRMapCallout', + 'AIRMapCalloutSubview', + 'AIRMapCircle', + 'AIRMapHeatmap', + 'AIRMapLocalTile', + 'AIRMapMarker', + 'AIRMapOverlay', + 'AIRMapPolygon', + 'AIRMapPolyline', + 'AIRMapUrlTile', + 'AIRMapWMSTile', + ], + }, + ios: { + unstable_reactLegacyComponentNames: [ + 'AIRMap', + 'AIRMapCallout', + 'AIRMapCalloutSubview', + 'AIRMapCircle', + 'AIRMapHeatmap', + 'AIRMapLocalTile', + 'AIRMapMarker', + 'AIRMapOverlay', + 'AIRMapPolygon', + 'AIRMapPolyline', + 'AIRMapUrlTile', + 'AIRMapWMSTile', + ], + }, + }, +}; +``` + +checkout the example project to see it in action. + +#### Tile Overlay using local tiles + +Tiles can be stored locally within device using xyz tiling scheme and displayed as tile overlay as well. This is usefull especially for offline map usage when tiles are available for selected map region within device storage. + +```jsx +import {LocalTile} from 'react-native-maps'; + + + +; +``` + +For Android: LocalTile is still just overlay over original map tiles. It means that if device is online, underlying tiles will be still downloaded. If original tiles download/display is not desirable set mapType to 'none'. For example: + +``` + +``` + +See [OSM Wiki](https://wiki.openstreetmap.org/wiki/Category:Tile_downloading) for how to download tiles for offline usage. + +### Overlaying other components on the map -## Examples +Place components that you wish to overlay `MapView` underneath the `MapView` closing tag. Absolutely position these elements. + +```jsx +render() { + return ( + + + ); +} +``` + +### Customizing the map style (Google Maps Only) + +The `` Google Maps on iOS and Android supports styling via google cloud platform, the styled maps are published under a googleMapId, by simply setting the property googleMapId to the MapView you can use that styled map +more info here: [google map id](https://developers.google.com/maps/documentation/get-map-id) ### MapView Events @@ -143,31 +323,26 @@ This example displays some of them in a log as a demonstration. ![](http://i.giphy.com/3o6UBpncYQASu2WTW8.gif) ![](http://i.giphy.com/xT77YdviLqtjaecRYA.gif) - - ### Tracking Region / Location ![](http://i.giphy.com/3o6UBoPSLlIKQ2dv7q.gif) ![](http://i.giphy.com/xT77XWjqECvdgjx9oA.gif) - - - ### Programmatically Changing Region One can change the mapview's position using refs and component methods, or by passing in an updated -`region` prop. The component methods will allow one to animate to a given position like the native +`region` prop. The component methods will allow one to animate to a given position like the native API could. ![](http://i.giphy.com/3o6UB7poyB6YJ0KPWU.gif) ![](http://i.giphy.com/xT77Yc4wK3pzZusEbm.gif) +### Changing the style of the map +![](http://i.imgur.com/a9WqCL6.png) ### Arbitrary React Views as Markers ![](http://i.giphy.com/3o6UBcsCLoLQtksJxe.gif) ![](http://i.giphy.com/3o6UB1qGEM9jYni3KM.gif) - - ### Using the MapView with the Animated API The `` component can be made to work with the Animated API, having the entire `region` prop @@ -185,14 +360,10 @@ Markers' coordinates can also be animated, as shown in this example: ![](http://i.giphy.com/xTcnTelp1OwGPu1Wh2.gif) ![](http://i.giphy.com/xTcnT6WVpwlCiQnFW8.gif) - - ### Polygon Creator ![](http://i.giphy.com/3o6UAZWqQBkOzs8HE4.gif) ![](http://i.giphy.com/xT77XVBRErNZl3zyWQ.gif) - - ### Other Overlays So far, ``, ``, and `` are available to pass in as children to the @@ -200,7 +371,11 @@ So far, ``, ``, and `` are available to pass in ![](http://i.giphy.com/xT77XZCH8JpEhzVcNG.gif) ![](http://i.giphy.com/xT77XZyA0aYeOX5jsA.gif) +### Gradient Polylines (iOS MapKit only) +Gradient polylines can be created using the `strokeColors` prop of the `` component. + +![](https://i.imgur.com/P7UeqAm.png?1) ### Default Markers @@ -209,11 +384,9 @@ color of the default marker by using the `pinColor` prop. ![](http://i.giphy.com/xT77Y0pWKmUUnguHK0.gif) ![](http://i.giphy.com/3o6UBfk3I58VIwZjVe.gif) - - ### Custom Callouts -Callouts to markers can be completely arbitrary react views, similar to markers. As a result, they +Callouts to markers can be completely arbitrary react views, similar to markers. As a result, they can be interacted with like any other view. Additionally, you can fall back to the standard behavior of just having a title/description through @@ -222,9 +395,10 @@ the ``'s `title` and `description` props. Custom callout views can be the entire tooltip bubble, or just the content inside of the system default bubble. -![](http://i.giphy.com/xT77XNePGnMIIDpbnq.gif) ![](http://i.giphy.com/xT77YdU0HXryvoRqaQ.gif) - +To handle press on specific subview of callout use `` with `onPress`. +See `Callouts.js` example. +![](http://i.giphy.com/xT77XNePGnMIIDpbnq.gif) ![](http://i.giphy.com/xT77YdU0HXryvoRqaQ.gif) ### Image-based Markers @@ -232,49 +406,34 @@ Markers can be customized by just using images, and specified using the `image` ![](http://i.imgur.com/mzrOjTR.png) - - ### Draggable Markers Markers are draggable, and emit continuous drag events to update other UI during drags. ![](http://i.giphy.com/l2JImnZxdv1WbpQfC.gif) ![](http://i.giphy.com/l2JIhv4Jx6Ugx1EGI.gif) +### Lite Mode ( Android ) -## Component API - -[`` Component API](docs/mapview.md) +Enable lite mode on Android with `liteMode` prop. Ideal when having multiple maps in a View or ScrollView. -[`` Component API](docs/marker.md) +![](http://i.giphy.com/qZ2lAf18s89na.gif) -[`` Component API](docs/callout.md) +### On Poi Click (Google Maps Only) -[`` Component API](docs/polygon.md) +Poi are clickable, you can catch the event to get its information (usually to get the full detail from Google Place using the placeId). -[`` Component API](docs/polyline.md) - -[`` Component API](docs/circle.md) - - - -## Using with the Animated API - -The API of this Map has been built with the intention of it being able to utilize the [Animated API](https://facebook.github.io/react-native/docs/animated.html). - -In order to get this to work, you will need to modify the `AnimatedImplementation.js` file in the -source of react-native with [this one](https://gist.github.com/lelandrichardson/c0d938e02301f9294465). - -Ideally this will be possible in the near future without this modification. +![](https://media.giphy.com/media/3480VsCKnHr31uCLU3/giphy.gif) ### Animated Region -The MapView can accept an `Animated.Region` value as its `region` prop. This allows you to utilize -the Animated API to control the map's center and zoom. +The MapView can accept an `AnimatedRegion` value as its `region` prop. This allows you to utilize the Animated API to control the map's center and zoom. ```jsx +import MapView, { AnimatedRegion, Animated } from 'react-native-maps'; + getInitialState() { return { - region: new Animated.Region({ + region: new AnimatedRegion({ latitude: LATITUDE, longitude: LONGITUDE, latitudeDelta: LATITUDE_DELTA, @@ -289,7 +448,7 @@ onRegionChange(region) { render() { return ( - @@ -299,31 +458,58 @@ render() { ### Animated Marker Position -Markers can also accept an `Animated.Region` value as a coordinate. +Markers can also accept an `AnimatedRegion` value as a coordinate. ```jsx +import MapView, { AnimatedRegion, MarkerAnimated } from 'react-native-maps'; + getInitialState() { return { - coordinate: new Animated.Region({ + coordinate: new AnimatedRegion({ latitude: LATITUDE, longitude: LONGITUDE, }), }; } +componentWillReceiveProps(nextProps) { + const duration = 500 + + if (this.props.coordinate !== nextProps.coordinate) { + if (Platform.OS === 'android') { + if (this.marker) { + this.marker.animateMarkerToCoordinate( + nextProps.coordinate, + duration + ); + } + } else { + this.state.coordinate.timing({ + ...nextProps.coordinate, + useNativeDriver: true, // defaults to false if not passed explicitly + duration + }).start(); + } + } +} + render() { return ( - + { this.marker = marker }} + coordinate={this.state.coordinate} + /> ); } ``` ### Take Snapshot of map -currently only for ios, android implementation WIP ```jsx +import MapView, { Marker } from 'react-native-maps'; + getInitialState() { return { coordinate: { @@ -334,18 +520,26 @@ getInitialState() { } takeSnapshot () { - // arguments to 'takeSnapshot' are width, height, coordinates and callback - this.refs.map.takeSnapshot(300, 300, this.state.coordinate, (err, snapshot) => { - // snapshot contains image 'uri' - full path to image and 'data' - base64 encoded image - this.setState({ mapSnapshot: snapshot }) - }) + // 'takeSnapshot' takes a config object with the + // following options + 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 + format: 'png', // image formats: 'png', 'jpg' (default: 'png') + quality: 0.8, // image quality: 0..1 (only relevant for jpg, default: 1) + result: 'file' // result types: 'file', 'base64' (default: 'file') + }); + snapshot.then((uri) => { + this.setState({ mapSnapshot: uri }); + }); } render() { return ( - - + { this.map = map }}> + @@ -362,22 +556,25 @@ Pass an array of marker identifiers to have the map re-focus. ![](http://i.giphy.com/3o7qEbOQnO0yoXqKJ2.gif) ![](http://i.giphy.com/l41YdrQZ7m6Dz4h0c.gif) +### Zoom to Specified Coordinates + +Pass an array of coordinates to focus a map region on said coordinates. + +![](https://cloud.githubusercontent.com/assets/1627824/18609960/da5d9e06-7cdc-11e6-811e-34e255093df9.gif) + ### Troubleshooting #### My map is blank -* Make sure that you have [properly installed](docs/installation.md) react-native-maps. -* Check in the logs if there is more informations about the issue. -* Try setting the style of the MapView to an absolute position with top, left, right and bottom values set. +- Make sure that you have [properly installed](docs/installation.md) react-native-maps. +- Check in the logs if there is more informations about the issue. +- Try setting the style of the MapView to an absolute position with top, left, right and bottom values set. +- Make sure you have enabled Google Maps API in [Google developer console](https://console.developers.google.com/apis/library) ```javascript const styles = StyleSheet.create({ map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, + ...StyleSheet.absoluteFillObject, }, }); ``` @@ -391,14 +588,14 @@ const styles = StyleSheet.create({ #### Inputs don't focus -* When inputs don't focus or elements don't respond to tap, look at the order of the view hierarchy, sometimes the issue could be due to ordering of rendered components, prefer putting MapView as the first component. +- When inputs don't focus or elements don't respond to tap, look at the order of the view hierarchy, sometimes the issue could be due to ordering of rendered components, prefer putting MapView as the first component. Bad: ```jsx - - + + ``` @@ -406,22 +603,70 @@ Good: ```jsx - - + + ``` +#### Children Components Not Re-Rendering + +Components that aren't declared by this library (Ex: Markers, Polyline) must not be children of the MapView component due to MapView's unique rendering methodology. Have your custom components / views outside the MapView component and position absolute to ensure they only re-render as needed. +Example: +Bad: + +```jsx + + + + + +``` + +Good: + +```jsx + + + + +``` + +Source: https://github.com/react-native-maps/react-native-maps/issues/1901 + +#### Crashing with EXC_BAD_ACCESS on iOS when switching apps + +`` using Apple Maps in `mapType: "standard"` will sometimes crash when you background the app or switch into another app. This is only an issue in XCode using Metal API Validation, and won't happen in production. To eliminate this problem even while debugging in XCode, go to `Edit Scheme... -> Run (Debug) -> Diagnostics` and uncheck `Metal -> API Validation`. (h/t [@Simon-TechForm](https://github.com/Simon-TechForm)). + +Source: https://github.com/react-native-maps/react-native-maps/issues/3957#issuecomment-924161121 + +#### onRegionChangeComplete() callback is called infinitely + +If changing the state in `onRegionChangeComplete` is called infinitely, add a condition to limit these calls to occur only when the region change was done as a result of a user's action. + +```javascript +onRegionChangeComplete={ (region, gesture) => { + // This fix only works on Google Maps because isGesture is NOT available on Apple Maps + if (!gesture.isGesture) { + return; + } + + // You can use + dispatch({ type: "map_region", payload: { mapRegion: region }}); // if using useReducer + // setMapRegionState(region); // if using useState +}} +``` + +Source: https://github.com/react-native-maps/react-native-maps/issues/846#issuecomment-1210079461 -License --------- +## License - Copyright (c) 2015 Leland Richardson + 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. You may obtain a copy of the License at - https://raw.githubusercontent.com/lelandrichardson/react-native-maps/master/LICENSE + https://raw.githubusercontent.com/airbnb/react-native-maps/master/LICENSE Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..98d22a31c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +**Please do not report security vulnerabilities through any public method** Instead, please make use of private vulnerability reporting (you can learn how to do so [here](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability)). diff --git a/__tests__/AnimatedRegion.test.js b/__tests__/AnimatedRegion.test.js new file mode 100644 index 000000000..c0a7d913b --- /dev/null +++ b/__tests__/AnimatedRegion.test.js @@ -0,0 +1,62 @@ +import {Animated} from 'react-native'; +import AnimatedRegion from '../src/AnimatedRegion'; + +const VALUES = { + latitude: 5, + longitude: 5, + latitudeDelta: 0, + longitudeDelta: 0, +}; + +describe('AnimatedRegion', () => { + it('converts numbers to instances of Animated.Value', () => { + const animatedRegion = new AnimatedRegion(VALUES); + + expect(animatedRegion.latitude instanceof Animated.Value).toBe(true); + expect(animatedRegion.longitude instanceof Animated.Value).toBe(true); + expect(animatedRegion.latitudeDelta instanceof Animated.Value).toBe(true); + expect(animatedRegion.longitudeDelta instanceof Animated.Value).toBe(true); + + const values = animatedRegion.__getValue(); + + expect(values.latitude).toEqual(VALUES.latitude); + expect(values.longitude).toEqual(VALUES.longitude); + expect(values.longitudeDelta).toEqual(VALUES.longitudeDelta); + expect(values.latitudeDelta).toEqual(VALUES.latitudeDelta); + }); + + it('uses Animated.Value instances', () => { + const animatedRegion = new AnimatedRegion({ + latitude: new Animated.Value(VALUES.latitude), + longitude: new Animated.Value(VALUES.longitude), + }); + + expect(animatedRegion.latitude instanceof Animated.Value).toBe(true); + expect(animatedRegion.longitude instanceof Animated.Value).toBe(true); + expect(animatedRegion.latitudeDelta instanceof Animated.Value).toBe(true); + expect(animatedRegion.longitudeDelta instanceof Animated.Value).toBe(true); + + const values = animatedRegion.__getValue(); + + expect(values.latitude).toEqual(VALUES.latitude); + expect(values.longitude).toEqual(VALUES.longitude); + expect(values.longitudeDelta).toEqual(VALUES.longitudeDelta); + expect(values.latitudeDelta).toEqual(VALUES.latitudeDelta); + }); + + it('uses defaults converted to Animated.Value instances when none are supplied', () => { + const animatedRegion = new AnimatedRegion({}); + + expect(animatedRegion.latitude instanceof Animated.Value).toBe(true); + expect(animatedRegion.longitude instanceof Animated.Value).toBe(true); + expect(animatedRegion.latitudeDelta instanceof Animated.Value).toBe(true); + expect(animatedRegion.longitudeDelta instanceof Animated.Value).toBe(true); + + const values = animatedRegion.__getValue(); + + expect(values.latitude).toEqual(0); + expect(values.longitude).toEqual(0); + expect(values.longitudeDelta).toEqual(VALUES.longitudeDelta); + expect(values.latitudeDelta).toEqual(VALUES.latitudeDelta); + }); +}); diff --git a/android/build.gradle b/android/build.gradle index bfa064b43..067a91368 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,17 +1,70 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. +def safeExtGet(prop, fallback) { + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback +} buildscript { - repositories { - jcenter() + // The Android Gradle plugin is only required when opening the android folder stand-alone. + // This avoids unnecessary downloads and potential conflicts when the library is included as a + // module dependency in an application project. + if (project == rootProject) { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.6.3") + } } - dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' +} + +apply plugin: 'com.android.library' + +def isNewArchitectureEnabled() { + return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" +} + +android { + namespace "com.rnmaps.maps" + def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION + if (agpVersion.tokenize('.')[0].toInteger() >= 7) { + namespace "com.rnmaps.maps" + } + + compileSdk safeExtGet('compileSdkVersion', 34) + + defaultConfig { + minSdkVersion safeExtGet('minSdkVersion', 21) + targetSdkVersion safeExtGet('targetSdkVersion', 34) + + buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() + } + packagingOptions { + excludes = [ + "META-INF", + "META-INF/**", + ] } } -allprojects { - repositories { - jcenter() - maven { url "$projectDir/../../node_modules/react-native/android" } +repositories { + mavenLocal() + mavenCentral() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } + maven { + // Android JSC is installed from npm + url "$rootDir/../node_modules/jsc-android/dist" } -} \ No newline at end of file + google() +} + +dependencies { + implementation 'com.facebook.react:react-native:+' + implementation "com.google.android.gms:play-services-base:${safeExtGet('playServicesVersion', '18.2.0')}" + implementation "com.google.android.gms:play-services-maps:${safeExtGet('playServicesVersion', '18.2.0')}" + implementation "com.google.android.gms:play-services-location:21.0.1" + implementation 'com.google.maps.android:android-maps-utils:3.8.2' + implementation "androidx.work:work-runtime:2.7.1" +} diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index 5533295e2..000000000 --- a/android/gradle.properties +++ /dev/null @@ -1,15 +0,0 @@ -## Project-wide Gradle settings. -# -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html -# -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -# -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true -#Thu May 12 17:26:12 PDT 2016 diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar index 13372aef5..7454180f2 100644 Binary files a/android/gradle/wrapper/gradle-wrapper.jar and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index ab100c03b..18330fcba 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Mon Dec 28 10:00:20 PST 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip diff --git a/android/gradlew b/android/gradlew index 9d82f7891..f5feea6d6 100755 --- a/android/gradlew +++ b/android/gradlew @@ -1,74 +1,130 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -77,84 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat index aec99730b..9b42019c7 100644 --- a/android/gradlew.bat +++ b/android/gradlew.bat @@ -1,4 +1,22 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -8,26 +26,30 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -35,54 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/android/lib/build.gradle b/android/lib/build.gradle deleted file mode 100644 index 1a4908efa..000000000 --- a/android/lib/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -apply plugin: 'com.android.library' -apply from: 'gradle-maven-push.gradle' - -android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" - - defaultConfig { - minSdkVersion 16 - targetSdkVersion 23 - } - - lintOptions { - disable 'InvalidPackage' - } -} - -dependencies { - compile 'com.facebook.react:react-native:+' - compile "com.google.android.gms:play-services-base:8.4.0" - compile 'com.google.android.gms:play-services-maps:8.4.0' -} diff --git a/android/lib/gradle-maven-push.gradle b/android/lib/gradle-maven-push.gradle deleted file mode 100644 index 84bf6163c..000000000 --- a/android/lib/gradle-maven-push.gradle +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2013 Chris Banes - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin: 'maven' -apply plugin: 'signing' - -def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL - : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" -} - -def getSnapshotRepositoryUrl() { - return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL - : "https://oss.sonatype.org/content/repositories/snapshots/" -} - -def getRepositoryUsername() { - return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" -} - -def getRepositoryPassword() { - return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" -} - -afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - snapshotRepository(url: getSnapshotRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description POM_DESCRIPTION - url POM_URL - - scm { - url POM_SCM_URL - connection POM_SCM_CONNECTION - developerConnection POM_SCM_DEV_CONNECTION - } - - licenses { - license { - name POM_LICENSE_NAME - url POM_LICENSE_URL - distribution POM_LICENSE_DIST - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - } - } - } - } - } - } - - signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives - } - - task androidJavadocs(type: Javadoc) { - source = android.sourceSets.main.java.srcDirs - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - } - - task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - classifier = 'javadoc' - from androidJavadocs.destinationDir - } - - task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.sourceFiles - } - - artifacts { - archives androidSourcesJar - archives androidJavadocsJar - } -} \ No newline at end of file diff --git a/android/lib/gradle.properties b/android/lib/gradle.properties deleted file mode 100644 index c86911511..000000000 --- a/android/lib/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -VERSION_CODE=2 -VERSION_NAME=0.7.1 -GROUP=com.airbnb.android - -POM_DESCRIPTION=React Native Map view component for Android -POM_URL=https://github.com/lelandrichardson/react-native-maps/tree/new-scv -POM_SCM_URL=https://github.com/lelandrichardson/react-native-maps/tree/new-scv -POM_SCM_CONNECTION=scm:git@github.com:lelandrichardson/react-native-maps.git -POM_SCM_DEV_CONNECTION=scm:git@github.com:lelandrichardson/react-native-maps.git -POM_LICENSE_NAME=MIT -POM_LICENSE_URL=https://github.com/lelandrichardson/react-native-maps/blob/master/LICENSE -POM_LICENSE_DIST=repo -POM_DEVELOPER_ID=lelandrichardson -POM_DEVELOPER_NAME=Leland Richardson - -POM_NAME=ReactNative Maps library -POM_ARTIFACT_ID=react-native-maps -POM_PACKAGING=aar diff --git a/android/lib/src/main/AndroidManifest.xml b/android/lib/src/main/AndroidManifest.xml deleted file mode 100644 index e07e0af89..000000000 --- a/android/lib/src/main/AndroidManifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java deleted file mode 100644 index c434c9c12..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCallout.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; - -import com.facebook.react.views.view.ReactViewGroup; - -public class AirMapCallout extends ReactViewGroup { - private boolean tooltip = false; - public int width; - public int height; - - public AirMapCallout(Context context) { - super(context); - } - - public void setTooltip(boolean tooltip) { - this.tooltip = tooltip; - } - - public boolean getTooltip() { - return this.tooltip; - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java deleted file mode 100644 index 5c21fadd3..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCalloutManager.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.airbnb.android.react.maps; - -import com.facebook.react.common.MapBuilder; -import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; - -import java.util.Map; - -import javax.annotation.Nullable; - -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 - public - @Nullable - 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/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java deleted file mode 100644 index e428b04f6..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCircle.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; - -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.model.Circle; -import com.google.android.gms.maps.model.CircleOptions; -import com.google.android.gms.maps.model.LatLng; - -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); - } - - 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 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 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 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/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java deleted file mode 100644 index c0eaf8f14..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapCircleManager.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; -import android.graphics.Color; -import android.os.Build; -import android.util.DisplayMetrics; -import android.view.WindowManager; - -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.google.android.gms.maps.model.LatLng; - -public class AirMapCircleManager extends ViewGroupManager { - 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(); - } - } - - @Override - public String getName() { - return "AIRMapCircle"; - } - - @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 = "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 = "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 = "zIndex", defaultFloat = 1.0f) - public void setZIndex(AirMapCircle view, float zIndex) { - view.setZIndex(zIndex); - } - -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java deleted file mode 100644 index 1c15ade5f..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapFeature.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; - -import com.facebook.react.views.view.ReactViewGroup; -import com.google.android.gms.maps.GoogleMap; - -public abstract class AirMapFeature extends ReactViewGroup { - public AirMapFeature(Context context) { - super(context); - } - - public abstract void addToMap(GoogleMap map); - - public abstract void removeFromMap(GoogleMap map); - - public abstract Object getFeature(); -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapManager.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapManager.java deleted file mode 100644 index 5ca02097b..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapManager.java +++ /dev/null @@ -1,290 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.view.View; -import android.content.Context; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReactContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.modules.core.DeviceEventManagerModule; -import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.facebook.react.uimanager.events.RCTEventEmitter; -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.MapsInitializer; -import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.LatLngBounds; - -import java.util.Map; - -import javax.annotation.Nullable; - -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 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 - ); - - private ReactContext reactContext; - - private final Context appContext; - - public AirMapManager(Context context) { - this.appContext = context; - } - - @Override - public String getName() { - return REACT_CLASS; - } - - @Override - protected AirMapView createViewInstance(ThemedReactContext context) { - reactContext = context; - - try { - MapsInitializer.initialize(this.appContext); - } catch (RuntimeException e) { - e.printStackTrace(); - emitMapError("Map initialize error", "map_init_error"); - } - - return new AirMapView(context, this.appContext, this); - } - - @Override - public void onDropViewInstance(AirMapView view) { - view.doDestroy(); - super.onDropViewInstance(view); - } - - private void emitMapError(String message, String type) { - WritableMap error = Arguments.createMap(); - error.putString("message", message); - error.putString("type", type); - - reactContext - .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 = "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 = "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="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; - } - } - - @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 - ); - } - - @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(View view, String name, WritableMap data) { - reactContext.getJSModule(RCTEventEmitter.class) - .receiveEvent(view.getId(), name, data); - } - -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java deleted file mode 100644 index 8623812c2..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapMarker.java +++ /dev/null @@ -1,410 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.Animatable; -import android.net.Uri; -import android.view.View; -import android.widget.LinearLayout; - -import com.facebook.common.references.CloseableReference; -import com.facebook.datasource.DataSource; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.controller.BaseControllerListener; -import com.facebook.drawee.controller.ControllerListener; -import com.facebook.drawee.drawable.ScalingUtils; -import com.facebook.drawee.generic.GenericDraweeHierarchy; -import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; -import com.facebook.drawee.interfaces.DraweeController; -import com.facebook.drawee.view.DraweeHolder; -import com.facebook.imagepipeline.core.ImagePipeline; -import com.facebook.imagepipeline.image.CloseableImage; -import com.facebook.imagepipeline.image.CloseableStaticBitmap; -import com.facebook.imagepipeline.image.ImageInfo; -import com.facebook.imagepipeline.request.ImageRequest; -import com.facebook.imagepipeline.request.ImageRequestBuilder; -import com.facebook.react.bridge.ReadableMap; -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.model.BitmapDescriptor; -import com.google.android.gms.maps.model.BitmapDescriptorFactory; -import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.Marker; -import com.google.android.gms.maps.model.MarkerOptions; - -import javax.annotation.Nullable; - -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 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(); - } - }; - - 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 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.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; - } - - 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 - )); - - LL.addView(LL2); - LL2.addView(this.calloutView); - - this.wrappedCalloutView = LL; - } - - private int getDrawableResourceByName(String name) { - return getResources().getIdentifier( - name, - "drawable", - getContext().getPackageName()); - } - - private BitmapDescriptor getBitmapDescriptorByName(String name) { - return BitmapDescriptorFactory.fromResource(getDrawableResourceByName(name)); - } - -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java deleted file mode 100644 index 52a0e6fc7..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapMarkerManager.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.graphics.Color; -import android.view.View; - -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.common.MapBuilder; -import com.facebook.react.uimanager.LayoutShadowNode; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; -import com.google.android.gms.maps.model.Marker; - -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.Nullable; - -public class AirMapMarkerManager extends ViewGroupManager { - - public static final int SHOW_INFO_WINDOW = 1; - public 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) { -// -// } -// -// @ReactProp(name = "calloutOffset") -// public void setCalloutOffset(AirMapMarker view, ReadableMap map) { -// -// } - - @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 - 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/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java deleted file mode 100644 index 166172795..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolygon.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; - -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.Polygon; -import com.google.android.gms.maps.model.PolygonOptions; - -import java.util.ArrayList; -import java.util.List; - -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); - } - - 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); - } - } - - public void setZIndex(float zIndex) { - this.zIndex = zIndex; - if (polygon != null) { - polygon.setZIndex(zIndex); - } - } - - 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()); - } - - @Override - public void removeFromMap(GoogleMap map) { - polygon.remove(); - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java deleted file mode 100644 index f08ce0289..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolygonManager.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; -import android.graphics.Color; -import android.os.Build; -import android.util.DisplayMetrics; -import android.view.WindowManager; - -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; - -public class AirMapPolygonManager extends ViewGroupManager { - 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(); - } - } - - @Override - public String getName() { - return "AIRMapPolygon"; - } - - @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 = "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 = "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 = "zIndex", defaultFloat = 1.0f) - public void setZIndex(AirMapPolygon view, float zIndex) { - view.setZIndex(zIndex); - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java deleted file mode 100644 index cf15dfadf..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolyline.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; - -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.Polyline; -import com.google.android.gms.maps.model.PolylineOptions; - -import java.util.ArrayList; -import java.util.List; - -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); - } - } - - 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); - } - } - - 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()); - } - - @Override - public void removeFromMap(GoogleMap map) { - polyline.remove(); - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java deleted file mode 100644 index 89edc861e..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapPolylineManager.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.Context; -import android.graphics.Color; -import android.os.Build; -import android.util.DisplayMetrics; -import android.view.WindowManager; - -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.ViewGroupManager; -import com.facebook.react.uimanager.annotations.ReactProp; - -public class AirMapPolylineManager extends ViewGroupManager { - 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(); - } - } - - @Override - public String getName() { - return "AIRMapPolyline"; - } - - @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 = "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 = "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); - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapView.java b/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapView.java deleted file mode 100644 index b817f804f..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/AirMapView.java +++ /dev/null @@ -1,744 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.content.pm.PackageManager; -import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Point; -import android.graphics.PorterDuff; -import android.os.Handler; -import android.os.Build; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.MotionEventCompat; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.View; -import android.view.View.OnLayoutChangeListener; -import android.content.Context; - -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; -import android.widget.TextView; -import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.uimanager.UIManagerModule; -import com.facebook.react.uimanager.events.EventDispatcher; -import com.google.android.gms.maps.CameraUpdate; -import com.google.android.gms.maps.CameraUpdateFactory; -import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.MapView; -import com.google.android.gms.maps.OnMapReadyCallback; -import com.google.android.gms.maps.Projection; -import com.google.android.gms.maps.model.CameraPosition; -import com.google.android.gms.maps.model.Circle; -import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.LatLngBounds; -import com.google.android.gms.maps.model.Marker; -import com.google.android.gms.maps.model.Polygon; -import com.google.android.gms.maps.model.Polyline; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -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 LatLngBounds boundsToMove; - private boolean showUserLocation = false; - private boolean isMonitoringRegion = false; - private boolean isTouchDown = false; - private boolean handlePanDrag = false; - private boolean cacheEnabled = false; - private boolean loadingEnabled = 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 Map circleMap = new HashMap<>(); - - private final ScaleGestureDetector scaleDetector; - private final GestureDetectorCompat gestureDetector; - private final AirMapManager manager; - private LifecycleEventListener lifecycleListener; - private OnLayoutChangeListener onLayoutChangeListener; - private boolean paused = false; - private ThemedReactContext context; - - final EventDispatcher eventDispatcher; - - public AirMapView(ThemedReactContext context, Context appContext, AirMapManager manager) { - super(appContext); - this.manager = manager; - this.context = context; - - super.onCreate(null); - super.onResume(); - super.getMapAsync(this); - - final AirMapView view = this; - scaleDetector = - new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { -// @Override -// public boolean onScale(ScaleGestureDetector detector) { -// Log.d("AirMapView", "onScale"); -// return false; -// } - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - view.startMonitoringRegion(); - return true; // stop recording this gesture. let mapview handle it. - } - }); - - gestureDetector = - new GestureDetectorCompat(context, 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; - } - }); - - onLayoutChangeListener = 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 (!AirMapView.this.paused) { - AirMapView.this.cacheView(); - } - } - }; - this.addOnLayoutChangeListener(this.onLayoutChangeListener); - - eventDispatcher = context.getNativeModule(UIManagerModule.class).getEventDispatcher(); - } - - @Override - public void onMapReady(final GoogleMap map) { - this.map = map; - this.map.setInfoWindowAdapter(this); - this.map.setOnMarkerDragListener(this); - - manager.pushEvent(this, "onMapReady", new WritableNativeMap()); - - final AirMapView view = this; - - map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() { - @Override - public boolean onMarkerClick(Marker marker) { - WritableMap event; - - event = makeClickEventData(marker.getPosition()); - event.putString("action", "marker-press"); - manager.pushEvent(view, "onMarkerPress", event); - - event = makeClickEventData(marker.getPosition()); - event.putString("action", "marker-press"); - manager.pushEvent(markerMap.get(marker), "onPress", event); - - return false; // returning false opens the callout window, if possible - } - }); - - map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { - @Override - public void onInfoWindowClick(Marker marker) { - WritableMap event; - - event = makeClickEventData(marker.getPosition()); - event.putString("action", "callout-press"); - manager.pushEvent(view, "onCalloutPress", event); - - event = makeClickEventData(marker.getPosition()); - event.putString("action", "callout-press"); - AirMapMarker markerView = markerMap.get(marker); - manager.pushEvent(markerView, "onCalloutPress", event); - - event = makeClickEventData(marker.getPosition()); - event.putString("action", "callout-press"); - AirMapCallout infoWindow = markerView.getCalloutView(); - if (infoWindow != null) manager.pushEvent(infoWindow, "onPress", event); - } - }); - - map.setOnMapClickListener(new GoogleMap.OnMapClickListener() { - @Override - public void onMapClick(LatLng point) { - WritableMap event = makeClickEventData(point); - event.putString("action", "press"); - manager.pushEvent(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(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); - } - 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 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 (lifecycleListener != null) { - context.removeLifecycleEventListener(lifecycleListener); - lifecycleListener = null; - } - if (!paused) { - onPause(); - } - 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 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); - Circle circle = (Circle) circleView.getFeature(); - circleMap.put(circle, circleView); - } else { - // TODO(lmr): throw? User shouldn't be adding non-feature children. - } - } - - 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()); - } else if (feature instanceof AirMapPolyline) { - polylineMap.remove(feature.getFeature()); - } else if (feature instanceof AirMapPolygon) { - polygonMap.remove(feature.getFeature()); - } else if (feature instanceof AirMapCircle) { - circleMap.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; - 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) { - startMonitoringRegion(); - map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null); - } - - public void animateToCoordinate(LatLng coordinate, int duration) { - startMonitoringRegion(); - map.animateCamera(CameraUpdateFactory.newLatLng(coordinate), duration, null); - } - - public void fitToElements(boolean animated) { - LatLngBounds.Builder builder = new LatLngBounds.Builder(); - for (AirMapFeature feature : features) { - if (feature instanceof AirMapMarker) { - Marker marker = (Marker) feature.getFeature(); - builder.include(marker.getPosition()); - } - // TODO(lmr): may want to include shapes / etc. - } - LatLngBounds bounds = builder.build(); - CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 50); - 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, 50); - if (animated) { - startMonitoringRegion(); - map.animateCamera(cu); - } else { - map.moveCamera(cu); - } - } - } - - // 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(true); - isTouchDown = true; - break; - case (MotionEvent.ACTION_MOVE): - startMonitoringRegion(); - break; - case (MotionEvent.ACTION_UP): - 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() { - - LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds; - if (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(this, "onMarkerDragStart", event); - - AirMapMarker markerView = markerMap.get(marker); - event = makeClickEventData(marker.getPosition()); - manager.pushEvent(markerView, "onDragStart", event); - } - - @Override - public void onMarkerDrag(Marker marker) { - WritableMap event = makeClickEventData(marker.getPosition()); - manager.pushEvent(this, "onMarkerDrag", event); - - AirMapMarker markerView = markerMap.get(marker); - event = makeClickEventData(marker.getPosition()); - manager.pushEvent(markerView, "onDrag", event); - } - - @Override - public void onMarkerDragEnd(Marker marker) { - WritableMap event = makeClickEventData(marker.getPosition()); - manager.pushEvent(this, "onMarkerDragEnd", event); - - AirMapMarker markerView = markerMap.get(marker); - event = makeClickEventData(marker.getPosition()); - manager.pushEvent(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.FILL_PARENT, ViewGroup.LayoutParams.FILL_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(this, "onPanDrag", event); - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java b/android/lib/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java deleted file mode 100644 index ed1058c88..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/LatLngBoundsUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.airbnb.android.react.maps; - -import com.google.android.gms.maps.model.LatLng; -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; - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/MapsPackage.java b/android/lib/src/main/java/com/airbnb/android/react/maps/MapsPackage.java deleted file mode 100644 index f352d924e..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/MapsPackage.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.airbnb.android.react.maps; - -import android.app.Activity; - -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public class MapsPackage implements ReactPackage { - public MapsPackage(Activity activity) { } // backwards compatability - public MapsPackage() { } - - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - return Collections.emptyList(); - } - - @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.getBaseContext()); - - return Arrays.asList( - calloutManager, - annotationManager, - polylineManager, - polygonManager, - circleManager, - mapManager); - } -} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java b/android/lib/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java deleted file mode 100644 index 4f3194160..000000000 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/RegionChangeEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.airbnb.android.react.maps; - -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.facebook.react.uimanager.events.Event; -import com.facebook.react.uimanager.events.RCTEventEmitter; -import com.google.android.gms.maps.model.LatLng; -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, System.currentTimeMillis()); - 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/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 2b49d90f6..000000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':lib' \ No newline at end of file diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..a2f47b605 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/android/src/main/java/com/rnmaps/maps/FileUtil.java b/android/src/main/java/com/rnmaps/maps/FileUtil.java new file mode 100644 index 000000000..db3b1d532 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/FileUtil.java @@ -0,0 +1,74 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.net.Uri; +import android.os.AsyncTask; + +import com.facebook.common.logging.FLog; +import com.facebook.react.common.ReactConstants; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; + +public class FileUtil extends AsyncTask { + + private Context context; + + public FileUtil(Context context) { + super(); + + this.context = context; + } + + protected InputStream doInBackground(String... urls) { + try { + Uri fileContentUri = Uri.parse(urls[0]); + + if (fileContentUri.getScheme().startsWith("http")) { + return getDownloadFileInputStream(context, fileContentUri); + } + return context.getContentResolver().openInputStream(fileContentUri); + } catch (Exception e) { + FLog.e( + ReactConstants.TAG, + "Could not retrieve file for contentUri " + urls[0], + e); + return null; + } + } + + private InputStream getDownloadFileInputStream(Context context, Uri uri) + throws IOException { + final File outputDir = context.getApplicationContext().getCacheDir(); + String NAME = "FileUtil"; + String TEMP_FILE_SUFFIX = "temp"; + final File file = File.createTempFile(NAME, TEMP_FILE_SUFFIX, outputDir); + file.deleteOnExit(); + + final URL url = new URL(uri.toString()); + final InputStream is = url.openStream(); + try { + final ReadableByteChannel channel = Channels.newChannel(is); + try { + final FileOutputStream stream = new FileOutputStream(file); + try { + stream.getChannel().transferFrom(channel, 0, Long.MAX_VALUE); + return new FileInputStream(file); + } finally { + stream.close(); + } + } finally { + channel.close(); + } + } finally { + is.close(); + } + } + +} diff --git a/android/src/main/java/com/rnmaps/maps/FusedLocationSource.java b/android/src/main/java/com/rnmaps/maps/FusedLocationSource.java new file mode 100644 index 000000000..ca65bd746 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/FusedLocationSource.java @@ -0,0 +1,75 @@ +package com.rnmaps.maps; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.location.Location; +import android.os.Looper; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.location.Priority; +import com.google.android.gms.maps.LocationSource; +import com.google.android.gms.tasks.OnSuccessListener; + +import java.lang.SecurityException; + +public class FusedLocationSource implements LocationSource { + + private final FusedLocationProviderClient fusedLocationClientProviderClient; + private final LocationRequest locationRequest; + private LocationCallback locationCallback; + + public FusedLocationSource(Context context){ + fusedLocationClientProviderClient = + LocationServices.getFusedLocationProviderClient(context); + locationRequest = LocationRequest.create(); + locationRequest.setPriority(Priority.PRIORITY_HIGH_ACCURACY); + locationRequest.setInterval(5000); + } + + public void setPriority(int priority){ + locationRequest.setPriority(priority); + } + + public void setInterval(int interval){ + locationRequest.setInterval(interval); + } + + public void setFastestInterval(int fastestInterval){ + locationRequest.setFastestInterval(fastestInterval); + } + + @SuppressLint("MissingPermission") + @Override + public void activate(final OnLocationChangedListener onLocationChangedListener) { + try { + fusedLocationClientProviderClient.getLastLocation().addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(Location location) { + if (location != null) { + onLocationChangedListener.onLocationChanged(location); + } + } + }); + locationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + for (Location location : locationResult.getLocations()) { + onLocationChangedListener.onLocationChanged(location); + } + } + }; + fusedLocationClientProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper()); + } catch (SecurityException e) { + e.printStackTrace(); + } + } + + @Override + public void deactivate() { + fusedLocationClientProviderClient.removeLocationUpdates(locationCallback); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/ImageReadable.java b/android/src/main/java/com/rnmaps/maps/ImageReadable.java new file mode 100644 index 000000000..5accd3f87 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/ImageReadable.java @@ -0,0 +1,15 @@ +package com.rnmaps.maps; + + +import android.graphics.Bitmap; + +import com.google.android.gms.maps.model.BitmapDescriptor; + +public interface ImageReadable { + + public void setIconBitmap(Bitmap bitmap); + + public void setIconBitmapDescriptor(BitmapDescriptor bitmapDescriptor); + + public void update(); +} diff --git a/android/src/main/java/com/rnmaps/maps/ImageReader.java b/android/src/main/java/com/rnmaps/maps/ImageReader.java new file mode 100644 index 000000000..1d532eaca --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/ImageReader.java @@ -0,0 +1,127 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.Animatable; +import android.net.Uri; + +import androidx.annotation.Nullable; + +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.drawee.controller.ControllerListener; +import com.facebook.drawee.drawable.ScalingUtils; +import com.facebook.drawee.generic.GenericDraweeHierarchy; +import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.DraweeHolder; +import com.facebook.imagepipeline.core.ImagePipeline; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.image.CloseableStaticBitmap; +import com.facebook.imagepipeline.image.ImageInfo; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; + +public class ImageReader { + + private final ImageReadable imp; + private final Context context; + private final Resources resources; + + 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 instanceof CloseableStaticBitmap) { + CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) image; + Bitmap bitmap = closeableStaticBitmap.getUnderlyingBitmap(); + if (bitmap != null) { + bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); + imp.setIconBitmap(bitmap); + imp.setIconBitmapDescriptor(BitmapDescriptorFactory.fromBitmap(bitmap)); + } + } + } + } finally { + dataSource.close(); + if (imageReference != null) { + CloseableReference.closeSafely(imageReference); + } + } + imp.update(); + } + }; + + public ImageReader(Context context, Resources resources, ImageReadable imp) { + this.context = context; + this.resources = resources; + this.imp = imp; + logoHolder = DraweeHolder.create(createDraweeHeirarchy(resources), context); + logoHolder.onAttach(); + } + + private GenericDraweeHierarchy createDraweeHeirarchy(Resources resources){ + return new GenericDraweeHierarchyBuilder(resources) + .setActualImageScaleType(ScalingUtils.ScaleType.FIT_CENTER) + .setFadeDuration(0) + .build(); + } + + public void setImage(String uri) { + if (uri == null) { + imp.setIconBitmapDescriptor(null); + imp.update(); + } else if (uri.startsWith("http://") || uri.startsWith("https://") || + uri.startsWith("file://") || uri.startsWith("asset://") || uri.startsWith("data:")) { + 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 { + BitmapDescriptor iconBitmapDescriptor = getBitmapDescriptorByName(uri); + imp.setIconBitmapDescriptor(iconBitmapDescriptor); + imp.setIconBitmap(BitmapFactory.decodeResource(this.resources, getDrawableResourceByName + (uri))); + imp.update(); + } + + + } + + private int getDrawableResourceByName(String name) { + return this.resources.getIdentifier( + name, + "drawable", + this.context.getPackageName()); + } + + private BitmapDescriptor getBitmapDescriptorByName(String name) { + return BitmapDescriptorFactory.fromResource(getDrawableResourceByName(name)); + } + +} diff --git a/android/src/main/java/com/rnmaps/maps/ImageUtil.java b/android/src/main/java/com/rnmaps/maps/ImageUtil.java new file mode 100644 index 000000000..8252652f8 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/ImageUtil.java @@ -0,0 +1,26 @@ +package com.rnmaps.maps; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Base64; + +import java.io.ByteArrayOutputStream; + +public class ImageUtil { + public static Bitmap convert(String base64Str) throws IllegalArgumentException { + byte[] decodedBytes = Base64.decode( + base64Str.substring(base64Str.indexOf(",") + 1), + Base64.DEFAULT + ); + + return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length); + } + + public static String convert(Bitmap bitmap) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + + return Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT); + } + +} \ No newline at end of file diff --git a/android/src/main/java/com/rnmaps/maps/LatLngBoundsUtils.java b/android/src/main/java/com/rnmaps/maps/LatLngBoundsUtils.java new file mode 100644 index 000000000..8b400df2a --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/LatLngBoundsUtils.java @@ -0,0 +1,47 @@ +package com.rnmaps.maps; + +import com.google.android.gms.maps.model.LatLng; +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; + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapCallout.java b/android/src/main/java/com/rnmaps/maps/MapCallout.java new file mode 100644 index 000000000..d19733620 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapCallout.java @@ -0,0 +1,23 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.facebook.react.views.view.ReactViewGroup; + +public class MapCallout extends ReactViewGroup { + private boolean tooltip = false; + public int width; + public int height; + + public MapCallout(Context context) { + super(context); + } + + public void setTooltip(boolean tooltip) { + this.tooltip = tooltip; + } + + public boolean getTooltip() { + return this.tooltip; + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapCalloutManager.java b/android/src/main/java/com/rnmaps/maps/MapCalloutManager.java new file mode 100644 index 000000000..d49714682 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapCalloutManager.java @@ -0,0 +1,56 @@ +package com.rnmaps.maps; + +import androidx.annotation.Nullable; + +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.LayoutShadowNode; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.Map; + +public class MapCalloutManager extends ViewGroupManager { + + @Override + public String getName() { + return "AIRMapCallout"; + } + + @Override + public MapCallout createViewInstance(ThemedReactContext context) { + return new MapCallout(context); + } + + @ReactProp(name = "tooltip", defaultBoolean = false) + public void setTooltip(MapCallout 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(MapCallout 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/android/src/main/java/com/rnmaps/maps/MapCircle.java b/android/src/main/java/com/rnmaps/maps/MapCircle.java new file mode 100644 index 000000000..e641b9843 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapCircle.java @@ -0,0 +1,102 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.google.android.gms.maps.model.Circle; +import com.google.android.gms.maps.model.CircleOptions; +import com.google.android.gms.maps.model.LatLng; +import com.google.maps.android.collections.CircleManager; + +public class MapCircle extends MapFeature { + + 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 MapCircle(Context context) { + super(context); + } + + 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 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 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 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(Object collection) { + CircleManager.Collection circleCollection = (CircleManager.Collection) collection; + circle = circleCollection.addCircle(getCircleOptions()); + } + + @Override + public void removeFromMap(Object collection) { + CircleManager.Collection circleCollection = (CircleManager.Collection) collection; + circleCollection.remove(circle); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapCircleManager.java b/android/src/main/java/com/rnmaps/maps/MapCircleManager.java new file mode 100644 index 000000000..8366b1cd5 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapCircleManager.java @@ -0,0 +1,67 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Color; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.google.android.gms.maps.model.LatLng; + +public class MapCircleManager extends ViewGroupManager { + private final DisplayMetrics metrics; + + public MapCircleManager(ReactApplicationContext reactContext) { + super(); + metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapCircle"; + } + + @Override + public MapCircle createViewInstance(ThemedReactContext context) { + return new MapCircle(context); + } + + @ReactProp(name = "center") + public void setCenter(MapCircle view, ReadableMap center) { + view.setCenter(new LatLng(center.getDouble("latitude"), center.getDouble("longitude"))); + } + + @ReactProp(name = "radius", defaultDouble = 0) + public void setRadius(MapCircle view, double radius) { + view.setRadius(radius); + } + + @ReactProp(name = "strokeWidth", defaultFloat = 1f) + public void setStrokeWidth(MapCircle 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(MapCircle view, int color) { + view.setFillColor(color); + } + + @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color") + public void setStrokeColor(MapCircle view, int color) { + view.setStrokeColor(color); + } + + @ReactProp(name = "zIndex", defaultFloat = 1.0f) + public void setZIndex(MapCircle view, float zIndex) { + view.setZIndex(zIndex); + } + +} diff --git a/android/src/main/java/com/rnmaps/maps/MapFeature.java b/android/src/main/java/com/rnmaps/maps/MapFeature.java new file mode 100644 index 000000000..876936ade --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapFeature.java @@ -0,0 +1,17 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.facebook.react.views.view.ReactViewGroup; + +public abstract class MapFeature extends ReactViewGroup { + public MapFeature(Context context) { + super(context); + } + + public abstract void addToMap(Object mapOrCollection); + + public abstract void removeFromMap(Object mapOrCollection); + + public abstract Object getFeature(); +} diff --git a/android/src/main/java/com/rnmaps/maps/MapGradientPolyline.java b/android/src/main/java/com/rnmaps/maps/MapGradientPolyline.java new file mode 100644 index 000000000..e3bed5bb7 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapGradientPolyline.java @@ -0,0 +1,343 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Shader; +import android.util.Log; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.Tile; +import com.google.android.gms.maps.model.TileOverlay; +import com.google.android.gms.maps.model.TileOverlayOptions; +import com.google.android.gms.maps.model.TileProvider; +import com.google.maps.android.SphericalUtil; +import com.google.maps.android.geometry.Point; +import com.google.maps.android.projection.SphericalMercatorProjection; + +import java.io.ByteArrayOutputStream; +import java.util.List; + + +/** + * Tile overlay used to display a colored polyline as a replacement for the + * non-existence of gradient polylines for google maps. Implementation borrowed + * from Dagothig/ColoredPolylineOverlay + * (https://gist.github.com/Dagothig/5f9cf0a4a7a42901a7b2) + */ +public class MapGradientPolyline extends MapFeature { + private List points; + private int[] colors; + private float zIndex; + private float width; + + private GoogleMap map; + + private TileOverlay tileOverlay; + protected final Context context; + + public MapGradientPolyline(Context context) { + super(context); + this.context = context; + } + + public void setCoordinates(List coordinates) { + this.points = coordinates; + if (tileOverlay != null) { + tileOverlay.remove(); + } + if (map != null) { + tileOverlay = map.addTileOverlay(createTileOverlayOptions()); + } + } + + public void setStrokeColors(int[] colors) { + this.colors = colors; + if (tileOverlay != null) { + tileOverlay.remove(); + } + if (map != null) { + tileOverlay = map.addTileOverlay(createTileOverlayOptions()); + } + } + + public void setZIndex(float zIndex) { + this.zIndex = zIndex; + if (tileOverlay != null) { + tileOverlay.setZIndex(zIndex); + } + } + + public void setWidth(float width) { + this.width = width; + if (tileOverlay != null) { + tileOverlay.remove(); + } + if (map != null) { + tileOverlay = map.addTileOverlay(createTileOverlayOptions()); + } + } + + private TileOverlayOptions createTileOverlayOptions() { + TileOverlayOptions options = new TileOverlayOptions(); + options.zIndex(zIndex); + AirMapGradientPolylineProvider tileProvider = new AirMapGradientPolylineProvider(context, points, colors, width); + options.tileProvider(tileProvider); + return options; + } + + public static int interpolateColor(int[] colors, float proportion) { + int rTotal = 0, gTotal = 0, bTotal = 0; + // We correct the ratio to colors.length - 1 so that + // for i == colors.length - 1 and p == 1, then the final ratio is 1 (see below) + float p = proportion * (colors.length - 1); + + for (int i = 0; i < colors.length; i++) { + // The ratio mostly resides on the 1 - Math.abs(p - i) calculation : + // Since for p == i, then the ratio is 1 and for p == i + 1 or p == i -1, then the ratio is 0 + // This calculation works BECAUSE p lies within [0, length - 1] and i lies within [0, length - 1] as well + float iRatio = Math.max(1 - Math.abs(p - i), 0.0f); + rTotal += (int) (Color.red(colors[i]) * iRatio); + gTotal += (int) (Color.green(colors[i]) * iRatio); + bTotal += (int) (Color.blue(colors[i]) * iRatio); + } + + return Color.rgb(rTotal, gTotal, bTotal); + } + + public class AirMapGradientPolylineProvider implements TileProvider { + + public static final int BASE_TILE_SIZE = 256; + + protected final List points; + protected final int[] colors; + protected final float width; + protected final float density; + protected final int tileDimension; + protected final SphericalMercatorProjection projection; + + // Caching calculation-related stuff + protected LatLng[] trailLatLngs; + protected Point[] projectedPts; + protected Point[] projectedPtMids; + + public AirMapGradientPolylineProvider(Context context, List points, int[] colors, + float width) { + super(); + + this.points = points; + this.colors = colors; + this.width = width; + density = context.getResources().getDisplayMetrics().density; + tileDimension = (int) (BASE_TILE_SIZE * density); + projection = new SphericalMercatorProjection(BASE_TILE_SIZE); + calculatePoints(); + } + + public void calculatePoints() { + trailLatLngs = new LatLng[points.size()]; + projectedPts = new Point[points.size()]; + projectedPtMids = new Point[Math.max(points.size() - 1, 0)]; + + for (int i = 0; i < points.size(); i++) { + LatLng latLng = points.get(i); + trailLatLngs[i] = latLng; + projectedPts[i] = projection.toPoint(latLng); + + // Mids + if (i > 0) { + LatLng previousLatLng = points.get(i - 1); + LatLng latLngMid = SphericalUtil.interpolate(previousLatLng, latLng, 0.5); + projectedPtMids[i - 1] = projection.toPoint(latLngMid); + } + } + } + + @Override + public Tile getTile(int x, int y, int zoom) { + // Because getTile can be called asynchronously by multiple threads, none of the info we keep in the class will be modified + // (getTile is essentially side-effect-less) : + // Instead, we create the bitmap, the canvas and the paints specifically for the call to getTile + + Bitmap bitmap = Bitmap.createBitmap(tileDimension, tileDimension, Bitmap.Config.ARGB_8888); + + // Normally, instead of the later calls for drawing being offset, we would offset them using scale() and translate() right here + // However, there seems to be funky issues related to float imprecisions that happen at large scales when using this method, so instead + // The points are offset properly when drawing + Canvas canvas = new Canvas(bitmap); + + Matrix shaderMat = new Matrix(); + Paint gradientPaint = new Paint(); + gradientPaint.setStyle(Paint.Style.STROKE); + gradientPaint.setStrokeWidth(width); + gradientPaint.setStrokeCap(Paint.Cap.BUTT); + gradientPaint.setStrokeJoin(Paint.Join.ROUND); + gradientPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + gradientPaint.setShader(new LinearGradient(0, 0, 1, 0, colors, null, + Shader.TileMode.CLAMP)); + gradientPaint.getShader().setLocalMatrix(shaderMat); + + Paint colorPaint = new Paint(); + colorPaint.setStyle(Paint.Style.STROKE); + colorPaint.setStrokeWidth(width); + colorPaint.setStrokeCap(Paint.Cap.BUTT); + colorPaint.setStrokeJoin(Paint.Join.ROUND); + colorPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + + // See https://developers.google.com/maps/documentation/android/views#zoom for handy info regarding what zoom is + float scale = (float) (Math.pow(2, zoom) * density); + + renderTrail(canvas, shaderMat, gradientPaint, colorPaint, scale, x, y); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); + return new Tile(tileDimension, tileDimension, baos.toByteArray()); + } + + public void renderTrail(Canvas canvas, Matrix shaderMat, Paint gradientPaint, Paint colorPaint, + float scale, int x, int y) { + MutPoint pt1 = new MutPoint(), pt2 = new MutPoint(), pt3 = new MutPoint(), pt1mid2 = + new MutPoint(), pt2mid3 = new MutPoint(); + + if (points.size() == 1) { + pt1.set(projectedPts[0], scale, x, y, tileDimension); + + colorPaint.setStyle(Paint.Style.FILL); + colorPaint.setColor(interpolateColor(colors, 1)); + canvas + .drawCircle((float) pt1.x, (float) pt1.y, colorPaint.getStrokeWidth() / 2f, colorPaint); + colorPaint.setStyle(Paint.Style.STROKE); + + return; + } + + + if (points.size() == 2) { + pt1.set(projectedPts[0], scale, x, y, tileDimension); + pt2.set(projectedPts[1], scale, x, y, tileDimension); + + drawLine(canvas, colorPaint, pt1, pt2, 0); + + return; + } + + for (int i = 2; i < points.size(); i++) { + pt1.set(projectedPts[i - 2], scale, x, y, tileDimension); + pt2.set(projectedPts[i - 1], scale, x, y, tileDimension); + pt3.set(projectedPts[i], scale, x, y, tileDimension); + + // Because we want to split the lines in two to ease over the corners, we need the middle points + pt1mid2.set(projectedPtMids[i - 2], scale, x, y, tileDimension); + pt2mid3.set(projectedPtMids[i - 1], scale, x, y, tileDimension); + + float interp1 = ((float)i - 2) / points.size(); + float interp2 = ((float)i - 1) / points.size(); + float interp1to2 = (interp1 + interp2) / 2; + + Log.d("AirMapGradientPolyline", String.valueOf(interp1to2)); + + // Circle for the corner (removes the weird empty corners that occur otherwise) + colorPaint.setStyle(Paint.Style.FILL); + colorPaint.setColor(interpolateColor(colors, interp1to2)); + canvas + .drawCircle((float) pt2.x, (float) pt2.y, colorPaint.getStrokeWidth() / 2f, colorPaint); + colorPaint.setStyle(Paint.Style.STROKE); + + // Corner + // Note that since for the very first point and the very last point we don't split it in two, we used them instead. + drawLine(canvas, shaderMat, gradientPaint, colorPaint, i - 2 == 0 ? pt1 : pt1mid2, + pt2, interp1, interp1to2); + drawLine(canvas, shaderMat, gradientPaint, colorPaint, pt2, i == points.size() - 1 ? + pt3 : pt2mid3, interp1to2, interp2); + } + } + + /** + * Note: it is assumed the shader is 0, 0, 1, 0 (horizontal) so that it lines up with the rotation + * (rotations are usually setup so that the angle 0 points right) + */ + public void drawLine(Canvas canvas, Matrix shaderMat, Paint gradientPaint, Paint colorPaint, + MutPoint pt1, MutPoint pt2, float ratio1, float ratio2) { + // Degenerate case: both ratios are the same; we just handle it using the colorPaint (handling it using the shader is just messy and ineffective) + if (ratio1 == ratio2) { + drawLine(canvas, colorPaint, pt1, pt2, ratio1); + return; + } + shaderMat.reset(); + + // PS: don't ask me why this specfic orders for calls works but other orders will fuck up + // Since every call is pre, this is essentially ordered as (or my understanding is that it is): + // ratio translate -> ratio scale -> scale to pt length -> translate to pt start -> rotate + // (my initial intuition was to use only post calls and to order as above, but it resulted in odd corruptions) + + // Setup based on points: + // We translate the shader so that it is based on the first point, rotated towards the second and since the length of the + // gradient is 1, then scaling to the length of the distance between the points makes it exactly as long as needed + shaderMat.preRotate((float) Math.toDegrees(Math.atan2(pt2.y - pt1.y, pt2.x - pt1.x)), + (float) pt1.x, (float) pt1.y); + shaderMat.preTranslate((float) pt1.x, (float) pt1.y); + float scale = (float) Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); + shaderMat.preScale(scale, scale); + + // Setup based on ratio + // By basing the shader to the first ratio, we ensure that the start of the gradient corresponds to it + // The inverse scaling of the shader means that it takes the full length of the call to go to the second ratio + // For instance; if d(ratio1, ratio2) is 0.5, then the shader needs to be twice as long so that an entire call (1) + // Results in only half of the gradient being used + shaderMat.preScale(1f / (ratio2 - ratio1), 1f / (ratio2 - ratio1)); + shaderMat.preTranslate(-ratio1, 0); + + gradientPaint.getShader().setLocalMatrix(shaderMat); + + canvas.drawLine( + (float) pt1.x, + (float) pt1.y, + (float) pt2.x, + (float) pt2.y, + gradientPaint + ); + } + + public void drawLine(Canvas canvas, Paint colorPaint, MutPoint pt1, MutPoint pt2, float ratio) { + colorPaint.setColor(interpolateColor(colors, ratio)); + canvas.drawLine( + (float) pt1.x, + (float) pt1.y, + (float) pt2.x, + (float) pt2.y, + colorPaint + ); + } + } + + @Override + public Object getFeature() { + return tileOverlay; + } + + @Override + public void addToMap(Object map) { + this.map = (GoogleMap) map; + this.tileOverlay = this.map.addTileOverlay(createTileOverlayOptions()); + } + + @Override + public void removeFromMap(Object map) { + tileOverlay.remove(); + } + + public static class MutPoint { + public double x, y; + + public MutPoint set(Point point, float scale, int x, int y, int tileDimension) { + this.x = point.x * scale - x * tileDimension; + this.y = point.y * scale - y * tileDimension; + return this; + } + } +} \ No newline at end of file diff --git a/android/src/main/java/com/rnmaps/maps/MapGradientPolylineManager.java b/android/src/main/java/com/rnmaps/maps/MapGradientPolylineManager.java new file mode 100644 index 000000000..a96c4454e --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapGradientPolylineManager.java @@ -0,0 +1,83 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.google.android.gms.maps.model.LatLng; + +import java.util.List; +import java.util.ArrayList; + +public class MapGradientPolylineManager extends ViewGroupManager { + private final DisplayMetrics metrics; + + public MapGradientPolylineManager(ReactApplicationContext reactContext) { + super(); + metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapGradientPolyline"; + } + + @Override + public MapGradientPolyline createViewInstance(ThemedReactContext context) { + return new MapGradientPolyline(context); + } + + @ReactProp(name = "coordinates") + public void setCoordinates(MapGradientPolyline view, ReadableArray coordinates) { + List p = new ArrayList(); + for (int i = 0; i < coordinates.size(); i++) { + ReadableMap point = coordinates.getMap(i); + LatLng latLng = new LatLng(point.getDouble("latitude"), point.getDouble("longitude")); + p.add(latLng); + } + view.setCoordinates(p); + } + + @ReactProp(name = "strokeColors", customType = "ColorArray") + public void setStrokeColors(MapGradientPolyline view, ReadableArray colors) { + if (colors != null) { + if (colors.size() == 0) { + int[] colorValues = {0,0}; + view.setStrokeColors(colorValues); + } else if (colors.size() == 1) { + int[] colorValues = { colors.getInt(0), colors.getInt(0) }; + view.setStrokeColors(colorValues); + } else { + int[] colorValues = new int[colors.size()]; + for (int i = 0; i < colors.size(); i++) { + colorValues[i] = colors.getInt(i); + } + view.setStrokeColors(colorValues); + } + } else { + int[] colorValues = {0,0}; + view.setStrokeColors(colorValues); + } + } + + @ReactProp(name = "zIndex", defaultFloat = 1.0f) + public void setZIndex(MapGradientPolyline view, float zIndex) { + view.setZIndex(zIndex); + } + + + @ReactProp(name = "strokeWidth", defaultFloat = 1f) + public void setStrokeWidth(MapGradientPolyline view, float widthInPoints) { + float widthInScreenPx = metrics.density * widthInPoints; // done for parity with iOS + view.setWidth(widthInScreenPx); + } +} \ No newline at end of file diff --git a/android/src/main/java/com/rnmaps/maps/MapHeatmap.java b/android/src/main/java/com/rnmaps/maps/MapHeatmap.java new file mode 100644 index 000000000..6b781b301 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapHeatmap.java @@ -0,0 +1,113 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.util.Log; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.TileOverlay; +import com.google.android.gms.maps.model.TileOverlayOptions; +import com.google.maps.android.heatmaps.HeatmapTileProvider; +import com.google.maps.android.heatmaps.WeightedLatLng; +import com.google.maps.android.heatmaps.Gradient; + +import java.util.Arrays; +import java.util.List; + +public class MapHeatmap extends MapFeature { + + private TileOverlayOptions heatmapOptions; + private TileOverlay heatmap; + private HeatmapTileProvider heatmapTileProvider; + + private List points; + private Gradient gradient; + private Double opacity; + private Integer radius; + + public MapHeatmap(Context context) { + super(context); + } + + public void setPoints(WeightedLatLng[] points) { + this.points = Arrays.asList(points); + if (heatmapTileProvider != null) { + heatmapTileProvider.setWeightedData(this.points); + } + if (heatmap != null) { + heatmap.clearTileCache(); + } + } + + public void setGradient(Gradient gradient) { + this.gradient = gradient; + if (heatmapTileProvider != null) { + heatmapTileProvider.setGradient(gradient); + } + if (heatmap != null) { + heatmap.clearTileCache(); + } + } + + public void setOpacity(double opacity) { + this.opacity = opacity; + if (heatmapTileProvider != null) { + heatmapTileProvider.setOpacity(opacity); + } + if (heatmap != null) { + heatmap.clearTileCache(); + } + } + + public void setRadius(int radius) { + this.radius = radius; + if (heatmapTileProvider != null) { + heatmapTileProvider.setRadius(radius); + } + if (heatmap != null) { + heatmap.clearTileCache(); + } + } + + public TileOverlayOptions getHeatmapOptions() { + if (heatmapOptions == null) { + heatmapOptions = createHeatmapOptions(); + } + return heatmapOptions; + } + + private TileOverlayOptions createHeatmapOptions() { + TileOverlayOptions options = new TileOverlayOptions(); + if (heatmapTileProvider == null) { + HeatmapTileProvider.Builder builder = + new HeatmapTileProvider.Builder().weightedData(this.points); + if (radius != null) { + builder.radius(radius); + } + if (opacity != null) { + builder.opacity(opacity); + } + if (gradient != null) { + builder.gradient(gradient); + } + heatmapTileProvider = builder.build(); + } + options.tileProvider(heatmapTileProvider); + return options; + } + + @Override + public Object getFeature() { + return heatmap; + } + + @Override + public void addToMap(Object map) { + heatmap = ((GoogleMap) map).addTileOverlay(getHeatmapOptions()); + } + + @Override + public void removeFromMap(Object map) { + heatmap.remove(); + } + +} \ No newline at end of file diff --git a/android/src/main/java/com/rnmaps/maps/MapHeatmapManager.java b/android/src/main/java/com/rnmaps/maps/MapHeatmapManager.java new file mode 100644 index 000000000..ff6ce7157 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapHeatmapManager.java @@ -0,0 +1,74 @@ +package com.rnmaps.maps; + +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; + +import com.google.android.gms.maps.model.LatLng; +import com.google.maps.android.heatmaps.WeightedLatLng; +import com.google.maps.android.heatmaps.Gradient; + + +public class MapHeatmapManager extends ViewGroupManager { + + @Override + public String getName() { + return "AIRMapHeatmap"; + } + + @Override + public MapHeatmap createViewInstance(ThemedReactContext context) { + return new MapHeatmap(context); + } + + @ReactProp(name = "points") + public void setPoints(MapHeatmap view, ReadableArray points) { + WeightedLatLng[] p = new WeightedLatLng[points.size()]; + for (int i = 0; i < points.size(); i++) { + ReadableMap point = points.getMap(i); + WeightedLatLng weightedLatLng; + LatLng latLng = new LatLng(point.getDouble("latitude"), point.getDouble("longitude")); + if (point.hasKey("weight")) { + weightedLatLng = new WeightedLatLng(latLng, point.getDouble("weight")); + } else { + weightedLatLng = new WeightedLatLng(latLng); + } + p[i] = weightedLatLng; + } + view.setPoints(p); + } + + @ReactProp(name = "gradient") + public void setGradient(MapHeatmap view, ReadableMap gradient) { + ReadableArray srcColors = gradient.getArray("colors"); + int[] colors = new int[srcColors.size()]; + for (int i = 0; i < srcColors.size(); i++) { + colors[i] = srcColors.getInt(i); + } + + ReadableArray srcStartPoints = gradient.getArray("startPoints"); + float[] startPoints = new float[srcStartPoints.size()]; + for (int i = 0; i < srcStartPoints.size(); i++) { + startPoints[i] = (float)srcStartPoints.getDouble(i); + } + + if (gradient.hasKey("colorMapSize")) { + int colorMapSize = gradient.getInt("colorMapSize"); + view.setGradient(new Gradient(colors, startPoints, colorMapSize)); + } else { + view.setGradient(new Gradient(colors, startPoints)); + } + } + + @ReactProp(name = "opacity") + public void setOpacity(MapHeatmap view, double opacity) { + view.setOpacity(opacity); + } + + @ReactProp(name = "radius") + public void setRadius(MapHeatmap view, int radius) { + view.setRadius(radius); + } +} \ No newline at end of file diff --git a/android/src/main/java/com/rnmaps/maps/MapLocalTile.java b/android/src/main/java/com/rnmaps/maps/MapLocalTile.java new file mode 100644 index 000000000..fe9b4cce3 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapLocalTile.java @@ -0,0 +1,151 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.Tile; +import com.google.android.gms.maps.model.TileOverlay; +import com.google.android.gms.maps.model.TileOverlayOptions; +import com.google.android.gms.maps.model.TileProvider; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class MapLocalTile extends MapFeature { + + class AIRMapLocalTileProvider implements TileProvider { + private static final int BUFFER_SIZE = 16 * 1024; + private int tileSize; + private String pathTemplate; + private final boolean useAssets; + + + public AIRMapLocalTileProvider(int tileSizet, String pathTemplate, boolean useAssets) { + this.tileSize = tileSizet; + this.pathTemplate = pathTemplate; + this.useAssets = useAssets; + } + + @Override + public Tile getTile(int x, int y, int zoom) { + byte[] image = readTileImage(x, y, zoom); + return image == null ? TileProvider.NO_TILE : new Tile(this.tileSize, this.tileSize, image); + } + + public void setPathTemplate(String pathTemplate) { + this.pathTemplate = pathTemplate; + } + + public void setTileSize(int tileSize) { + this.tileSize = tileSize; + } + + private byte[] readTileImage(int x, int y, int zoom) { + InputStream in = null; + ByteArrayOutputStream buffer = null; + String tileFilename = getTileFilename(x, y, zoom); + + try { + in = useAssets ? getContext().getAssets().open(tileFilename) : new FileInputStream(tileFilename); + buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[BUFFER_SIZE]; + + while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + + return buffer.toByteArray(); + } catch (IOException | OutOfMemoryError e) { + e.printStackTrace(); + return null; + } finally { + if (in != null) try { in.close(); } catch (Exception ignored) {} + if (buffer != null) try { buffer.close(); } catch (Exception ignored) {} + } + } + + private String getTileFilename(int x, int y, int zoom) { + String s = this.pathTemplate + .replace("{x}", Integer.toString(x)) + .replace("{y}", Integer.toString(y)) + .replace("{z}", Integer.toString(zoom)); + return s; + } + } + + private TileOverlayOptions tileOverlayOptions; + private TileOverlay tileOverlay; + private MapLocalTile.AIRMapLocalTileProvider tileProvider; + + private String pathTemplate; + private float tileSize; + private float zIndex; + private boolean useAssets; + + public MapLocalTile(Context context) { + super(context); + } + + public void setPathTemplate(String pathTemplate) { + this.pathTemplate = pathTemplate; + if (tileProvider != null) { + tileProvider.setPathTemplate(pathTemplate); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setZIndex(float zIndex) { + this.zIndex = zIndex; + if (tileOverlay != null) { + tileOverlay.setZIndex(zIndex); + } + } + + public void setTileSize(float tileSize) { + this.tileSize = tileSize; + if (tileProvider != null) { + tileProvider.setTileSize((int)tileSize); + } + } + + public void setUseAssets(boolean useAssets) { + this.useAssets = useAssets; + } + + public TileOverlayOptions getTileOverlayOptions() { + if (tileOverlayOptions == null) { + tileOverlayOptions = createTileOverlayOptions(); + } + return tileOverlayOptions; + } + + private TileOverlayOptions createTileOverlayOptions() { + TileOverlayOptions options = new TileOverlayOptions(); + options.zIndex(zIndex); + this.tileProvider = new MapLocalTile.AIRMapLocalTileProvider((int)this.tileSize, this.pathTemplate, this.useAssets); + options.tileProvider(this.tileProvider); + return options; + } + + @Override + public Object getFeature() { + return tileOverlay; + } + + @Override + public void addToMap(Object map) { + this.tileOverlay = ((GoogleMap) map).addTileOverlay(getTileOverlayOptions()); + } + + @Override + public void removeFromMap(Object map) { + tileOverlay.remove(); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapLocalTileManager.java b/android/src/main/java/com/rnmaps/maps/MapLocalTileManager.java new file mode 100644 index 000000000..88861e6dc --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapLocalTileManager.java @@ -0,0 +1,54 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +/** + * Created by zavadpe on 30/11/2017. + */ +public class MapLocalTileManager extends ViewGroupManager { + + public MapLocalTileManager(ReactApplicationContext reactContext) { + super(); + DisplayMetrics metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapLocalTile"; + } + + @Override + public MapLocalTile createViewInstance(ThemedReactContext context) { + return new MapLocalTile(context); + } + + @ReactProp(name = "pathTemplate") + public void setPathTemplate(MapLocalTile view, String pathTemplate) { + view.setPathTemplate(pathTemplate); + } + + @ReactProp(name = "tileSize", defaultFloat = 256f) + public void setTileSize(MapLocalTile view, float tileSize) { + view.setTileSize(tileSize); + } + + @ReactProp(name = "zIndex", defaultFloat = -1.0f) + public void setZIndex(MapLocalTile view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "useAssets", defaultBoolean = false) + public void setUseAssets(MapLocalTile view, boolean useAssets) { + view.setUseAssets(useAssets); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapManager.java b/android/src/main/java/com/rnmaps/maps/MapManager.java new file mode 100644 index 000000000..69a724de1 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapManager.java @@ -0,0 +1,504 @@ +package com.rnmaps.maps; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.facebook.react.R; +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.uimanager.LayoutShadowNode; +import com.facebook.react.uimanager.ReactStylesDiffMap; +import com.facebook.react.uimanager.StateWrapper; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.google.android.gms.location.Priority; +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.CameraPosition; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; + +import java.util.Map; + +public class MapManager extends ViewGroupManager { + + private static final String REACT_CLASS = "AIRMap"; + + 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 Map MY_LOCATION_PRIORITY = MapBuilder.of( + "balanced", Priority.PRIORITY_BALANCED_POWER_ACCURACY, + "high", Priority.PRIORITY_HIGH_ACCURACY, + "low", Priority.PRIORITY_LOW_POWER, + "passive", Priority.PRIORITY_PASSIVE + ); + + private final ReactApplicationContext appContext; + private MapMarkerManager markerManager; + + protected GoogleMapOptions googleMapOptions; + + protected MapsInitializer.Renderer renderer; + + public MapManager(ReactApplicationContext context) { + this.appContext = context; + } + + public MapMarkerManager getMarkerManager() { + return this.markerManager; + } + + public void setMarkerManager(MapMarkerManager markerManager) { + this.markerManager = markerManager; + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + protected MapView createViewInstance(@NonNull ThemedReactContext context) { + return new MapView(context, this.appContext, this, googleMapOptions); + } + + @Override + protected MapView createViewInstance(int reactTag, @NonNull ThemedReactContext reactContext, @Nullable ReactStylesDiffMap initialProps, @Nullable StateWrapper stateWrapper) { + this.googleMapOptions = new GoogleMapOptions(); + if (initialProps != null) { + if (initialProps.getString("googleMapId") != null) { + googleMapOptions.mapId(initialProps.getString("googleMapId")); + } + if (initialProps.hasKey("liteMode")) { + googleMapOptions.liteMode(initialProps.getBoolean("liteMode", false)); + } + if (initialProps.hasKey("initialCamera")) { + CameraPosition position = MapView.cameraPositionFromMap(initialProps.getMap("initialCamera")); + if (position != null) { + googleMapOptions.camera(position); + } + } else if (initialProps.hasKey("camera")) { + CameraPosition position = MapView.cameraPositionFromMap(initialProps.getMap("camera")); + if (position != null) { + googleMapOptions.camera(position); + } + } + if (initialProps.hasKey("googleRenderer") && "LEGACY".equals(initialProps.getString("googleRenderer"))) { + renderer = MapsInitializer.Renderer.LEGACY; + } else { + renderer = MapsInitializer.Renderer.LATEST; + } + } + return super.createViewInstance(reactTag, reactContext, initialProps, stateWrapper); + } + + 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(MapView view, ReadableMap region) { + view.setRegion(region); + } + + @ReactProp(name = "googleRenderer") + public void setGoogleRenderer(MapView view, @Nullable String googleRenderer) { + // do nothing, passed as part of the InitialProps + } + + @ReactProp(name = "liteMode", defaultBoolean = false) + public void setLiteMode(MapView view, boolean liteMode) { + googleMapOptions.liteMode(liteMode); + } + + @ReactProp(name = "googleMapId") + public void setGoogleMapId(MapView view, @Nullable String googleMapId) { + if (googleMapId != null) { + googleMapOptions.mapId(googleMapId); + } + } + + @ReactProp(name = "initialRegion") + public void setInitialRegion(MapView view, ReadableMap initialRegion) { + view.setInitialRegion(initialRegion); + } + + @ReactProp(name = "camera") + public void setCamera(MapView view, ReadableMap camera) { + view.setCamera(camera); + } + + @ReactProp(name = "initialCamera") + public void setInitialCamera(MapView view, ReadableMap initialCamera) { + view.setInitialCamera(initialCamera); + } + + @ReactProp(name = "mapType") + public void setMapType(MapView view, @Nullable String mapType) { + int typeId = MAP_TYPES.get(mapType); + view.map.setMapType(typeId); + } + + @ReactProp(name = "customMapStyleString") + public void setMapStyle(MapView view, @Nullable String customMapStyleString) { + view.setMapStyle(customMapStyleString); + } + + @ReactProp(name = "mapPadding") + public void setMapPadding(MapView view, @Nullable ReadableMap padding) { + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; + double density = (double) view.getResources().getDisplayMetrics().density; + + if (padding != null) { + if (padding.hasKey("left")) { + left = (int) (padding.getDouble("left") * density); + } + + if (padding.hasKey("top")) { + top = (int) (padding.getDouble("top") * density); + } + + if (padding.hasKey("right")) { + right = (int) (padding.getDouble("right") * density); + } + + if (padding.hasKey("bottom")) { + bottom = (int) (padding.getDouble("bottom") * density); + } + } + + view.applyBaseMapPadding(left, top, right, bottom); + view.map.setPadding(left, top, right, bottom); + } + + @ReactProp(name = "showsUserLocation", defaultBoolean = false) + public void setShowsUserLocation(MapView view, boolean showUserLocation) { + view.setShowsUserLocation(showUserLocation); + } + + @ReactProp(name = "userLocationPriority") + public void setUserLocationPriority(MapView view, @Nullable String accuracy) { + view.setUserLocationPriority(MY_LOCATION_PRIORITY.get(accuracy)); + } + + @ReactProp(name = "userLocationUpdateInterval", defaultInt = 5000) + public void setUserLocationUpdateInterval(MapView view, int updateInterval) { + view.setUserLocationUpdateInterval(updateInterval); + } + + @ReactProp(name = "userLocationFastestInterval", defaultInt = 5000) + public void setUserLocationFastestInterval(MapView view, int fastestInterval) { + view.setUserLocationFastestInterval(fastestInterval); + } + + @ReactProp(name = "showsMyLocationButton", defaultBoolean = true) + public void setShowsMyLocationButton(MapView view, boolean showMyLocationButton) { + view.setShowsMyLocationButton(showMyLocationButton); + } + + @ReactProp(name = "toolbarEnabled", defaultBoolean = true) + public void setToolbarEnabled(MapView 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(MapView view, boolean handlePanDrag) { + view.setHandlePanDrag(handlePanDrag); + } + + @ReactProp(name = "showsTraffic", defaultBoolean = false) + public void setShowTraffic(MapView view, boolean showTraffic) { + view.map.setTrafficEnabled(showTraffic); + } + + @ReactProp(name = "showsBuildings", defaultBoolean = false) + public void setShowBuildings(MapView view, boolean showBuildings) { + view.map.setBuildingsEnabled(showBuildings); + } + + @ReactProp(name = "showsIndoors", defaultBoolean = false) + public void setShowIndoors(MapView view, boolean showIndoors) { + view.map.setIndoorEnabled(showIndoors); + } + + @ReactProp(name = "showsIndoorLevelPicker", defaultBoolean = false) + public void setShowsIndoorLevelPicker(MapView view, boolean showsIndoorLevelPicker) { + view.map.getUiSettings().setIndoorLevelPickerEnabled(showsIndoorLevelPicker); + } + + @ReactProp(name = "showsCompass", defaultBoolean = false) + public void setShowsCompass(MapView view, boolean showsCompass) { + view.map.getUiSettings().setCompassEnabled(showsCompass); + } + + @ReactProp(name = "scrollEnabled", defaultBoolean = false) + public void setScrollEnabled(MapView view, boolean scrollEnabled) { + view.map.getUiSettings().setScrollGesturesEnabled(scrollEnabled); + } + + @ReactProp(name = "zoomEnabled", defaultBoolean = false) + public void setZoomEnabled(MapView view, boolean zoomEnabled) { + view.map.getUiSettings().setZoomGesturesEnabled(zoomEnabled); + } + + @ReactProp(name = "zoomControlEnabled", defaultBoolean = true) + public void setZoomControlEnabled(MapView view, boolean zoomControlEnabled) { + view.map.getUiSettings().setZoomControlsEnabled(zoomControlEnabled); + } + + @ReactProp(name = "rotateEnabled", defaultBoolean = false) + public void setRotateEnabled(MapView view, boolean rotateEnabled) { + view.map.getUiSettings().setRotateGesturesEnabled(rotateEnabled); + } + + @ReactProp(name = "scrollDuringRotateOrZoomEnabled", defaultBoolean = true) + public void setScrollDuringRotateOrZoomEnabled(MapView view, boolean scrollDuringRotateOrZoomEnabled) { + view.map.getUiSettings().setScrollGesturesEnabledDuringRotateOrZoom(scrollDuringRotateOrZoomEnabled); + } + + @ReactProp(name = "cacheEnabled", defaultBoolean = false) + public void setCacheEnabled(MapView view, boolean cacheEnabled) { + view.setCacheEnabled(cacheEnabled); + } + + @ReactProp(name = "poiClickEnabled", defaultBoolean = true) + public void setPoiClickEnabled(MapView view, boolean poiClickEnabled) { + view.setPoiClickEnabled(poiClickEnabled); + } + + @ReactProp(name = "loadingEnabled", defaultBoolean = false) + public void setLoadingEnabled(MapView view, boolean loadingEnabled) { + view.enableMapLoading(loadingEnabled); + } + + @ReactProp(name = "moveOnMarkerPress", defaultBoolean = true) + public void setMoveOnMarkerPress(MapView view, boolean moveOnPress) { + view.setMoveOnMarkerPress(moveOnPress); + } + + @ReactProp(name = "loadingBackgroundColor", customType = "Color") + public void setLoadingBackgroundColor(MapView view, @Nullable Integer loadingBackgroundColor) { + view.setLoadingBackgroundColor(loadingBackgroundColor); + } + + @ReactProp(name = "loadingIndicatorColor", customType = "Color") + public void setLoadingIndicatorColor(MapView view, @Nullable Integer loadingIndicatorColor) { + view.setLoadingIndicatorColor(loadingIndicatorColor); + } + + @ReactProp(name = "pitchEnabled", defaultBoolean = false) + public void setPitchEnabled(MapView view, boolean pitchEnabled) { + view.map.getUiSettings().setTiltGesturesEnabled(pitchEnabled); + } + + @ReactProp(name = "minZoomLevel") + public void setMinZoomLevel(MapView view, float minZoomLevel) { + view.map.setMinZoomPreference(minZoomLevel); + } + + @ReactProp(name = "maxZoomLevel") + public void setMaxZoomLevel(MapView view, float maxZoomLevel) { + view.map.setMaxZoomPreference(maxZoomLevel); + } + + @ReactProp(name = "kmlSrc") + public void setKmlSrc(MapView view, String kmlUrl) { + if (kmlUrl != null) { + view.setKmlSrc(kmlUrl); + } + } + + @ReactProp(name = "accessibilityLabel") + public void setAccessibilityLabel(MapView view, @Nullable String accessibilityLabel) { + view.setTag(R.id.accessibility_label, accessibilityLabel); + } + + @Override + public void receiveCommand(@NonNull MapView view, String commandId, @Nullable ReadableArray args) { + int duration; + double lat; + double lng; + double lngDelta; + double latDelta; + ReadableMap region; + ReadableMap camera; + + switch (commandId) { + case "setCamera": + if (args == null) { + break; + } + camera = args.getMap(0); + view.animateToCamera(camera, 0); + break; + + case "animateCamera": + if (args == null) { + break; + } + camera = args.getMap(0); + duration = args.getInt(1); + view.animateToCamera(camera, duration); + break; + + case "animateToRegion": + if (args == null) { + break; + } + 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 "fitToElements": + if (args == null) { + break; + } + view.fitToElements(args.getMap(0), args.getBoolean(1)); + break; + + case "fitToSuppliedMarkers": + if (args == null) { + break; + } + view.fitToSuppliedMarkers(args.getArray(0), args.getMap(1), args.getBoolean(2)); + break; + + case "fitToCoordinates": + if (args == null) { + break; + } + view.fitToCoordinates(args.getArray(0), args.getMap(1), args.getBoolean(2)); + break; + + case "setMapBoundaries": + if (args == null) { + break; + } + view.setMapBoundaries(args.getMap(0), args.getMap(1)); + break; + + case "setIndoorActiveLevelIndex": + if (args == null) { + break; + } + view.setIndoorActiveLevelIndex(args.getInt(0)); + 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"), + "onCalloutPress", MapBuilder.of("registrationName", "onCalloutPress") + ); + + map.putAll(MapBuilder.of( + "onUserLocationChange", MapBuilder.of("registrationName", "onUserLocationChange"), + "onMarkerDragStart", MapBuilder.of("registrationName", "onMarkerDragStart"), + "onMarkerDrag", MapBuilder.of("registrationName", "onMarkerDrag"), + "onMarkerDragEnd", MapBuilder.of("registrationName", "onMarkerDragEnd"), + "onPanDrag", MapBuilder.of("registrationName", "onPanDrag"), + "onKmlReady", MapBuilder.of("registrationName", "onKmlReady"), + "onPoiClick", MapBuilder.of("registrationName", "onPoiClick") + )); + + map.putAll(MapBuilder.of( + "onIndoorLevelActivated", MapBuilder.of("registrationName", "onIndoorLevelActivated"), + "onIndoorBuildingFocused", MapBuilder.of("registrationName", "onIndoorBuildingFocused"), + "onDoublePress", MapBuilder.of("registrationName", "onDoublePress"), + "onMapLoaded", MapBuilder.of("registrationName", "onMapLoaded"), + "onMarkerSelect", MapBuilder.of("registrationName", "onMarkerSelect"), + "onMarkerDeselect", MapBuilder.of("registrationName", "onMarkerDeselect"), + "onRegionChangeStart", MapBuilder.of("registrationName", "onRegionChangeStart") + )); + + return map; + } + + @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(MapView parent, View child, int index) { + parent.addFeature(child, index); + } + + @Override + public int getChildCount(MapView view) { + return view.getFeatureCount(); + } + + @Override + public View getChildAt(MapView view, int index) { + return view.getFeatureAt(index); + } + + @Override + public void removeViewAt(MapView parent, int index) { + parent.removeFeatureAt(index); + } + + @Override + public void updateExtraData(MapView view, Object extraData) { + view.updateExtraData(extraData); + } + + void pushEvent(ThemedReactContext context, View view, String name, WritableMap data) { + context + .getReactApplicationContext() + .getJSModule(RCTEventEmitter.class) + .receiveEvent(view.getId(), name, data); + } + + @Override + public void onDropViewInstance(MapView view) { + view.doDestroy(); + super.onDropViewInstance(view); + } + +} diff --git a/android/src/main/java/com/rnmaps/maps/MapMarker.java b/android/src/main/java/com/rnmaps/maps/MapMarker.java new file mode 100644 index 000000000..b755f9ba5 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapMarker.java @@ -0,0 +1,625 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.view.View; +import android.widget.LinearLayout; +import android.animation.ObjectAnimator; +import android.util.Property; +import android.animation.TypeEvaluator; + +import androidx.annotation.Nullable; + +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.controller.BaseControllerListener; +import com.facebook.drawee.controller.ControllerListener; +import com.facebook.drawee.drawable.ScalingUtils; +import com.facebook.drawee.generic.GenericDraweeHierarchy; +import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.DraweeHolder; +import com.facebook.imagepipeline.core.ImagePipeline; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.image.CloseableStaticBitmap; +import com.facebook.imagepipeline.image.ImageInfo; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; +import com.facebook.react.bridge.ReadableMap; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.maps.android.collections.MarkerManager; + +public class MapMarker extends MapFeature { + + 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 MapCallout 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 tracksViewChanges = true; + private boolean tracksViewChangesActive = false; + + private boolean hasCustomMarkerView = false; + private final MapMarkerManager markerManager; + private String imageUri; + + 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 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); + } + } + if (MapMarker.this.markerManager != null && MapMarker.this.imageUri != null) { + MapMarker.this.markerManager.getSharedIcon(MapMarker.this.imageUri) + .updateIcon(iconBitmapDescriptor, iconBitmap); + } + update(true); + } + }; + + public MapMarker(Context context, MapMarkerManager markerManager) { + super(context); + this.context = context; + this.markerManager = markerManager; + logoHolder = DraweeHolder.create(createDraweeHierarchy(), context); + logoHolder.onAttach(); + } + + public MapMarker(Context context, MarkerOptions options, MapMarkerManager markerManager) { + super(context); + this.context = context; + this.markerManager = markerManager; + logoHolder = DraweeHolder.create(createDraweeHierarchy(), context); + logoHolder.onAttach(); + + position = options.getPosition(); + setAnchor(options.getAnchorU(), options.getAnchorV()); + setCalloutAnchor(options.getInfoWindowAnchorU(), options.getInfoWindowAnchorV()); + setTitle(options.getTitle()); + setSnippet(options.getSnippet()); + setRotation(options.getRotation()); + setFlat(options.isFlat()); + setDraggable(options.isDraggable()); + setZIndex(Math.round(options.getZIndex())); + setAlpha(options.getAlpha()); + iconBitmapDescriptor = options.getIcon(); + } + + 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(false); + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + update(false); + } + + public String getIdentifier() { + return this.identifier; + } + + public void setTitle(String title) { + this.title = title; + if (marker != null) { + marker.setTitle(title); + } + update(false); + } + + public void setSnippet(String snippet) { + this.snippet = snippet; + if (marker != null) { + marker.setSnippet(snippet); + } + update(false); + } + + public void setRotation(float rotation) { + this.rotation = rotation; + if (marker != null) { + marker.setRotation(rotation); + } + update(false); + } + + public void setFlat(boolean flat) { + this.flat = flat; + if (marker != null) { + marker.setFlat(flat); + } + update(false); + } + + public void setDraggable(boolean draggable) { + this.draggable = draggable; + if (marker != null) { + marker.setDraggable(draggable); + } + update(false); + } + + public void setZIndex(int zIndex) { + this.zIndex = zIndex; + if (marker != null) { + marker.setZIndex(zIndex); + } + update(false); + } + + public void setOpacity(float opacity) { + this.opacity = opacity; + if (marker != null) { + marker.setAlpha(opacity); + } + update(false); + } + + public void setMarkerHue(float markerHue) { + this.markerHue = markerHue; + update(false); + } + + public void setAnchor(double x, double y) { + anchorIsSet = true; + anchorX = (float) x; + anchorY = (float) y; + if (marker != null) { + marker.setAnchor(anchorX, anchorY); + } + update(false); + } + + public void setCalloutAnchor(double x, double y) { + calloutAnchorIsSet = true; + calloutAnchorX = (float) x; + calloutAnchorY = (float) y; + if (marker != null) { + marker.setInfoWindowAnchor(calloutAnchorX, calloutAnchorY); + } + update(false); + } + + public void setTracksViewChanges(boolean tracksViewChanges) { + this.tracksViewChanges = tracksViewChanges; + updateTracksViewChanges(); + } + + private void updateTracksViewChanges() { + boolean shouldTrack = tracksViewChanges && hasCustomMarkerView && marker != null; + if (shouldTrack == tracksViewChangesActive) return; + tracksViewChangesActive = shouldTrack; + + if (shouldTrack) { + ViewChangesTracker.getInstance().addMarker(this); + } else { + ViewChangesTracker.getInstance().removeMarker(this); + + // Let it render one more time to avoid race conditions. + // i.e. Image onLoad -> + // ViewChangesTracker may not get a chance to render -> + // setState({ tracksViewChanges: false }) -> + // image loaded but not rendered. + updateMarkerIcon(); + } + } + + public LatLng getPosition() { + return position; + } + + public boolean updateCustomForTracking() { + if (!tracksViewChangesActive) + return false; + + updateMarkerIcon(); + + return true; + } + + public void updateMarkerIcon() { + if (marker == null) return; + + marker.setIcon(getIcon()); + } + + public LatLng interpolate(float fraction, LatLng a, LatLng b) { + double lat = (b.latitude - a.latitude) * fraction + a.latitude; + double lng = (b.longitude - a.longitude) * fraction + a.longitude; + return new LatLng(lat, lng); + } + + public void animateToCoodinate(LatLng finalPosition, Integer duration) { + TypeEvaluator typeEvaluator = new TypeEvaluator() { + @Override + public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { + return interpolate(fraction, startValue, endValue); + } + }; + Property property = Property.of(Marker.class, LatLng.class, "position"); + ObjectAnimator animator = ObjectAnimator.ofObject( + marker, + property, + typeEvaluator, + finalPosition); + animator.setDuration(duration); + animator.start(); + } + + public void setImage(String uri) { + + boolean shouldLoadImage = true; + + if (this.markerManager != null) { + // remove marker from previous shared icon if needed, to avoid future updates from it. + // remove the shared icon completely if no markers on it as well. + // this is to avoid memory leak due to orphan bitmaps. + // + // However in case where client want to update all markers from icon A to icon B + // and after some time to update back from icon B to icon A + // it may be better to keep it though. We assume that is rare. + if (this.imageUri != null) { + this.markerManager.getSharedIcon(this.imageUri).removeMarker(this); + this.markerManager.removeSharedIconIfEmpty(this.imageUri); + } + if (uri != null) { + // listening for marker bitmap descriptor update, as well as check whether to load the image. + MapMarkerManager.AirMapMarkerSharedIcon sharedIcon = this.markerManager.getSharedIcon(uri); + sharedIcon.addMarker(this); + shouldLoadImage = sharedIcon.shouldLoadImage(); + } + } + + this.imageUri = uri; + if (!shouldLoadImage) {return;} + + if (uri == null) { + iconBitmapDescriptor = null; + update(true); + } else if (uri.startsWith("http://") || uri.startsWith("https://") || + uri.startsWith("file://") || uri.startsWith("asset://") || uri.startsWith("data:")) { + 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); + int drawableId = getDrawableResourceByName(uri); + iconBitmap = BitmapFactory.decodeResource(getResources(), drawableId); + if (iconBitmap == null) { // VectorDrawable or similar + Drawable drawable = getResources().getDrawable(drawableId); + iconBitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); + Canvas canvas = new Canvas(iconBitmap); + drawable.draw(canvas); + } + if (this.markerManager != null) { + this.markerManager.getSharedIcon(uri).updateIcon(iconBitmapDescriptor, iconBitmap); + } + update(true); + } + } + + public void setIconBitmapDescriptor(BitmapDescriptor bitmapDescriptor, Bitmap bitmap) { + this.iconBitmapDescriptor = bitmapDescriptor; + this.iconBitmap = bitmap; + this.update(true); + } + + public void setIconBitmap(Bitmap bitmap) { + this.iconBitmap = bitmap; + } + + public MarkerOptions getMarkerOptions() { + if (markerOptions == null) { + markerOptions = new MarkerOptions(); + } + + fillMarkerOptions(markerOptions); + 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 MapCallout)) { + hasCustomMarkerView = true; + updateTracksViewChanges(); + } + update(true); + } + + @Override + public void requestLayout() { + super.requestLayout(); + + if (getChildCount() == 0) { + if (hasCustomMarkerView) { + hasCustomMarkerView = false; + clearDrawableCache(); + updateTracksViewChanges(); + update(true); + } + + } + } + + @Override + public Object getFeature() { + return marker; + } + + @Override + public void addToMap(Object collection) { + MarkerManager.Collection markerCollection = (MarkerManager.Collection) collection; + marker = markerCollection.addMarker(getMarkerOptions()); + updateTracksViewChanges(); + } + + @Override + public void removeFromMap(Object collection) { + if (marker == null) { + return; + } + MarkerManager.Collection markerCollection = (MarkerManager.Collection) collection; + markerCollection.remove(marker); + marker = null; + updateTracksViewChanges(); + } + + 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 fillMarkerOptions(MarkerOptions options) { + options.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(boolean updateIcon) { + if (marker == null) { + return; + } + + if (updateIcon) + updateMarkerIcon(); + + 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(true); + } + + private Bitmap mLastBitmapCreated = null; + + private void clearDrawableCache() { + mLastBitmapCreated = null; + } + + private Bitmap createDrawable() { + int width = this.width <= 0 ? 100 : this.width; + int height = this.height <= 0 ? 100 : this.height; + this.buildDrawingCache(); + + // Do not create the doublebuffer-bitmap each time. reuse it to save memory. + Bitmap bitmap = mLastBitmapCreated; + + if (bitmap == null || + bitmap.isRecycled() || + bitmap.getWidth() != width || + bitmap.getHeight() != height) { + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + mLastBitmapCreated = bitmap; + } else { + bitmap.eraseColor(Color.TRANSPARENT); + } + + Canvas canvas = new Canvas(bitmap); + this.draw(canvas); + + return bitmap; + } + + public void setCalloutView(MapCallout view) { + this.calloutView = view; + } + + public MapCallout 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; + } + + 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 + )); + + LL.addView(LL2); + LL2.addView(this.calloutView); + + this.wrappedCalloutView = LL; + } + + private int getDrawableResourceByName(String name) { + return getResources().getIdentifier( + name, + "drawable", + getContext().getPackageName()); + } + + private BitmapDescriptor getBitmapDescriptorByName(String name) { + return BitmapDescriptorFactory.fromResource(getDrawableResourceByName(name)); + } + +} diff --git a/android/src/main/java/com/rnmaps/maps/MapMarkerManager.java b/android/src/main/java/com/rnmaps/maps/MapMarkerManager.java new file mode 100644 index 000000000..8cf3d72fc --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapMarkerManager.java @@ -0,0 +1,368 @@ +package com.rnmaps.maps; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.facebook.react.R; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.LayoutShadowNode; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.LatLng; + +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; + +public class MapMarkerManager extends ViewGroupManager { + + public static class AirMapMarkerSharedIcon { + private BitmapDescriptor iconBitmapDescriptor; + private Bitmap bitmap; + private final Map markers; + private boolean loadImageStarted; + + public AirMapMarkerSharedIcon() { + this.markers = new WeakHashMap<>(); + this.loadImageStarted = false; + } + + /** + * check whether the load image process started. + * caller AirMapMarker will only need to load it when this returns true. + * + * @return true if it is not started, false otherwise. + */ + public synchronized boolean shouldLoadImage() { + if (!this.loadImageStarted) { + this.loadImageStarted = true; + return true; + } + return false; + } + + /** + * subscribe icon update for given marker. + *

+ * The marker is wrapped in weakReference, so no need to remove it explicitly. + * + * @param marker + */ + public synchronized void addMarker(MapMarker marker) { + this.markers.put(marker, true); + if (this.iconBitmapDescriptor != null) { + marker.setIconBitmapDescriptor(this.iconBitmapDescriptor, this.bitmap); + } + } + + /** + * Remove marker from this shared icon. + *

+ * Marker will only need to call it when the marker receives a different marker image uri. + * + * @param marker + */ + public synchronized void removeMarker(MapMarker marker) { + this.markers.remove(marker); + } + + /** + * check if there is markers still listening on this icon. + * when there are not markers listen on it, we can remove it. + * + * @return true if there is, false otherwise + */ + public synchronized boolean hasMarker() { + return this.markers.isEmpty(); + } + + /** + * Update the bitmap descriptor and bitmap for the image uri. + * And notify all subscribers about the update. + * + * @param bitmapDescriptor + * @param bitmap + */ + public synchronized void updateIcon(BitmapDescriptor bitmapDescriptor, Bitmap bitmap) { + + this.iconBitmapDescriptor = bitmapDescriptor; + this.bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); + + if (this.markers.isEmpty()) { + return; + } + + for (Map.Entry markerEntry : markers.entrySet()) { + if (markerEntry.getKey() != null) { + markerEntry.getKey().setIconBitmapDescriptor(bitmapDescriptor, bitmap); + } + } + } + } + + private final Map sharedIcons = new ConcurrentHashMap<>(); + + /** + * get the shared icon object, if not existed, create a new one and store it. + * + * @param uri + * @return the icon object for the given uri. + */ + public AirMapMarkerSharedIcon getSharedIcon(String uri) { + AirMapMarkerSharedIcon icon = this.sharedIcons.get(uri); + if (icon == null) { + synchronized (this) { + if ((icon = this.sharedIcons.get(uri)) == null) { + icon = new AirMapMarkerSharedIcon(); + this.sharedIcons.put(uri, icon); + } + } + } + return icon; + } + + /** + * Remove the share icon object from our sharedIcons map when no markers are listening for it. + * + * @param uri + */ + public void removeSharedIconIfEmpty(String uri) { + AirMapMarkerSharedIcon icon = this.sharedIcons.get(uri); + if (icon == null) { + return; + } + if (!icon.hasMarker()) { + synchronized (this) { + if ((icon = this.sharedIcons.get(uri)) != null && !icon.hasMarker()) { + this.sharedIcons.remove(uri); + } + } + } + } + + public MapMarkerManager() { + } + + @Override + public String getName() { + return "AIRMapMarker"; + } + + @Override + public MapMarker createViewInstance(ThemedReactContext context) { + return new MapMarker(context, this); + } + + @ReactProp(name = "coordinate") + public void setCoordinate(MapMarker view, ReadableMap map) { + view.setCoordinate(map); + } + + @ReactProp(name = "title") + public void setTitle(MapMarker view, String title) { + view.setTitle(title); + } + + @ReactProp(name = "identifier") + public void setIdentifier(MapMarker view, String identifier) { + view.setIdentifier(identifier); + } + + @ReactProp(name = "description") + public void setDescription(MapMarker 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) { +// +// } +// +// @ReactProp(name = "calloutOffset") +// public void setCalloutOffset(AirMapMarker view, ReadableMap map) { +// +// } + + @ReactProp(name = "anchor") + public void setAnchor(MapMarker 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(MapMarker 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(MapMarker view, @Nullable String source) { + view.setImage(source); + } +// public void setImage(AirMapMarker view, ReadableMap image) { +// view.setImage(image); +// } + + @ReactProp(name = "icon") + public void setIcon(MapMarker view, @Nullable String source) { + view.setImage(source); + } + + @ReactProp(name = "pinColor", defaultInt = Color.RED, customType = "Color") + public void setPinColor(MapMarker 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(MapMarker view, float rotation) { + view.setRotation(rotation); + } + + @ReactProp(name = "flat", defaultBoolean = false) + public void setFlat(MapMarker view, boolean flat) { + view.setFlat(flat); + } + + @ReactProp(name = "draggable", defaultBoolean = false) + public void setDraggable(MapMarker view, boolean draggable) { + view.setDraggable(draggable); + } + + @Override + @ReactProp(name = "zIndex", defaultFloat = 0.0f) + public void setZIndex(MapMarker 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(MapMarker view, float opacity) { + super.setOpacity(view, opacity); + view.setOpacity(opacity); + } + + @ReactProp(name = "tracksViewChanges", defaultBoolean = true) + public void setTracksViewChanges(MapMarker view, boolean tracksViewChanges) { + view.setTracksViewChanges(tracksViewChanges); + } + + @ReactProp(name = "accessibilityLabel") + public void setAccessibilityLabel(MapMarker view, @Nullable String accessibilityLabel) { + view.setTag(R.id.accessibility_label, accessibilityLabel); + } + + @Override + public void addView(MapMarker 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 MapCallout) { + parent.setCalloutView((MapCallout) child); + } else { + super.addView(parent, child, index); + parent.update(true); + } + } + + @Override + public void removeViewAt(MapMarker parent, int index) { + super.removeViewAt(parent, index); + parent.update(true); + } + + @Override + public void receiveCommand(@NonNull MapMarker view, String commandId, @Nullable ReadableArray args) { + int duration; + double lat; + double lng; + ReadableMap region; + + switch (commandId) { + case "showCallout": + ((Marker) view.getFeature()).showInfoWindow(); + break; + + case "hideCallout": + ((Marker) view.getFeature()).hideInfoWindow(); + break; + + case "animateMarkerToCoordinate": + if (args == null) { + break; + } + region = args.getMap(0); + duration = args.getInt(1); + + lng = region.getDouble("longitude"); + lat = region.getDouble("latitude"); + view.animateToCoodinate(new LatLng(lat, lng), duration); + break; + + case "redraw": + view.updateMarkerIcon(); + break; + } + } + + @Override + @Nullable + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.>builder() + .put("onPress", MapBuilder.of("registrationName", "onPress")) + .put("onCalloutPress", MapBuilder.of("registrationName", "onCalloutPress")) + .put("onDragStart", MapBuilder.of("registrationName", "onDragStart")) + .put("onDrag", MapBuilder.of("registrationName", "onDrag")) + .put("onDragEnd", MapBuilder.of("registrationName", "onDragEnd")) + .build(); + } + + @Override + @Nullable + public Map getExportedCustomBubblingEventTypeConstants() { + return MapBuilder.>builder() + .put("onSelect", MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", "onSelect"))) + .put("onDeselect", MapBuilder.of("phasedRegistrationNames", MapBuilder.of("bubbled", "onDeselect"))) + .build(); + } + + + @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(MapMarker 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/android/src/main/java/com/rnmaps/maps/MapModule.java b/android/src/main/java/com/rnmaps/maps/MapModule.java new file mode 100644 index 000000000..aef5bc5ff --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapModule.java @@ -0,0 +1,283 @@ +package com.rnmaps.maps; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.location.Address; +import android.location.Geocoder; +import android.net.Uri; +import android.util.Base64; +import android.util.DisplayMetrics; + +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.module.annotations.ReactModule; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.CameraPosition; +import com.google.android.gms.maps.model.LatLng; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@ReactModule(name = MapModule.NAME) +public class MapModule extends ReactContextBaseJavaModule { + + public static final String NAME = "AirMapModule"; + 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 MapModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public String getName() { + return NAME; + } + + @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) { + + // 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"; + + MapUIBlock uiBlock = new MapUIBlock(tag, promise, context, view -> { + view.map.snapshot(new GoogleMap.SnapshotReadyCallback() { + public void onSnapshotReady(@Nullable Bitmap snapshot) { + + // 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); + } + + // 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); + } + } + }); + + return null; + }); + + // Add UI-block so we can get a valid reference to the map-view + + uiBlock.addToUIManager(); + } + + @ReactMethod + public void getCamera(final int tag, final Promise promise) { + final ReactApplicationContext context = getReactApplicationContext(); + + MapUIBlock uiBlock = new MapUIBlock(tag, promise, context, view -> { + CameraPosition position = view.map.getCameraPosition(); + + WritableMap centerJson = new WritableNativeMap(); + centerJson.putDouble("latitude", position.target.latitude); + centerJson.putDouble("longitude", position.target.longitude); + + WritableMap cameraJson = new WritableNativeMap(); + cameraJson.putMap("center", centerJson); + cameraJson.putDouble("heading", (double)position.bearing); + cameraJson.putDouble("zoom", (double)position.zoom); + cameraJson.putDouble("pitch", (double)position.tilt); + + promise.resolve(cameraJson); + + return null; + }); + + uiBlock.addToUIManager(); + } + + @ReactMethod + public void getAddressFromCoordinates(final int tag, final ReadableMap coordinate, final Promise promise) { + final ReactApplicationContext context = getReactApplicationContext(); + + MapUIBlock uiBlock = new MapUIBlock(tag, promise, context, mapView -> { + if (coordinate == null || + !coordinate.hasKey("latitude") || + !coordinate.hasKey("longitude")) { + promise.reject("Invalid coordinate format"); + return null; + } + Geocoder geocoder = new Geocoder(context); + try { + List

list = + geocoder.getFromLocation(coordinate.getDouble("latitude"),coordinate.getDouble("longitude"),1); + if (list.isEmpty()) { + promise.reject("Can not get address location"); + return null; + } + Address address = list.get(0); + + WritableMap addressJson = new WritableNativeMap(); + addressJson.putString("name", address.getFeatureName()); + addressJson.putString("locality", address.getLocality()); + addressJson.putString("thoroughfare", address.getThoroughfare()); + addressJson.putString("subThoroughfare", address.getSubThoroughfare()); + addressJson.putString("subLocality", address.getSubLocality()); + addressJson.putString("administrativeArea", address.getAdminArea()); + addressJson.putString("subAdministrativeArea", address.getSubAdminArea()); + addressJson.putString("postalCode", address.getPostalCode()); + addressJson.putString("countryCode", address.getCountryCode()); + addressJson.putString("country", address.getCountryName()); + + promise.resolve(addressJson); + } catch (IOException e) { + promise.reject("Can not get address location"); + } + + return null; + }); + + uiBlock.addToUIManager(); + } + + @ReactMethod + public void pointForCoordinate(final int tag, ReadableMap coordinate, final Promise promise) { + final ReactApplicationContext context = getReactApplicationContext(); + final double density = (double) context.getResources().getDisplayMetrics().density; + + final LatLng coord = new LatLng( + coordinate.hasKey("latitude") ? coordinate.getDouble("latitude") : 0.0, + coordinate.hasKey("longitude") ? coordinate.getDouble("longitude") : 0.0 + ); + + MapUIBlock uiBlock = new MapUIBlock(tag, promise, context, view -> { + Point pt = view.map.getProjection().toScreenLocation(coord); + + WritableMap ptJson = new WritableNativeMap(); + ptJson.putDouble("x", (double)pt.x / density); + ptJson.putDouble("y", (double)pt.y / density); + + promise.resolve(ptJson); + + return null; + }); + + uiBlock.addToUIManager(); + } + + @ReactMethod + public void coordinateForPoint(final int tag, ReadableMap point, final Promise promise) { + final ReactApplicationContext context = getReactApplicationContext(); + final double density = (double) context.getResources().getDisplayMetrics().density; + + final Point pt = new Point( + point.hasKey("x") ? (int)(point.getDouble("x") * density) : 0, + point.hasKey("y") ? (int)(point.getDouble("y") * density) : 0 + ); + + MapUIBlock uiBlock = new MapUIBlock(tag, promise, context, view -> { + LatLng coord = view.map.getProjection().fromScreenLocation(pt); + + WritableMap coordJson = new WritableNativeMap(); + coordJson.putDouble("latitude", coord.latitude); + coordJson.putDouble("longitude", coord.longitude); + + promise.resolve(coordJson); + + return null; + }); + + uiBlock.addToUIManager(); + } + + @ReactMethod + public void getMapBoundaries(final int tag, final Promise promise) { + final ReactApplicationContext context = getReactApplicationContext(); + + MapUIBlock uiBlock = new MapUIBlock(tag, promise, context, view -> { + double[][] boundaries = view.getMapBoundaries(); + + WritableMap coordinates = new WritableNativeMap(); + WritableMap northEastHash = new WritableNativeMap(); + WritableMap southWestHash = new WritableNativeMap(); + + northEastHash.putDouble("longitude", boundaries[0][0]); + northEastHash.putDouble("latitude", boundaries[0][1]); + southWestHash.putDouble("longitude", boundaries[1][0]); + southWestHash.putDouble("latitude", boundaries[1][1]); + + coordinates.putMap("northEast", northEastHash); + coordinates.putMap("southWest", southWestHash); + + promise.resolve(coordinates); + + return null; + }); + + uiBlock.addToUIManager(); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapOverlay.java b/android/src/main/java/com/rnmaps/maps/MapOverlay.java new file mode 100644 index 000000000..fc74c9bdd --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapOverlay.java @@ -0,0 +1,166 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Bitmap; + +import com.facebook.react.bridge.ReadableArray; +import com.google.android.gms.maps.model.BitmapDescriptor; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.GroundOverlay; +import com.google.android.gms.maps.model.GroundOverlayOptions; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.maps.android.collections.GroundOverlayManager; + +public class MapOverlay extends MapFeature implements ImageReadable { + + private GroundOverlayOptions groundOverlayOptions; + private GroundOverlay groundOverlay; + private LatLngBounds bounds; + private float bearing; + private BitmapDescriptor iconBitmapDescriptor; + private boolean tappable; + private float zIndex; + private float transparency; + + private final ImageReader mImageReader; + private GroundOverlayManager.Collection groundOverlayCollection; + + public MapOverlay(Context context) { + super(context); + this.mImageReader = new ImageReader(context, getResources(), this); + } + + public void setBounds(ReadableArray bounds) { + LatLng sw = new LatLng(bounds.getArray(0).getDouble(0), bounds.getArray(0).getDouble(1)); + LatLng ne = new LatLng(bounds.getArray(1).getDouble(0), bounds.getArray(1).getDouble(1)); + this.bounds = new LatLngBounds(sw, ne); + if (this.groundOverlay != null) { + this.groundOverlay.setPositionFromBounds(this.bounds); + } + } + + public void setBearing(float bearing){ + this.bearing = bearing; + if (this.groundOverlay != null) { + this.groundOverlay.setBearing(bearing); + } + } + + public void setZIndex(float zIndex) { + this.zIndex = zIndex; + if (this.groundOverlay != null) { + this.groundOverlay.setZIndex(zIndex); + } + } + + public void setTransparency(float transparency) { + this.transparency = transparency; + if (groundOverlay != null) { + groundOverlay.setTransparency(transparency); + } + } + + public void setImage(String uri) { + this.mImageReader.setImage(uri); + } + + public void setTappable(boolean tapabble) { + this.tappable = tapabble; + if (groundOverlay != null) { + groundOverlay.setClickable(tappable); + } + } + + + public GroundOverlayOptions getGroundOverlayOptions() { + if (this.groundOverlayOptions == null) { + this.groundOverlayOptions = createGroundOverlayOptions(); + } + return this.groundOverlayOptions; + } + + private GroundOverlayOptions createGroundOverlayOptions() { + if (this.groundOverlayOptions != null) { + return this.groundOverlayOptions; + } + GroundOverlayOptions options = new GroundOverlayOptions(); + if (this.iconBitmapDescriptor != null) { + options.image(iconBitmapDescriptor); + } else { + // add stub image to be able to instantiate the overlay + // and store a reference to it in MapView + options.image(BitmapDescriptorFactory.defaultMarker()); + // hide overlay until real image gets added + options.visible(false); + } + options.positionFromBounds(bounds); + options.zIndex(zIndex); + options.bearing(bearing); + options.transparency(transparency); + return options; + } + + @Override + public Object getFeature() { + return groundOverlay; + } + + @Override + public void addToMap(Object collection) { + GroundOverlayManager.Collection groundOverlayCollection = (GroundOverlayManager.Collection) collection; + GroundOverlayOptions groundOverlayOptions = getGroundOverlayOptions(); + if (groundOverlayOptions != null) { + groundOverlay = groundOverlayCollection.addGroundOverlay(groundOverlayOptions); + groundOverlay.setClickable(this.tappable); + } else { + this.groundOverlayCollection = groundOverlayCollection; + } + } + + @Override + public void removeFromMap(Object collection) { + if (groundOverlay != null) { + GroundOverlayManager.Collection groundOverlayCollection = (GroundOverlayManager.Collection) collection; + groundOverlayCollection.remove(groundOverlay); + groundOverlay = null; + groundOverlayOptions = null; + } + groundOverlayCollection = null; + } + + @Override + public void setIconBitmap(Bitmap bitmap) { + } + + @Override + public void setIconBitmapDescriptor( + BitmapDescriptor iconBitmapDescriptor) { + this.iconBitmapDescriptor = iconBitmapDescriptor; + } + + @Override + public void update() { + this.groundOverlay = getGroundOverlay(); + if (this.groundOverlay != null) { + this.groundOverlay.setVisible(true); + this.groundOverlay.setImage(this.iconBitmapDescriptor); + this.groundOverlay.setTransparency(this.transparency); + this.groundOverlay.setClickable(this.tappable); + } + } + + private GroundOverlay getGroundOverlay() { + if (this.groundOverlay != null) { + return this.groundOverlay; + } + if (this.groundOverlayCollection == null) { + return null; + } + GroundOverlayOptions groundOverlayOptions = getGroundOverlayOptions(); + if (groundOverlayOptions != null) { + return this.groundOverlayCollection.addGroundOverlay(groundOverlayOptions); + } + return null; + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapOverlayManager.java b/android/src/main/java/com/rnmaps/maps/MapOverlayManager.java new file mode 100644 index 000000000..c8f2498a1 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapOverlayManager.java @@ -0,0 +1,75 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.Map; + +public class MapOverlayManager extends ViewGroupManager { + + public MapOverlayManager(ReactApplicationContext reactContext) { + super(); + DisplayMetrics metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapOverlay"; + } + + @Override + public MapOverlay createViewInstance(ThemedReactContext context) { + return new MapOverlay(context); + } + + @ReactProp(name = "bounds") + public void setBounds(MapOverlay view, ReadableArray bounds) { + view.setBounds(bounds); + } + + @ReactProp(name = "bearing") + public void setBearing(MapOverlay view, float bearing){ + view.setBearing(bearing); + } + + @ReactProp(name = "zIndex", defaultFloat = 1.0f) + public void setZIndex(MapOverlay view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "opacity", defaultFloat = 1.0f) + public void setOpacity(MapOverlay view, float opacity) { + view.setTransparency(1 - opacity); + } + + @ReactProp(name = "image") + public void setImage(MapOverlay view, @Nullable String source) { + view.setImage(source); + } + + @ReactProp(name = "tappable", defaultBoolean = false) + public void setTappable(MapOverlay view, boolean tapabble) { + view.setTappable(tapabble); + } + + @Override + @Nullable + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.of( + "onPress", MapBuilder.of("registrationName", "onPress") + ); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapPolygon.java b/android/src/main/java/com/rnmaps/maps/MapPolygon.java new file mode 100644 index 000000000..4dabeef0e --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapPolygon.java @@ -0,0 +1,194 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.google.android.gms.maps.model.Dash; +import com.google.android.gms.maps.model.Gap; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.PatternItem; +import com.google.android.gms.maps.model.Polygon; +import com.google.android.gms.maps.model.PolygonOptions; +import com.google.maps.android.collections.PolygonManager; + +import java.util.ArrayList; +import java.util.List; + +public class MapPolygon extends MapFeature { + + private PolygonOptions polygonOptions; + private Polygon polygon; + + private List coordinates; + private List> holes; + private int strokeColor; + private int fillColor; + private float strokeWidth; + private boolean geodesic; + private boolean tappable; + private float zIndex; + private ReadableArray patternValues; + private List pattern; + + public MapPolygon(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"))); + } + if (polygon != null) { + polygon.setPoints(this.coordinates); + } + } + + public void setHoles(ReadableArray holes) { + if (holes == null) { return; } + + this.holes = new ArrayList<>(holes.size()); + + for (int i = 0; i < holes.size(); i++) { + ReadableArray hole = holes.getArray(i); + + if (hole.size() < 3) { continue; } + + List coordinates = new ArrayList<>(); + for (int j = 0; j < hole.size(); j++) { + ReadableMap coordinate = hole.getMap(j); + coordinates.add(new LatLng( + coordinate.getDouble("latitude"), + coordinate.getDouble("longitude"))); + } + + // If hole is triangle + if (coordinates.size() == 3) { + coordinates.add(coordinates.get(0)); + } + + this.holes.add(coordinates); + } + + if (polygon != null) { + polygon.setHoles(this.holes); + } + } + + + 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 setTappable(boolean tapabble) { + this.tappable = tapabble; + if (polygon != null) { + polygon.setClickable(tappable); + } + } + + public void setGeodesic(boolean geodesic) { + this.geodesic = geodesic; + if (polygon != null) { + polygon.setGeodesic(geodesic); + } + } + + public void setZIndex(float zIndex) { + this.zIndex = zIndex; + if (polygon != null) { + polygon.setZIndex(zIndex); + } + } + + public void setLineDashPattern(ReadableArray patternValues) { + this.patternValues = patternValues; + this.applyPattern(); + } + + private void applyPattern() { + if(patternValues == null) { + return; + } + this.pattern = new ArrayList<>(patternValues.size()); + for (int i = 0; i < patternValues.size(); i++) { + float patternValue = (float) patternValues.getDouble(i); + boolean isGap = i % 2 != 0; + if(isGap) { + this.pattern.add(new Gap(patternValue)); + }else { + PatternItem patternItem; + patternItem = new Dash(patternValue); + this.pattern.add(patternItem); + } + } + if(polygon != null) { + polygon.setStrokePattern(this.pattern); + } + } + + 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); + options.strokePattern(this.pattern); + + if (this.holes != null) { + for (int i = 0; i < holes.size(); i++) { + options.addHole(holes.get(i)); + } + } + + return options; + } + + @Override + public Object getFeature() { + return polygon; + } + + @Override + public void addToMap(Object collection) { + PolygonManager.Collection polygonCollection = (PolygonManager.Collection) collection; + polygon = polygonCollection.addPolygon(getPolygonOptions()); + polygon.setClickable(this.tappable); + } + + @Override + public void removeFromMap(Object collection) { + PolygonManager.Collection polygonCollection = (PolygonManager.Collection) collection; + polygonCollection.remove(polygon); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapPolygonManager.java b/android/src/main/java/com/rnmaps/maps/MapPolygonManager.java new file mode 100644 index 000000000..26c3f78c0 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapPolygonManager.java @@ -0,0 +1,93 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Color; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.Map; + +public class MapPolygonManager extends ViewGroupManager { + private final DisplayMetrics metrics; + + public MapPolygonManager(ReactApplicationContext reactContext) { + super(); + metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapPolygon"; + } + + @Override + public MapPolygon createViewInstance(ThemedReactContext context) { + return new MapPolygon(context); + } + + @ReactProp(name = "coordinates") + public void setCoordinate(MapPolygon view, ReadableArray coordinates) { + view.setCoordinates(coordinates); + } + + @ReactProp(name = "holes") + public void setHoles(MapPolygon view, ReadableArray holes) { + view.setHoles(holes); + } + + @ReactProp(name = "strokeWidth", defaultFloat = 1f) + public void setStrokeWidth(MapPolygon 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(MapPolygon view, int color) { + view.setFillColor(color); + } + + @ReactProp(name = "strokeColor", defaultInt = Color.RED, customType = "Color") + public void setStrokeColor(MapPolygon view, int color) { + view.setStrokeColor(color); + } + + @ReactProp(name = "tappable", defaultBoolean = false) + public void setTappable(MapPolygon view, boolean tapabble) { + view.setTappable(tapabble); + } + + @ReactProp(name = "geodesic", defaultBoolean = false) + public void setGeodesic(MapPolygon view, boolean geodesic) { + view.setGeodesic(geodesic); + } + + @ReactProp(name = "zIndex", defaultFloat = 1.0f) + public void setZIndex(MapPolygon view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "lineDashPattern") + public void setLineDashPattern(MapPolygon view, ReadableArray patternValues) { + view.setLineDashPattern(patternValues); + } + + @Override + @Nullable + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.of( + "onPress", MapBuilder.of("registrationName", "onPress") + ); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapPolyline.java b/android/src/main/java/com/rnmaps/maps/MapPolyline.java new file mode 100644 index 000000000..f008b8869 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapPolyline.java @@ -0,0 +1,164 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; +import com.google.android.gms.maps.model.Cap; +import com.google.android.gms.maps.model.Dash; +import com.google.android.gms.maps.model.Dot; +import com.google.android.gms.maps.model.Gap; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.PatternItem; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.PolylineOptions; +import com.google.android.gms.maps.model.RoundCap; +import com.google.maps.android.collections.PolylineManager; + +import java.util.ArrayList; +import java.util.List; + +public class MapPolyline extends MapFeature { + + private PolylineOptions polylineOptions; + private Polyline polyline; + + private List coordinates; + private int color; + private float width; + private boolean tappable; + private boolean geodesic; + private float zIndex; + private Cap lineCap = new RoundCap(); + private ReadableArray patternValues; + private List pattern; + + public MapPolyline(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); + } + } + + 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 setTappable(boolean tapabble) { + this.tappable = tapabble; + if (polyline != null) { + polyline.setClickable(tappable); + } + } + + public void setGeodesic(boolean geodesic) { + this.geodesic = geodesic; + if (polyline != null) { + polyline.setGeodesic(geodesic); + } + } + + public void setLineCap(Cap cap) { + this.lineCap = cap; + if (polyline != null) { + polyline.setStartCap(cap); + polyline.setEndCap(cap); + } + this.applyPattern(); + } + + public void setLineDashPattern(ReadableArray patternValues) { + this.patternValues = patternValues; + this.applyPattern(); + } + + private void applyPattern() { + if(patternValues == null) { + return; + } + this.pattern = new ArrayList<>(patternValues.size()); + for (int i = 0; i < patternValues.size(); i++) { + float patternValue = (float) patternValues.getDouble(i); + boolean isGap = i % 2 != 0; + if(isGap) { + this.pattern.add(new Gap(patternValue)); + }else { + PatternItem patternItem; + boolean isLineCapRound = this.lineCap instanceof RoundCap; + if(isLineCapRound) { + patternItem = new Dot(); + }else { + patternItem = new Dash(patternValue); + } + this.pattern.add(patternItem); + } + } + if(polyline != null) { + polyline.setPattern(this.pattern); + } + } + + 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); + options.startCap(lineCap); + options.endCap(lineCap); + options.pattern(this.pattern); + return options; + } + + @Override + public Object getFeature() { + return polyline; + } + + @Override + public void addToMap(Object collection) { + PolylineManager.Collection polylineCollection = (PolylineManager.Collection) collection; + polyline = polylineCollection.addPolyline(getPolylineOptions()); + polyline.setClickable(this.tappable); + } + + @Override + public void removeFromMap(Object collection) { + PolylineManager.Collection polylineCollection = (PolylineManager.Collection) collection; + polylineCollection.remove(polyline); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapPolylineManager.java b/android/src/main/java/com/rnmaps/maps/MapPolylineManager.java new file mode 100644 index 000000000..178641704 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapPolylineManager.java @@ -0,0 +1,107 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Color; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import androidx.annotation.Nullable; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; +import com.google.android.gms.maps.model.ButtCap; +import com.google.android.gms.maps.model.Cap; +import com.google.android.gms.maps.model.RoundCap; +import com.google.android.gms.maps.model.SquareCap; + +import java.util.Map; + +public class MapPolylineManager extends ViewGroupManager { + private final DisplayMetrics metrics; + + public MapPolylineManager(ReactApplicationContext reactContext) { + super(); + metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapPolyline"; + } + + @Override + public MapPolyline createViewInstance(ThemedReactContext context) { + return new MapPolyline(context); + } + + @ReactProp(name = "coordinates") + public void setCoordinate(MapPolyline view, ReadableArray coordinates) { + view.setCoordinates(coordinates); + } + + @ReactProp(name = "strokeWidth", defaultFloat = 1f) + public void setStrokeWidth(MapPolyline 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(MapPolyline view, int color) { + view.setColor(color); + } + + @ReactProp(name = "tappable", defaultBoolean = false) + public void setTappable(MapPolyline view, boolean tapabble) { + view.setTappable(tapabble); + } + + @ReactProp(name = "geodesic", defaultBoolean = false) + public void setGeodesic(MapPolyline view, boolean geodesic) { + view.setGeodesic(geodesic); + } + + @ReactProp(name = "zIndex", defaultFloat = 1.0f) + public void setZIndex(MapPolyline view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "lineCap") + public void setlineCap(MapPolyline view, String lineCap) { + Cap cap = null; + switch (lineCap) { + case "butt": + cap = new ButtCap(); + break; + case "round": + cap = new RoundCap(); + break; + case "square": + cap = new SquareCap(); + break; + default: + cap = new RoundCap(); + break; + } + view.setLineCap(cap); + } + + @ReactProp(name = "lineDashPattern") + public void setLineDashPattern(MapPolyline view, ReadableArray patternValues) { + view.setLineDashPattern(patternValues); + } + + @Override + @Nullable + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.of( + "onPress", MapBuilder.of("registrationName", "onPress") + ); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapTileProvider.java b/android/src/main/java/com/rnmaps/maps/MapTileProvider.java new file mode 100644 index 000000000..a974311c2 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapTileProvider.java @@ -0,0 +1,494 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import android.util.Log; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Future; +import java.util.List; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Rect; + +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; +import androidx.work.Data; +import androidx.work.Constraints; +import androidx.work.NetworkType; +import androidx.work.ExistingWorkPolicy; +import androidx.work.Operation; +import androidx.work.WorkInfo; + +import com.google.android.gms.maps.model.Tile; +import com.google.android.gms.maps.model.TileProvider; +import com.google.android.gms.maps.model.UrlTileProvider; + +import java.lang.System; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +public class MapTileProvider implements TileProvider { + + class AIRMapUrlTileProvider extends UrlTileProvider { + private String urlTemplate; + + public AIRMapUrlTileProvider(int width, int height, String urlTemplate) { + super(width, height); + this.urlTemplate = urlTemplate; + } + + @Override + public URL getTileUrl(int x, int y, int zoom) { + + if (MapTileProvider.this.flipY) { + y = (1 << zoom) - y - 1; + } + + String s = this.urlTemplate + .replace("{x}", Integer.toString(x)) + .replace("{y}", Integer.toString(y)) + .replace("{z}", Integer.toString(zoom)); + URL url; + + if(MapTileProvider.this.maximumZ > 0 && zoom > MapTileProvider.this.maximumZ) { + return null; + } + + if(MapTileProvider.this.minimumZ > 0 && zoom < MapTileProvider.this.minimumZ) { + return null; + } + + try { + url = new URL(s); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + return url; + } + + public void setUrlTemplate(String urlTemplate) { + this.urlTemplate = urlTemplate; + } + } + + protected static final int BUFFER_SIZE = 16 * 1024; + protected static final int TARGET_TILE_SIZE = 512; + protected UrlTileProvider tileProvider; + protected String urlTemplate; + protected int tileSize; + protected boolean doubleTileSize; + protected int maximumZ; + protected int maximumNativeZ; + protected int minimumZ; + protected boolean flipY; + protected String tileCachePath; + protected int tileCacheMaxAge; + protected boolean offlineMode; + protected Context context; + protected boolean customMode; + + public MapTileProvider(int tileSizet, boolean doubleTileSize, String urlTemplate, + int maximumZ, int maximumNativeZ, int minimumZ, boolean flipY, String tileCachePath, + int tileCacheMaxAge, boolean offlineMode, Context context, boolean customMode) { + this.tileProvider = new AIRMapUrlTileProvider(tileSizet, tileSizet, urlTemplate); + + this.tileSize = tileSizet; + this.doubleTileSize = doubleTileSize; + this.urlTemplate = urlTemplate; + this.maximumZ = maximumZ; + this.maximumNativeZ = maximumNativeZ; + this.minimumZ = minimumZ; + this.flipY = flipY; + this.tileCachePath = tileCachePath; + this.tileCacheMaxAge = tileCacheMaxAge; + this.offlineMode = offlineMode; + this.context = context; + this.customMode = customMode; + } + + @Override + public Tile getTile(int x, int y, int zoom) { + if (!this.customMode) return this.tileProvider.getTile(x, y, zoom); + + byte[] image = null; + int maximumZ = this.maximumZ > 0 ? this.maximumZ : Integer.MAX_VALUE; + + if (this.tileSize == 256 && this.doubleTileSize && zoom + 1 <= this.maximumNativeZ && zoom + 1 <= maximumZ) { + Log.d("urlTile", "pullTilesFromHigherZoom"); + image = pullTilesFromHigherZoom(x, y, zoom); + } + + if (zoom > this.maximumNativeZ) { + Log.d("urlTile", "scaleLowerZoomTile"); + image = scaleLowerZoomTile(x, y, zoom, this.maximumNativeZ); + } + + if (image == null && zoom <= maximumZ) { + Log.d("urlTile", "getTileImage"); + image = getTileImage(x, y, zoom); + } + + if (image == null && this.tileCachePath != null && this.offlineMode) { + Log.d("urlTile", "findLowerZoomTileForScaling"); + int zoomLevelToStart = (zoom > this.maximumNativeZ) ? this.maximumNativeZ - 1 : zoom - 1; + int minimumZoomToSearch = Math.max(this.minimumZ, zoom - 3); + for (int tryZoom = zoomLevelToStart; tryZoom >= minimumZoomToSearch; tryZoom--) { + image = scaleLowerZoomTile(x, y, zoom, tryZoom); + if (image != null) { + break; + } + } + } + + return image == null ? null : new Tile(this.tileSize, this.tileSize, image); + } + + byte[] getTileImage(int x, int y, int zoom) { + byte[] image = null; + + if (this.tileCachePath != null) { + image = readTileImage(x, y, zoom); + if (image != null) { + Log.d("urlTile", "tile cache HIT for " + zoom + + "/" + x + "/" + y); + } else { + Log.d("urlTile", "tile cache MISS for " + zoom + + "/" + x + "/" + y); + } + if (image != null && !this.offlineMode) { + checkForRefresh(x, y, zoom); + } + } + + if (image == null && !this.offlineMode && this.tileCachePath != null) { + String fileName = getTileFilename(x, y, zoom); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest tileRefreshWorkRequest = new OneTimeWorkRequest.Builder(MapTileWorker.class) + .setConstraints(constraints) + .addTag(fileName) + .setInputData( + new Data.Builder() + .putString("url", getTileUrl(x, y, zoom).toString()) + .putString("filename", fileName) + .putInt("maxAge", -1) + .build() + ) + .build(); + WorkManager workManager = WorkManager.getInstance(this.context.getApplicationContext()); + Operation fetchOperation = workManager + .enqueueUniqueWork(fileName, ExistingWorkPolicy.KEEP, tileRefreshWorkRequest); + Future operationFuture = fetchOperation.getResult(); + try { + operationFuture.get(1L, TimeUnit.SECONDS); + Thread.sleep(500); + Future> fetchFuture = workManager.getWorkInfosByTag(fileName); + List workInfo = fetchFuture.get(1L, TimeUnit.SECONDS); + Log.d("urlTile: ", workInfo.get(0).toString()); + if (this.tileCachePath != null) { + image = readTileImage(x, y, zoom); + if (image != null) { + Log.d("urlTile","tile cache fetch HIT for " + zoom + + "/" + x + "/" + y); + } else { + Log.d("urlTile","tile cache fetch MISS for " + zoom + + "/" + x + "/" + y); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } else if (image == null && !this.offlineMode) { + Log.d("urlTile", "Normal fetch"); + image = fetchTile(x, y, zoom); + if (image == null) { + Log.d("urlTile", "tile fetch TIMEOUT / FAIL for " + zoom + + "/" + x + "/" + y); + } + } + + return image; + } + + byte[] pullTilesFromHigherZoom(int x, int y, int zoom) { + byte[] data; + Bitmap image = getNewBitmap(); + Canvas canvas = new Canvas(image); + Paint paint = new Paint(); + + x = x * 2; + y = y * 2; + byte[] leftTop = getTileImage(x, y, zoom + 1); + byte[] leftBottom = getTileImage(x, y + 1, zoom + 1); + byte[] rightTop = getTileImage(x + 1, y, zoom + 1); + byte[] rightBottom = getTileImage(x + 1, y + 1, zoom + 1); + + if (leftTop == null || leftBottom == null || rightTop == null || rightBottom == null) { + return null; + } + + Bitmap bitmap; + + bitmap = BitmapFactory.decodeByteArray(leftTop, 0, leftTop.length); + canvas.drawBitmap(bitmap, 0, 0, paint); + bitmap.recycle(); + + bitmap = BitmapFactory.decodeByteArray(leftBottom, 0, leftBottom.length); + canvas.drawBitmap(bitmap, 0, 256, paint); + bitmap.recycle(); + + bitmap = BitmapFactory.decodeByteArray(rightTop, 0, rightTop.length); + canvas.drawBitmap(bitmap, 256, 0, paint); + bitmap.recycle(); + + bitmap = BitmapFactory.decodeByteArray(rightBottom, 0, rightBottom.length); + canvas.drawBitmap(bitmap, 256, 256, paint); + bitmap.recycle(); + + data = bitmapToByteArray(image); + image.recycle(); + return data; + } + + Bitmap getNewBitmap() { + Bitmap image = Bitmap.createBitmap(TARGET_TILE_SIZE, TARGET_TILE_SIZE, Bitmap.Config.ARGB_8888); + image.eraseColor(Color.TRANSPARENT); + return image; + } + + byte[] bitmapToByteArray(Bitmap bm) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bm.compress(Bitmap.CompressFormat.PNG, 100, bos); + + byte[] data = bos.toByteArray(); + try { + bos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + return data; + } + + byte[] scaleLowerZoomTile(int x, int y, int zoom, int maximumZoom) { + int overZoomLevel = zoom - maximumZoom; + int zoomFactor = 1 << overZoomLevel; + + int xParent = x >> overZoomLevel; + int yParent = y >> overZoomLevel; + int zoomParent = zoom - overZoomLevel; + + int xOffset = x % zoomFactor; + int yOffset = y % zoomFactor; + + byte[] data; + Bitmap image = getNewBitmap(); + Canvas canvas = new Canvas(image); + Paint paint = new Paint(); + + data = getTileImage(xParent, yParent, zoomParent); + if (data == null) return null; + + Bitmap sourceImage; + sourceImage = BitmapFactory.decodeByteArray(data, 0, data.length); + + int subTileSize = this.tileSize / zoomFactor; + Rect sourceRect = new Rect(xOffset * subTileSize, yOffset * subTileSize, xOffset * subTileSize + subTileSize , yOffset * subTileSize + subTileSize); + Rect targetRect = new Rect(0,0,TARGET_TILE_SIZE, TARGET_TILE_SIZE); + canvas.drawBitmap(sourceImage, sourceRect, targetRect, paint); + sourceImage.recycle(); + + data = bitmapToByteArray(image); + image.recycle(); + return data; + } + + void checkForRefresh(int x, int y, int zoom) { + String fileName = getTileFilename(x, y, zoom); + File file = new File(fileName); + long lastModified = file.lastModified(); + long now = System.currentTimeMillis(); + + if ((now - lastModified) / 1000 > this.tileCacheMaxAge) { + Log.d("urlTile", "Refreshing"); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); + OneTimeWorkRequest tileRefreshWorkRequest = new OneTimeWorkRequest.Builder(MapTileWorker.class) + .setConstraints(constraints) + .addTag(fileName) + .setInputData( + new Data.Builder() + .putString("url", getTileUrl(x, y, zoom).toString()) + .putString("filename", fileName) + .putInt("maxAge", this.tileCacheMaxAge) + .build() + ) + .build(); + WorkManager.getInstance(this.context.getApplicationContext()) + .enqueueUniqueWork(fileName, ExistingWorkPolicy.KEEP, tileRefreshWorkRequest); + } + } + + byte[] fetchTile(int x, int y, int zoom) { + URL url = getTileUrl(x, y, zoom); + ByteArrayOutputStream buffer = null; + InputStream in = null; + + try { + URLConnection conn = url.openConnection(); + in = conn.getInputStream(); + buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[BUFFER_SIZE]; + + while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + + return buffer.toByteArray(); + } catch (IOException | OutOfMemoryError e) { + e.printStackTrace(); + return null; + } finally { + if (in != null) try { in.close(); } catch (Exception ignored) {} + if (buffer != null) try { buffer.close(); } catch (Exception ignored) {} + } + } + + byte[] readTileImage(int x, int y, int zoom) { + InputStream in = null; + ByteArrayOutputStream buffer = null; + String fileName = getTileFilename(x, y, zoom); + if (fileName == null) { + return null; + } + + File file = new File(fileName); + + try { + in = new FileInputStream(file); + buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[BUFFER_SIZE]; + + while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + + if (this.tileCacheMaxAge == 0) { + file.setLastModified(System.currentTimeMillis()); + } + + return buffer.toByteArray(); + } catch (IOException | OutOfMemoryError e) { + e.printStackTrace(); + return null; + } finally { + if (in != null) try { in.close(); } catch (Exception ignored) {} + if (buffer != null) try { buffer.close(); } catch (Exception ignored) {} + } + } + + boolean writeTileImage(byte[] image, int x, int y, int zoom) { + OutputStream out = null; + String fileName = getTileFilename(x, y, zoom); + if (fileName == null) { + return false; + } + + try { + File file = new File(fileName); + file.getParentFile().mkdirs(); + out = new FileOutputStream(file); + out.write(image); + + return true; + } catch (IOException | OutOfMemoryError e) { + e.printStackTrace(); + return false; + } finally { + if (out != null) try { out.close(); } catch (Exception ignored) {} + } + } + + String getTileFilename(int x, int y, int zoom) { + if (this.tileCachePath == null) { + return null; + } + return this.tileCachePath + '/' + zoom + + "/" + x + "/" + y; + } + + protected URL getTileUrl(int x, int y, int zoom) { + return this.tileProvider.getTileUrl(x, y, zoom); + } + + public void setUrlTemplate(String urlTemplate) { + if (this.urlTemplate != urlTemplate) { + this.tileProvider = new AIRMapUrlTileProvider(tileSize, tileSize, urlTemplate); + } + + this.urlTemplate = urlTemplate; + } + + public void setTileSize(int tileSize) { + if (this.tileSize != tileSize) { + this.tileProvider = new AIRMapUrlTileProvider(tileSize, tileSize, urlTemplate); + } + this.tileSize = tileSize; + } + + public void setDoubleTileSize(boolean doubleTileSize) { + this.doubleTileSize = doubleTileSize; + } + + public void setMaximumZ(int maximumZ) { + this.maximumZ = maximumZ; + } + + public void setMaximumNativeZ(int maximumNativeZ) { + this.maximumNativeZ = maximumNativeZ; + } + + public void setMinimumZ(int minimumZ) { + this.minimumZ = minimumZ; + } + + public void setFlipY(boolean flipY) { + this.flipY = flipY; + } + + public void setTileCachePath(String tileCachePath) { + this.tileCachePath = tileCachePath; + } + + public void setTileCacheMaxAge(int tileCacheMaxAge) { + this.tileCacheMaxAge = tileCacheMaxAge; + } + + public void setOfflineMode(boolean offlineMode) { + this.offlineMode = offlineMode; + } + + public void setCustomMode() { + } +} \ No newline at end of file diff --git a/android/src/main/java/com/rnmaps/maps/MapTileWorker.java b/android/src/main/java/com/rnmaps/maps/MapTileWorker.java new file mode 100644 index 000000000..ed94cb59d --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapTileWorker.java @@ -0,0 +1,115 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +import java.net.MalformedURLException; +import java.net.URL; + +public class MapTileWorker extends Worker { + private static final int BUFFER_SIZE = 16 * 1024; + + public MapTileWorker( + @NonNull Context context, + @NonNull WorkerParameters params) { + super(context, params); + } + + @Override + public Result doWork() { + byte[] image; + URL url; + String fileName = getInputData().getString("filename"); + + try { + int tileCacheMaxAge = getInputData().getInt("maxAge", 0); + if (tileCacheMaxAge >= 0) { + File file = new File(fileName); + long lastModified = file.lastModified(); + long now = System.currentTimeMillis(); + if ((now - lastModified) / 1000 < tileCacheMaxAge) return Result.failure(); + } + } catch (Error e) { + return Result.failure(); + } + + try { + url = new URL(getInputData().getString("url")); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + + image = fetchTile(url); + if (image != null) { + boolean success = writeTileImage(image, fileName); + if (!success) { + return Result.failure(); + } + } else { + return Result.retry(); + } + + // Indicate whether the work finished successfully with the Result + Log.d("urlTile", "Worker fetched " + fileName); + return Result.success(); + } + + private byte[] fetchTile(URL url) { + ByteArrayOutputStream buffer = null; + InputStream in = null; + + try { + in = url.openStream(); + buffer = new ByteArrayOutputStream(); + + int nRead; + byte[] data = new byte[BUFFER_SIZE]; + + while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + + return buffer.toByteArray(); + } catch (IOException | OutOfMemoryError e) { + e.printStackTrace(); + return null; + } finally { + if (in != null) try { in.close(); } catch (Exception ignored) {} + if (buffer != null) try { buffer.close(); } catch (Exception ignored) {} + } + } + + private boolean writeTileImage(byte[] image, String fileName) { + OutputStream out = null; + if (fileName == null) { + return false; + } + + try { + File file = new File(fileName); + file.getParentFile().mkdirs(); + out = new FileOutputStream(file); + out.write(image); + + return true; + } catch (IOException | OutOfMemoryError e) { + e.printStackTrace(); + return false; + } finally { + if (out != null) try { out.close(); } catch (Exception ignored) {} + } + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapUIBlock.java b/android/src/main/java/com/rnmaps/maps/MapUIBlock.java new file mode 100644 index 000000000..cceab1a31 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapUIBlock.java @@ -0,0 +1,64 @@ +package com.rnmaps.maps; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.UIManager; +import com.facebook.react.fabric.FabricUIManager; +import com.facebook.react.fabric.interop.UIBlockViewResolver; +import com.facebook.react.uimanager.common.UIManagerType; +import com.facebook.react.uimanager.NativeViewHierarchyManager; +import com.facebook.react.uimanager.UIBlock; +import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.UIManagerModule; + +import java.util.function.Function; + +public class MapUIBlock implements UIBlockInterface { + private int tag; + private Promise promise; + private ReactApplicationContext context; + private Function mapOperation; + + public MapUIBlock(int tag, Promise promise, ReactApplicationContext context, Function mapOperation) { + this.tag = tag; + this.promise = promise; + this.context = context; + this.mapOperation = mapOperation; + } + + @Override + public void execute(NativeViewHierarchyManager nvhm) { + executeImpl(nvhm, null); + } + + @Override + public void execute(UIBlockViewResolver uiBlockViewResolver) { + executeImpl(null, uiBlockViewResolver); + } + + private void executeImpl(NativeViewHierarchyManager nvhm, UIBlockViewResolver uiBlockViewResolver) { + MapView view = uiBlockViewResolver != null ? (MapView) uiBlockViewResolver.resolveView(tag) : (MapView) nvhm.resolveView(tag); + if (view == null) { + promise.reject("AirMapView not found"); + return; + } + if (view.map == null) { + promise.reject("AirMapView.map is not valid"); + return; + } + + mapOperation.apply(view); + } + + public void addToUIManager() { + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + UIManager uiManager = UIManagerHelper.getUIManager(context, UIManagerType.FABRIC); + ((FabricUIManager) uiManager).addUIBlock(this); + } else { + UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class); + uiManager.addUIBlock(this); + } + } +} + +interface UIBlockInterface extends UIBlock, com.facebook.react.fabric.interop.UIBlock {} diff --git a/android/src/main/java/com/rnmaps/maps/MapUrlTile.java b/android/src/main/java/com/rnmaps/maps/MapUrlTile.java new file mode 100644 index 000000000..968d58ab2 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapUrlTile.java @@ -0,0 +1,207 @@ +package com.rnmaps.maps; + +import android.util.Log; + +import android.content.Context; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.TileOverlay; +import com.google.android.gms.maps.model.TileOverlayOptions; + +import java.net.MalformedURLException; +import java.net.URL; + +public class MapUrlTile extends MapFeature { + protected TileOverlayOptions tileOverlayOptions; + protected TileOverlay tileOverlay; + protected MapTileProvider tileProvider; + + protected String urlTemplate; + protected float zIndex; + protected float maximumZ; + protected float maximumNativeZ = 100; + protected float minimumZ; + protected boolean flipY = false; + protected float tileSize = 256; + protected boolean doubleTileSize = false; + protected String tileCachePath; + protected float tileCacheMaxAge; + protected boolean offlineMode = false; + protected float opacity = 1; + protected Context context; + protected boolean customTileProviderNeeded = false; + + public MapUrlTile(Context context) { + super(context); + this.context = context; + } + + public void setUrlTemplate(String urlTemplate) { + this.urlTemplate = urlTemplate; + if (tileProvider != null) { + tileProvider.setUrlTemplate(urlTemplate); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setZIndex(float zIndex) { + this.zIndex = zIndex; + if (tileOverlay != null) { + tileOverlay.setZIndex(zIndex); + } + } + + public void setMaximumZ(float maximumZ) { + this.maximumZ = maximumZ; + if (tileProvider != null) { + tileProvider.setMaximumZ((int)maximumZ); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setMaximumNativeZ(float maximumNativeZ) { + this.maximumNativeZ = maximumNativeZ; + if (tileProvider != null) { + tileProvider.setMaximumNativeZ((int)maximumNativeZ); + } + setCustomTileProviderMode(); + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setMinimumZ(float minimumZ) { + this.minimumZ = minimumZ; + if (tileProvider != null) { + tileProvider.setMinimumZ((int)minimumZ); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setFlipY(boolean flipY) { + this.flipY = flipY; + if (tileProvider != null) { + tileProvider.setFlipY(flipY); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setDoubleTileSize(boolean doubleTileSize) { + this.doubleTileSize = doubleTileSize; + if (tileProvider != null) { + tileProvider.setDoubleTileSize(doubleTileSize); + } + setCustomTileProviderMode(); + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setTileSize(float tileSize) { + this.tileSize = tileSize; + if (tileProvider != null) { + tileProvider.setTileSize((int)tileSize); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setTileCachePath(String tileCachePath) { + if (tileCachePath == null || tileCachePath.isEmpty()) return; + + try { + URL url = new URL(tileCachePath); + this.tileCachePath = url.getPath(); + } catch (MalformedURLException e) { + this.tileCachePath = tileCachePath; + } catch (Exception e) { + return; + } + + if (tileProvider != null) { + tileProvider.setTileCachePath(tileCachePath); + } + setCustomTileProviderMode(); + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setTileCacheMaxAge(float tileCacheMaxAge) { + this.tileCacheMaxAge = tileCacheMaxAge; + if (tileProvider != null) { + tileProvider.setTileCacheMaxAge((int)tileCacheMaxAge); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setOfflineMode(boolean offlineMode) { + this.offlineMode = offlineMode; + if (tileProvider != null) { + tileProvider.setOfflineMode(offlineMode); + } + if (tileOverlay != null) { + tileOverlay.clearTileCache(); + } + } + + public void setOpacity(float opacity) { + this.opacity = opacity; + if (tileOverlay != null) { + tileOverlay.setTransparency(1 - opacity); + } + } + + public TileOverlayOptions getTileOverlayOptions() { + if (tileOverlayOptions == null) { + tileOverlayOptions = createTileOverlayOptions(); + } + return tileOverlayOptions; + } + + protected void setCustomTileProviderMode() { + Log.d("urlTile ", "creating new mode TileProvider"); + this.customTileProviderNeeded = true; + if (tileProvider != null) { + tileProvider.setCustomMode(); + } + } + + protected TileOverlayOptions createTileOverlayOptions() { + Log.d("urlTile ", "creating TileProvider"); + TileOverlayOptions options = new TileOverlayOptions(); + options.zIndex(zIndex); + options.transparency(1 - this.opacity); + this.tileProvider = new MapTileProvider((int)this.tileSize, this.doubleTileSize, this.urlTemplate, + (int)this.maximumZ, (int)this.maximumNativeZ, (int)this.minimumZ, this.flipY, this.tileCachePath, + (int)this.tileCacheMaxAge, this.offlineMode, this.context, this.customTileProviderNeeded); + options.tileProvider(this.tileProvider); + return options; + } + + @Override + public Object getFeature() { + return tileOverlay; + } + + @Override + public void addToMap(Object map) { + this.tileOverlay = ((GoogleMap) map).addTileOverlay(getTileOverlayOptions()); + } + + @Override + public void removeFromMap(Object map) { + tileOverlay.remove(); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapUrlTileManager.java b/android/src/main/java/com/rnmaps/maps/MapUrlTileManager.java new file mode 100644 index 000000000..2f1c573be --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapUrlTileManager.java @@ -0,0 +1,91 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +public class MapUrlTileManager extends ViewGroupManager { + + public MapUrlTileManager(ReactApplicationContext reactContext) { + super(); + DisplayMetrics metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapUrlTile"; + } + + @Override + public MapUrlTile createViewInstance(ThemedReactContext context) { + return new MapUrlTile(context); + } + + @ReactProp(name = "urlTemplate") + public void setUrlTemplate(MapUrlTile view, String urlTemplate) { + view.setUrlTemplate(urlTemplate); + } + + @ReactProp(name = "zIndex", defaultFloat = -1.0f) + public void setZIndex(MapUrlTile view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "minimumZ", defaultFloat = 0.0f) + public void setMinimumZ(MapUrlTile view, float minimumZ) { + view.setMinimumZ(minimumZ); + } + + @ReactProp(name = "maximumZ", defaultFloat = 100.0f) + public void setMaximumZ(MapUrlTile view, float maximumZ) { + view.setMaximumZ(maximumZ); + } + + @ReactProp(name = "maximumNativeZ", defaultFloat = 100.0f) + public void setMaximumNativeZ(MapUrlTile view, float maximumNativeZ) { + view.setMaximumNativeZ(maximumNativeZ); + } + + @ReactProp(name = "flipY", defaultBoolean = false) + public void setFlipY(MapUrlTile view, boolean flipY) { + view.setFlipY(flipY); + } + + @ReactProp(name = "tileSize", defaultFloat = 256.0f) + public void setTileSize(MapUrlTile view, float tileSize) { + view.setTileSize(tileSize); + } + + @ReactProp(name = "doubleTileSize", defaultBoolean = false) + public void setDoubleTileSize(MapUrlTile view, boolean doubleTileSize) { + view.setDoubleTileSize(doubleTileSize); + } + + @ReactProp(name = "tileCachePath") + public void setTileCachePath(MapUrlTile view, String tileCachePath) { + view.setTileCachePath(tileCachePath); + } + + @ReactProp(name = "tileCacheMaxAge", defaultFloat = 0.0f) + public void setTileCacheMaxAge(MapUrlTile view, float tileCacheMaxAge) { + view.setTileCacheMaxAge(tileCacheMaxAge); + } + + @ReactProp(name = "offlineMode", defaultBoolean = false) + public void setOfflineMode(MapUrlTile view, boolean offlineMode) { + view.setOfflineMode(offlineMode); + } + + @ReactProp(name = "opacity", defaultFloat = 1.0f) + public void setOpacity(MapUrlTile view, float opacity) { + view.setOpacity(opacity); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapView.java b/android/src/main/java/com/rnmaps/maps/MapView.java new file mode 100644 index 000000000..a193a4753 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapView.java @@ -0,0 +1,1519 @@ +package com.rnmaps.maps; + +import static androidx.core.content.PermissionChecker.checkSelfPermission; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Point; +import android.location.Location; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.PermissionChecker; +import androidx.core.view.GestureDetectorCompat; +import androidx.core.view.MotionEventCompat; + +import com.facebook.react.bridge.Arguments; +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.WritableArray; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeArray; +import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.UIManagerHelper; +import com.facebook.react.uimanager.common.UIManagerType; +import com.facebook.react.uimanager.events.EventDispatcher; +import com.google.android.gms.maps.CameraUpdate; +import com.google.android.gms.maps.CameraUpdateFactory; +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.GoogleMapOptions; +import com.google.android.gms.maps.MapsInitializer; +import com.google.android.gms.maps.OnMapReadyCallback; +import com.google.android.gms.maps.Projection; +import com.google.android.gms.maps.model.BitmapDescriptorFactory; +import com.google.android.gms.maps.model.CameraPosition; +import com.google.android.gms.maps.model.GroundOverlay; +import com.google.android.gms.maps.model.IndoorBuilding; +import com.google.android.gms.maps.model.IndoorLevel; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; +import com.google.android.gms.maps.model.MapStyleOptions; +import com.google.android.gms.maps.model.Marker; +import com.google.android.gms.maps.model.MarkerOptions; +import com.google.android.gms.maps.model.PointOfInterest; +import com.google.android.gms.maps.model.Polygon; +import com.google.android.gms.maps.model.Polyline; +import com.google.android.gms.maps.model.TileOverlay; +import com.google.maps.android.collections.CircleManager; +import com.google.maps.android.collections.GroundOverlayManager; +import com.google.maps.android.collections.MarkerManager; +import com.google.maps.android.collections.PolygonManager; +import com.google.maps.android.collections.PolylineManager; +import com.google.maps.android.data.kml.KmlContainer; +import com.google.maps.android.data.kml.KmlLayer; +import com.google.maps.android.data.kml.KmlPlacemark; +import com.google.maps.android.data.kml.KmlStyle; + +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +public class MapView extends com.google.android.gms.maps.MapView implements GoogleMap.InfoWindowAdapter, + GoogleMap.OnMarkerDragListener, OnMapReadyCallback, GoogleMap.OnPoiClickListener, GoogleMap.OnIndoorStateChangeListener { + public GoogleMap map; + private MarkerManager markerManager; + private MarkerManager.Collection markerCollection; + private PolylineManager polylineManager; + private PolylineManager.Collection polylineCollection; + private PolygonManager polygonManager; + private PolygonManager.Collection polygonCollection; + private CircleManager.Collection circleCollection; + private GroundOverlayManager groundOverlayManager; + private GroundOverlayManager.Collection groundOverlayCollection; + private ProgressBar mapLoadingProgressBar; + private RelativeLayout mapLoadingLayout; + private ImageView cacheImageView; + private Boolean isMapLoaded = false; + private Integer loadingBackgroundColor = null; + private Integer loadingIndicatorColor = null; + + private LatLngBounds boundsToMove; + private CameraUpdate cameraToSet; + private boolean setPaddingDeferred = false; + private boolean showUserLocation = false; + private boolean handlePanDrag = false; + private boolean moveOnMarkerPress = true; + private boolean cacheEnabled = false; + private boolean poiClickEnabled = true; + + private ReadableMap initialRegion; + private ReadableMap region; + private ReadableMap initialCamera; + private ReadableMap camera; + private String customMapStyleString; + private boolean initialRegionSet = false; + private boolean initialCameraSet = false; + private LatLngBounds cameraLastIdleBounds; + private int cameraMoveReason = 0; + private MapMarker selectedMarker; + + 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 Map overlayMap = new HashMap<>(); + private final Map heatmapMap = new HashMap<>(); + private final Map gradientPolylineMap = new HashMap<>(); + private final GestureDetectorCompat gestureDetector; + private final MapManager manager; + private LifecycleEventListener lifecycleListener; + private boolean paused = false; + private boolean destroyed = false; + private final ThemedReactContext context; + private final EventDispatcher eventDispatcher; + private final FusedLocationSource fusedLocationSource; + + private final ViewAttacherGroup attacherGroup; + private LatLng tapLocation; + + 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/react-native-maps/react-native-maps/issues/271 + // + // which conflicts with another bug regarding the passed in context: + // https://github.com/react-native-maps/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(); + } + + } + return superContext; + } + + public MapView(ThemedReactContext reactContext, ReactApplicationContext appContext, + MapManager manager, + GoogleMapOptions googleMapOptions) { + super(getNonBuggyContext(reactContext, appContext), googleMapOptions); + + this.manager = manager; + this.context = reactContext; + MapsInitializer.initialize(context, this.manager.renderer, renderer -> Log.d("AirMapRenderer", renderer.toString())); + super.onCreate(null); + super.onResume(); + super.getMapAsync(this); + + final MapView view = this; + + fusedLocationSource = new FusedLocationSource(context); + + gestureDetector = + new GestureDetectorCompat(reactContext, new GestureDetector.SimpleOnGestureListener() { + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + if (handlePanDrag) { + onPanDrag(e2); + } + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent ev) { + onDoublePress(ev); + 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) { + MapView.this.cacheView(); + } + } + }); + + int uiManagerType = UIManagerType.DEFAULT; + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + uiManagerType = UIManagerType.FABRIC; + } + + eventDispatcher = UIManagerHelper + .getUIManager(reactContext, uiManagerType) + .getEventDispatcher(); + + // Set up a parent view for triggering visibility in subviews that depend on it. + // Mainly ReactImageView depends on Fresco which depends on onVisibilityChanged() event + attacherGroup = new ViewAttacherGroup(context); + LayoutParams attacherLayoutParams = new LayoutParams(0, 0); + attacherLayoutParams.width = 0; + attacherLayoutParams.height = 0; + attacherLayoutParams.leftMargin = 99999999; + attacherLayoutParams.topMargin = 99999999; + attacherGroup.setLayoutParams(attacherLayoutParams); + addView(attacherGroup); + } + + @Override + public void onMapReady(@NonNull final GoogleMap map) { + if (destroyed) { + return; + } + this.map = map; + + markerManager = new MarkerManager(map); + markerCollection = markerManager.newCollection(); + polylineManager = new PolylineManager(map); + polylineCollection = polylineManager.newCollection(); + polygonManager = new PolygonManager(map); + polygonCollection = polygonManager.newCollection(); + CircleManager circleManager = new CircleManager(map); + circleCollection = circleManager.newCollection(); + groundOverlayManager = new GroundOverlayManager(map); + groundOverlayCollection = groundOverlayManager.newCollection(); + + markerCollection.setInfoWindowAdapter(this); + markerCollection.setOnMarkerDragListener(this); + this.map.setOnIndoorStateChangeListener(this); + + applyBridgedProps(); + + manager.pushEvent(context, this, "onMapReady", new WritableNativeMap()); + + final MapView view = this; + + map.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() { + @Override + public void onMyLocationChange(Location location){ + WritableMap event = new WritableNativeMap(); + + WritableMap coordinate = new WritableNativeMap(); + coordinate.putDouble("latitude", location.getLatitude()); + coordinate.putDouble("longitude", location.getLongitude()); + coordinate.putDouble("altitude", location.getAltitude()); + coordinate.putDouble("timestamp", location.getTime()); + coordinate.putDouble("accuracy", location.getAccuracy()); + coordinate.putDouble("speed", location.getSpeed()); + coordinate.putDouble("heading", location.getBearing()); + coordinate.putBoolean("isFromMockProvider", location.isFromMockProvider()); + + event.putMap("coordinate", coordinate); + + manager.pushEvent(context, view, "onUserLocationChange", event); + } + }); + + markerCollection.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() { + @Override + public boolean onMarkerClick(@NonNull Marker marker) { + MapMarker airMapMarker = getMarkerMap(marker); + + WritableMap 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, airMapMarker, "onPress", event); + + handleMarkerSelection(airMapMarker); + + // 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; + } + } + }); + + polygonCollection.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() { + @Override + public void onPolygonClick(@NonNull Polygon polygon) { + WritableMap event = makeClickEventData(tapLocation); + event.putString("action", "polygon-press"); + manager.pushEvent(context, polygonMap.get(polygon), "onPress", event); + } + }); + + polylineCollection.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() { + @Override + public void onPolylineClick(@NonNull Polyline polyline) { + WritableMap event = makeClickEventData(tapLocation); + event.putString("action", "polyline-press"); + manager.pushEvent(context, polylineMap.get(polyline), "onPress", event); + } + }); + + markerCollection.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { + @Override + public void onInfoWindowClick(@NonNull Marker marker) { + WritableMap event = makeClickEventData(marker.getPosition()); + event.putString("action", "callout-press"); + manager.pushEvent(context, view, "onCalloutPress", event); + + event = makeClickEventData(marker.getPosition()); + event.putString("action", "callout-press"); + MapMarker markerView = getMarkerMap(marker); + manager.pushEvent(context, markerView, "onCalloutPress", event); + + event = makeClickEventData(marker.getPosition()); + event.putString("action", "callout-press"); + MapCallout infoWindow = markerView.getCalloutView(); + if (infoWindow != null) manager.pushEvent(context, infoWindow, "onPress", event); + } + }); + + map.setOnMapClickListener(new GoogleMap.OnMapClickListener() { + @Override + public void onMapClick(@NonNull LatLng point) { + WritableMap event = makeClickEventData(point); + event.putString("action", "press"); + manager.pushEvent(context, view, "onPress", event); + + handleMarkerSelection(null); + } + }); + + map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() { + @Override + public void onMapLongClick(@NonNull LatLng point) { + WritableMap event = makeClickEventData(point); + event.putString("action", "long-press"); + manager.pushEvent(context, view, "onLongPress", makeClickEventData(point)); + } + }); + + groundOverlayCollection.setOnGroundOverlayClickListener(new GoogleMap.OnGroundOverlayClickListener() { + @Override + public void onGroundOverlayClick(@NonNull GroundOverlay groundOverlay) { + WritableMap event = makeClickEventData(groundOverlay.getPosition()); + event.putString("action", "overlay-press"); + manager.pushEvent(context, overlayMap.get(groundOverlay), "onPress", event); + } + }); + + map.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() { + @Override + public void onCameraMoveStarted(int reason) { + cameraMoveReason = reason; + boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == reason; + WritableMap event = new WritableNativeMap(); + event.putBoolean("isGesture", isGesture); + manager.pushEvent(context, view, "onRegionChangeStart", event); + } + }); + + map.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() { + @Override + public void onCameraMove() { + LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds; + + cameraLastIdleBounds = null; + boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason; + + RegionChangeEvent event = new RegionChangeEvent(getId(), bounds, true, isGesture); + eventDispatcher.dispatchEvent(event); + } + }); + + map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() { + @Override + public void onCameraIdle() { + LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds; + if ((cameraMoveReason != 0) && + ((cameraLastIdleBounds == null) || + LatLngBoundsUtils.BoundsAreDifferent(bounds, cameraLastIdleBounds))) { + + cameraLastIdleBounds = bounds; + boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason; + + RegionChangeEvent event = new RegionChangeEvent(getId(), bounds, false, isGesture); + eventDispatcher.dispatchEvent(event); + } + } + }); + + map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() { + @Override public void onMapLoaded() { + isMapLoaded = true; + manager.pushEvent(context, view, "onMapLoaded", new WritableNativeMap()); + MapView.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() && map != null) { + //noinspection MissingPermission + map.setMyLocationEnabled(showUserLocation); + map.setLocationSource(fusedLocationSource); + } + synchronized (MapView.this) { + if (!destroyed) { + MapView.this.onResume(); + } + paused = false; + } + } + + @Override + public void onHostPause() { + if (hasPermissions() && map != null) { + //noinspection MissingPermission + map.setMyLocationEnabled(false); + } + synchronized (MapView.this) { + if (!destroyed) { + MapView.this.onPause(); + } + paused = true; + } + } + + @Override + public void onHostDestroy() { + MapView.this.doDestroy(); + } + }; + + context.addLifecycleEventListener(lifecycleListener); + } + + private synchronized void handleMarkerSelection(MapMarker target) { + if (selectedMarker == target) { + return; + } + + WritableMap event; + + if (selectedMarker != null) { + event = makeClickEventData(selectedMarker.getPosition()); + event.putString("action", "marker-deselect"); + event.putString("id", selectedMarker.getIdentifier()); + manager.pushEvent(context, selectedMarker, "onDeselect", event); + + event = makeClickEventData(selectedMarker.getPosition()); + event.putString("action", "marker-deselect"); + event.putString("id", selectedMarker.getIdentifier()); + manager.pushEvent(context, this, "onMarkerDeselect", event); + } + + if (target != null) { + event = makeClickEventData(target.getPosition()); + event.putString("action", "marker-select"); + event.putString("id", target.getIdentifier()); + manager.pushEvent(context, target, "onSelect", event); + + event = makeClickEventData(target.getPosition()); + event.putString("action", "marker-select"); + event.putString("id", target.getIdentifier()); + manager.pushEvent(context, this, "onMarkerSelect", event); + } + + selectedMarker = target; + } + + private boolean hasPermissions() { + return checkSelfPermission(getContext(), PERMISSIONS[0]) == PermissionChecker.PERMISSION_GRANTED || + checkSelfPermission(getContext(), PERMISSIONS[1]) == PermissionChecker.PERMISSION_GRANTED; + } + + + /* + 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) { + this.initialRegion = initialRegion; + // Theoretically onMapReady might be called before setInitialRegion + // In that case, trigger moveToRegion manually + if (!initialRegionSet && map != null) { + moveToRegion(initialRegion); + initialRegionSet = true; + } + } + + public void setInitialCamera(ReadableMap initialCamera) { + this.initialCamera = initialCamera; + if (!initialCameraSet && map != null) { + moveToCamera(initialCamera); + initialCameraSet = true; + } + } + + private void applyBridgedProps() { + if(initialRegion != null) { + moveToRegion(initialRegion); + initialRegionSet = true; + } else if(region != null) { + moveToRegion(region); + } else if (initialCamera != null) { + moveToCamera(initialCamera); + initialCameraSet = true; + } else if (camera != null) { + moveToCamera(camera); + } + if(customMapStyleString != null) { + map.setMapStyle(new MapStyleOptions(customMapStyleString)); + } + this.setPoiClickEnabled(poiClickEnabled); + } + + private void moveToRegion(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 setRegion(ReadableMap region) { + this.region = region; + if(region != null && map != null) { + moveToRegion(region); + } + } + + public void setCamera(ReadableMap camera) { + this.camera = camera; + if(camera != null && map != null) { + moveToCamera(camera); + } + } +public static CameraPosition cameraPositionFromMap(ReadableMap camera){ + if (camera == null) return null; + + CameraPosition.Builder builder = new CameraPosition.Builder(); + + ReadableMap center = camera.getMap("center"); + if (center != null) { + double lng = center.getDouble("longitude"); + double lat = center.getDouble("latitude"); + builder.target(new LatLng(lat, lng)); + } + + builder.tilt((float)camera.getDouble("pitch")); + builder.bearing((float)camera.getDouble("heading")); + builder.zoom((float)camera.getDouble("zoom")); + + return builder.build(); +} + public void moveToCamera(ReadableMap cameraMap) { + CameraPosition camera = cameraPositionFromMap(cameraMap); + if (camera == null) return; + CameraUpdate update = CameraUpdateFactory.newCameraPosition(camera); + + if (super.getHeight() <= 0 || super.getWidth() <= 0) { + // in this case, our map has not been laid out yet, so we save the camera update in a + // local variable. As soon as layout occurs, we will move the camera to the saved update. + // Note that if we tried to move to the camera now, it would trigger an exception. + cameraToSet = update; + } else { + map.moveCamera(update); + cameraToSet = null; + } + } + + public void setMapStyle(@Nullable String customMapStyleString) { + this.customMapStyleString = customMapStyleString; + if(map != null && customMapStyleString != null) { + map.setMapStyle(new MapStyleOptions(customMapStyleString)); + } + } + + public void setShowsUserLocation(boolean showUserLocation) { + this.showUserLocation = showUserLocation; // hold onto this for lifecycle handling + if (hasPermissions()) { + map.setLocationSource(fusedLocationSource); + //noinspection MissingPermission + map.setMyLocationEnabled(showUserLocation); + } + } + + public void setUserLocationPriority(int priority){ + fusedLocationSource.setPriority(priority); + } + + public void setUserLocationUpdateInterval(int interval){ + fusedLocationSource.setInterval(interval); + } + + public void setUserLocationFastestInterval(int interval){ + fusedLocationSource.setFastestInterval(interval); + } + + public void setShowsMyLocationButton(boolean showMyLocationButton) { + if (hasPermissions() || !showMyLocationButton) { + map.getUiSettings().setMyLocationButtonEnabled(showMyLocationButton); + } + } + + public void setToolbarEnabled(boolean toolbarEnabled) { + if (hasPermissions() || !toolbarEnabled) { + map.getUiSettings().setMapToolbarEnabled(toolbarEnabled); + } + } + + public void setCacheEnabled(boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + this.cacheView(); + } + + public void setPoiClickEnabled(boolean poiClickEnabled) { + this.poiClickEnabled = poiClickEnabled; + map.setOnPoiClickListener(poiClickEnabled ? this : null); + } + + 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"); + } + + 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); + } + } + + 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 MapMarker) { + MapMarker annotation = (MapMarker) child; + annotation.addToMap(markerCollection); + features.add(index, annotation); + + // Allow visibility event to be triggered later + int visibility = annotation.getVisibility(); + annotation.setVisibility(INVISIBLE); + + // Remove from a view group if already present, prevent "specified child + // already had a parent" error. + ViewGroup annotationParent = (ViewGroup)annotation.getParent(); + if (annotationParent != null) { + annotationParent.removeView(annotation); + } + + // Add to the parent group + attacherGroup.addView(annotation); + + // Trigger visibility event if necessary. + // With some testing, seems like it is not always + // triggered just by being added to a parent view. + annotation.setVisibility(visibility); + + Marker marker = (Marker) annotation.getFeature(); + markerMap.put(marker, annotation); + } else if (child instanceof MapPolyline) { + MapPolyline polylineView = (MapPolyline) child; + polylineView.addToMap(polylineCollection); + features.add(index, polylineView); + Polyline polyline = (Polyline) polylineView.getFeature(); + polylineMap.put(polyline, polylineView); + } else if (child instanceof MapGradientPolyline) { + MapGradientPolyline polylineView = (MapGradientPolyline) child; + polylineView.addToMap(map); + features.add(index, polylineView); + TileOverlay tileOverlay = (TileOverlay) polylineView.getFeature(); + gradientPolylineMap.put(tileOverlay, polylineView); + } else if (child instanceof MapPolygon) { + MapPolygon polygonView = (MapPolygon) child; + polygonView.addToMap(polygonCollection); + features.add(index, polygonView); + Polygon polygon = (Polygon) polygonView.getFeature(); + polygonMap.put(polygon, polygonView); + } else if (child instanceof MapCircle) { + MapCircle circleView = (MapCircle) child; + circleView.addToMap(circleCollection); + features.add(index, circleView); + } else if (child instanceof MapUrlTile) { + MapUrlTile urlTileView = (MapUrlTile) child; + urlTileView.addToMap(map); + features.add(index, urlTileView); + } else if (child instanceof MapWMSTile) { + MapWMSTile urlTileView = (MapWMSTile) child; + urlTileView.addToMap(map); + features.add(index, urlTileView); + } else if (child instanceof MapLocalTile) { + MapLocalTile localTileView = (MapLocalTile) child; + localTileView.addToMap(map); + features.add(index, localTileView); + } else if (child instanceof MapOverlay) { + MapOverlay overlayView = (MapOverlay) child; + overlayView.addToMap(groundOverlayCollection); + features.add(index, overlayView); + GroundOverlay overlay = (GroundOverlay) overlayView.getFeature(); + overlayMap.put(overlay, overlayView); + } else if (child instanceof MapHeatmap) { + MapHeatmap heatmapView = (MapHeatmap) child; + heatmapView.addToMap(map); + features.add(index, heatmapView); + TileOverlay heatmap = (TileOverlay)heatmapView.getFeature(); + heatmapMap.put(heatmap, heatmapView); + } else if (child instanceof ViewGroup) { + ViewGroup children = (ViewGroup) child; + for (int i = 0; i < children.getChildCount(); i++) { + addFeature(children.getChildAt(i), index); + } + } else { + addView(child, index); + } + } + + public int getFeatureCount() { + return features.size(); + } + + public View getFeatureAt(int index) { + return features.get(index); + } + + public void removeFeatureAt(int index) { + MapFeature feature = features.remove(index); + if (feature instanceof MapMarker) { + markerMap.remove(feature.getFeature()); + feature.removeFromMap(markerCollection); + attacherGroup.removeView(feature); + } else if (feature instanceof MapHeatmap) { + heatmapMap.remove(feature.getFeature()); + feature.removeFromMap(map); + } else if(feature instanceof MapCircle) { + feature.removeFromMap(circleCollection); + } else if(feature instanceof MapOverlay) { + feature.removeFromMap(groundOverlayCollection); + } else if(feature instanceof MapPolygon) { + feature.removeFromMap(polygonCollection); + } else if(feature instanceof MapPolyline) { + feature.removeFromMap(polylineCollection); + } else { + 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 (setPaddingDeferred && super.getHeight() > 0 && super.getWidth() > 0) { + CameraUpdate cu = CameraUpdateFactory.newCameraPosition(map.getCameraPosition()); + + map.setPadding(edgeLeftPadding + baseLeftMapPadding, + edgeTopPadding + baseTopMapPadding, + edgeRightPadding + baseRightMapPadding, + edgeBottomPadding + baseBottomMapPadding); + map.moveCamera(cu); + + // Move the google logo to the default base padding value. + map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding); + + setPaddingDeferred = false; + } + + // 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/react-native-maps/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; + cameraToSet = null; + } + else if (cameraToSet != null) { + map.moveCamera(cameraToSet); + cameraToSet = null; + } + } + + public void animateToCamera(ReadableMap camera, int duration) { + if (map == null) return; + CameraPosition.Builder builder = new CameraPosition.Builder(map.getCameraPosition()); + if (camera.hasKey("zoom")) { + builder.zoom((float)camera.getDouble("zoom")); + } + if (camera.hasKey("heading")) { + builder.bearing((float)camera.getDouble("heading")); + } + if (camera.hasKey("pitch")) { + builder.tilt((float)camera.getDouble("pitch")); + } + if (camera.hasKey("center")) { + ReadableMap center = camera.getMap("center"); + builder.target(new LatLng(center.getDouble("latitude"), center.getDouble("longitude"))); + } + + CameraUpdate update = CameraUpdateFactory.newCameraPosition(builder.build()); + + if (duration <= 0) { + map.moveCamera(update); + } + else { + map.animateCamera(update, duration, null); + } + } + + public void animateToRegion(LatLngBounds bounds, int duration) { + if (map == null) return; + if(duration <= 0) { + map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0)); + } else { + map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null); + } + } + + public void fitToElements(ReadableMap edgePadding, boolean animated) { + if (map == null) return; + + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + + boolean addedPosition = false; + + for (MapFeature feature : features) { + if (feature instanceof MapMarker) { + 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, 0); + + if (edgePadding != null) { + appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), + edgePadding.getInt("right"), edgePadding.getInt("bottom")); + } + + if (animated) { + map.animateCamera(cu); + } else { + map.moveCamera(cu); + } + // Move the google logo to the default base padding value. + map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding); + } + } + + public void fitToSuppliedMarkers(ReadableArray markerIDsArray, ReadableMap edgePadding, boolean animated) { + if (map == null) return; + + 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 (MapFeature feature : features) { + if (feature instanceof MapMarker) { + String identifier = ((MapMarker) 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, 0); + + if (edgePadding != null) { + appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), + edgePadding.getInt("right"), edgePadding.getInt("bottom")); + } + + if (animated) { + map.animateCamera(cu); + } else { + map.moveCamera(cu); + } + // Move the google logo to the default base padding value. + map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding); + } + } + + // padding configured by 'mapPadding' property + int baseLeftMapPadding; + int baseRightMapPadding; + int baseTopMapPadding; + int baseBottomMapPadding; + // extra padding specified by 'edgePadding' option of fitToElements/fitToSuppliedMarkers/fitToCoordinates + int edgeLeftPadding; + int edgeRightPadding; + int edgeTopPadding; + int edgeBottomPadding; + + public void applyBaseMapPadding(int left, int top, int right, int bottom){ + if (super.getHeight() <= 0 || super.getWidth() <= 0) { + // the map is not laid out yet and calling setPadding() now has no effect + baseLeftMapPadding = left; + baseRightMapPadding = right; + baseTopMapPadding = top; + baseBottomMapPadding = bottom; + setPaddingDeferred = true; + return; + } + + // retrieve current camera with current edge paddings configured + map.setPadding(edgeLeftPadding + baseLeftMapPadding, + edgeTopPadding + baseTopMapPadding, + edgeRightPadding + baseRightMapPadding, + edgeBottomPadding + baseBottomMapPadding); + CameraUpdate cu = CameraUpdateFactory.newCameraPosition(map.getCameraPosition()); + + baseLeftMapPadding = left; + baseRightMapPadding = right; + baseTopMapPadding = top; + baseBottomMapPadding = bottom; + + // apply base paddings and restore center position of the map + map.setPadding(edgeLeftPadding + left, + edgeTopPadding + top, + edgeRightPadding + right, + edgeBottomPadding + bottom); + map.moveCamera(cu); + + // Move the google logo to the default base padding value. + map.setPadding(left, top, right, bottom); + } + + public void fitToCoordinates(ReadableArray coordinatesArray, ReadableMap edgePadding, + boolean animated) { + if (map == null) return; + + 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, 0); + + if (edgePadding != null) { + appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), edgePadding.getInt("right"), edgePadding.getInt("bottom")); + } + + if (animated) { + map.animateCamera(cu); + } else { + map.moveCamera(cu); + } + // Move the google logo to the default base padding value. + map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding); + } + + private void appendMapPadding(int iLeft,int iTop, int iRight, int iBottom) { + double density = getResources().getDisplayMetrics().density; + + edgeLeftPadding = (int) (iLeft * density); + edgeTopPadding = (int) (iTop * density); + edgeRightPadding = (int) (iRight * density); + edgeBottomPadding = (int) (iBottom * density); + + map.setPadding(edgeLeftPadding + baseLeftMapPadding, + edgeTopPadding + baseTopMapPadding, + edgeRightPadding + baseRightMapPadding, + edgeBottomPadding + baseBottomMapPadding); + } + + public double[][] getMapBoundaries() { + LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds; + LatLng northEast = bounds.northeast; + LatLng southWest = bounds.southwest; + + return new double[][] { + {northEast.longitude, northEast.latitude}, + {southWest.longitude, southWest.latitude} + }; + } + + public void setMapBoundaries(ReadableMap northEast, ReadableMap southWest) { + if (map == null) return; + + LatLngBounds.Builder builder = new LatLngBounds.Builder(); + + double latNE = northEast.getDouble("latitude"); + double lngNE = northEast.getDouble("longitude"); + builder.include(new LatLng(latNE, lngNE)); + + double latSW = southWest.getDouble("latitude"); + double lngSW = southWest.getDouble("longitude"); + builder.include(new LatLng(latSW, lngSW)); + + LatLngBounds bounds = builder.build(); + + map.setLatLngBoundsForCameraTarget(bounds); + } + + // InfoWindowAdapter interface + + @Override + public View getInfoWindow(Marker marker) { + MapMarker markerView = getMarkerMap(marker); + return markerView.getCallout(); + } + + @Override + public View getInfoContents(Marker marker) { + MapMarker markerView = getMarkerMap(marker); + return markerView.getInfoContents(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + gestureDetector.onTouchEvent(ev); + + int X = (int)ev.getX(); + int Y = (int)ev.getY(); + if(map != null) { + tapLocation = map.getProjection().fromScreenLocation(new Point(X,Y)); + } + + int action = MotionEventCompat.getActionMasked(ev); + + switch (action) { + case (MotionEvent.ACTION_DOWN): + this.getParent().requestDisallowInterceptTouchEvent( + map != null && map.getUiSettings().isScrollGesturesEnabled()); + break; + case (MotionEvent.ACTION_UP): + // Clear this regardless, since isScrollGesturesEnabled() may have been updated + this.getParent().requestDisallowInterceptTouchEvent(false); + break; + } + super.dispatchTouchEvent(ev); + return true; + } + + @Override + public void onMarkerDragStart(Marker marker) { + WritableMap event = makeClickEventData(marker.getPosition()); + manager.pushEvent(context, this, "onMarkerDragStart", event); + + MapMarker markerView = getMarkerMap(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); + + MapMarker markerView = getMarkerMap(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); + + MapMarker markerView = getMarkerMap(marker); + event = makeClickEventData(marker.getPosition()); + manager.pushEvent(context, markerView, "onDragEnd", event); + } + + @Override + public void onPoiClick(PointOfInterest poi) { + WritableMap event = makeClickEventData(poi.latLng); + + event.putString("placeId", poi.placeId); + event.putString("name", poi.name); + + manager.pushEvent(context, this, "onPoiClick", 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); + } + + public void onDoublePress(MotionEvent ev) { + if (this.map == null) return; + 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, "onDoublePress", event); + } + + public void setKmlSrc(String kmlSrc) { + try { + InputStream kmlStream = new FileUtil(context).execute(kmlSrc).get(); + + if (kmlStream == null) { + return; + } + + KmlLayer kmlLayer = new KmlLayer(map, kmlStream, context, markerManager, polygonManager, polylineManager, groundOverlayManager, null); + kmlLayer.addLayerToMap(); + + WritableMap pointers = new WritableNativeMap(); + WritableArray markers = new WritableNativeArray(); + + if (kmlLayer.getContainers() == null) { + manager.pushEvent(context, this, "onKmlReady", pointers); + return; + } + + //Retrieve a nested container within the first container + KmlContainer container = kmlLayer.getContainers().iterator().next(); + if (container == null || container.getContainers() == null) { + manager.pushEvent(context, this, "onKmlReady", pointers); + return; + } + + + if (container.getContainers().iterator().hasNext()) { + container = container.getContainers().iterator().next(); + } + + int index = 0; + for (KmlPlacemark placemark : container.getPlacemarks()) { + MarkerOptions options = new MarkerOptions(); + + if (placemark.getInlineStyle() != null) { + options = placemark.getMarkerOptions(); + } else { + options.icon(BitmapDescriptorFactory.defaultMarker()); + } + + LatLng latLng = ((LatLng) placemark.getGeometry().getGeometryObject()); + String title = ""; + String snippet = ""; + + if (placemark.hasProperty("name")) { + title = placemark.getProperty("name"); + } + + if (placemark.hasProperty("description")) { + snippet = placemark.getProperty("description"); + } + + options.position(latLng); + options.title(title); + options.snippet(snippet); + + MapMarker marker = new MapMarker(context, options, this.manager.getMarkerManager()); + + if (placemark.getInlineStyle() != null + && placemark.getInlineStyle().getIconUrl() != null) { + marker.setImage(placemark.getInlineStyle().getIconUrl()); + } else if (container.getStyle(placemark.getStyleId()) != null) { + KmlStyle style = container.getStyle(placemark.getStyleId()); + marker.setImage(style.getIconUrl()); + } + + String identifier = title + " - " + index; + + marker.setIdentifier(identifier); + + addFeature(marker, index++); + + WritableMap loadedMarker = makeClickEventData(latLng); + loadedMarker.putString("id", identifier); + loadedMarker.putString("title", title); + loadedMarker.putString("description", snippet); + + markers.pushMap(loadedMarker); + } + + pointers.putArray("markers", markers); + + manager.pushEvent(context, this, "onKmlReady", pointers); + + } catch (XmlPullParserException | IOException | InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + + @Override + public void onIndoorBuildingFocused() { + IndoorBuilding building = this.map.getFocusedBuilding(); + if (building != null) { + List levels = building.getLevels(); + int index = 0; + WritableArray levelsArray = Arguments.createArray(); + for (IndoorLevel level : levels) { + WritableMap levelMap = Arguments.createMap(); + levelMap.putInt("index", index); + levelMap.putString("name", level.getName()); + levelMap.putString("shortName", level.getShortName()); + levelsArray.pushMap(levelMap); + index++; + } + WritableMap event = Arguments.createMap(); + WritableMap indoorBuilding = Arguments.createMap(); + indoorBuilding.putArray("levels", levelsArray); + indoorBuilding.putInt("activeLevelIndex", building.getActiveLevelIndex()); + indoorBuilding.putBoolean("underground", building.isUnderground()); + + event.putMap("IndoorBuilding", indoorBuilding); + + manager.pushEvent(context, this, "onIndoorBuildingFocused", event); + } else { + WritableMap event = Arguments.createMap(); + WritableArray levelsArray = Arguments.createArray(); + WritableMap indoorBuilding = Arguments.createMap(); + indoorBuilding.putArray("levels", levelsArray); + indoorBuilding.putInt("activeLevelIndex", 0); + indoorBuilding.putBoolean("underground", false); + + event.putMap("IndoorBuilding", indoorBuilding); + + manager.pushEvent(context, this, "onIndoorBuildingFocused", event); + } + } + + @Override + public void onIndoorLevelActivated(IndoorBuilding building) { + if (building == null) { + return; + } + int activeLevelIndex = building.getActiveLevelIndex(); + if (activeLevelIndex < 0 || activeLevelIndex >= building.getLevels().size()) { + return; + } + IndoorLevel level = building.getLevels().get(activeLevelIndex); + + WritableMap event = Arguments.createMap(); + WritableMap indoorlevel = Arguments.createMap(); + + indoorlevel.putInt("activeLevelIndex", activeLevelIndex); + indoorlevel.putString("name", level.getName()); + indoorlevel.putString("shortName", level.getShortName()); + + event.putMap("IndoorLevel", indoorlevel); + + manager.pushEvent(context, this, "onIndoorLevelActivated", event); + } + + public void setIndoorActiveLevelIndex(int activeLevelIndex) { + IndoorBuilding building = this.map.getFocusedBuilding(); + if (building != null) { + if (activeLevelIndex >= 0 && activeLevelIndex < building.getLevels().size()) { + IndoorLevel level = building.getLevels().get(activeLevelIndex); + if (level != null) { + level.activate(); + } + } + } + } + + private MapMarker getMarkerMap(Marker marker) { + MapMarker airMarker = markerMap.get(marker); + + if (airMarker != null) { + return airMarker; + } + + for (Map.Entry entryMarker : markerMap.entrySet()) { + if (entryMarker.getKey().getPosition().equals(marker.getPosition()) + && entryMarker.getKey().getTitle().equals(marker.getTitle())) { + airMarker = entryMarker.getValue(); + break; + } + } + + return airMarker; + } + + @Override + public void requestLayout() { + super.requestLayout(); + post(measureAndLayout); + } + + private final Runnable measureAndLayout = new Runnable() { + @Override + public void run() { + measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); + layout(getLeft(), getTop(), getRight(), getBottom()); + } + }; + +} diff --git a/android/src/main/java/com/rnmaps/maps/MapWMSTile.java b/android/src/main/java/com/rnmaps/maps/MapWMSTile.java new file mode 100644 index 000000000..b455b523f --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapWMSTile.java @@ -0,0 +1,94 @@ +package com.rnmaps.maps; + +import android.content.Context; + +import com.google.android.gms.maps.model.TileOverlayOptions; +import com.google.android.gms.maps.model.UrlTileProvider; + +import java.net.MalformedURLException; +import java.net.URL; + +public class MapWMSTile extends MapUrlTile { + private static final double[] mapBound = {-20037508.34789244, 20037508.34789244}; + private static final double FULL = 20037508.34789244 * 2; + + class AIRMapGSUrlTileProvider extends MapTileProvider { + + class AIRMapWMSTileProvider extends UrlTileProvider { + private String urlTemplate; + private final int tileSize; + + public AIRMapWMSTileProvider(int width, int height, String urlTemplate) { + super(width, height); + this.urlTemplate = urlTemplate; + this.tileSize = width; + } + + @Override + public URL getTileUrl(int x, int y, int zoom) { + if(MapWMSTile.this.maximumZ > 0 && zoom > maximumZ) { + return null; + } + + if(MapWMSTile.this.minimumZ > 0 && zoom < minimumZ) { + return null; + } + + double[] bb = getBoundingBox(x, y, zoom); + String s = this.urlTemplate + .replace("{minX}", Double.toString(bb[0])) + .replace("{minY}", Double.toString(bb[1])) + .replace("{maxX}", Double.toString(bb[2])) + .replace("{maxY}", Double.toString(bb[3])) + .replace("{width}", Integer.toString(this.tileSize)) + .replace("{height}", Integer.toString(this.tileSize)); + URL url = null; + + try { + url = new URL(s); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + return url; + } + + private double[] getBoundingBox(int x, int y, int zoom) { + double tile = FULL / Math.pow(2, zoom); + return new double[]{ + mapBound[0] + x * tile, + mapBound[1] - (y + 1) * tile, + mapBound[0] + (x + 1) * tile, + mapBound[1] - y * tile + }; + } + + public void setUrlTemplate(String urlTemplate) { + this.urlTemplate = urlTemplate; + } + } + + public AIRMapGSUrlTileProvider(int tileSizet, String urlTemplate, + int maximumZ, int maximumNativeZ, int minimumZ, String tileCachePath, + int tileCacheMaxAge, boolean offlineMode, Context context, boolean customMode) { + super(tileSizet, false, urlTemplate, maximumZ, maximumNativeZ, minimumZ, false, + tileCachePath, tileCacheMaxAge, offlineMode, context, customMode); + this.tileProvider = new AIRMapWMSTileProvider(tileSizet, tileSizet, urlTemplate); + } + } + + public MapWMSTile(Context context) { + super(context); + } + + @Override + protected TileOverlayOptions createTileOverlayOptions() { + TileOverlayOptions options = new TileOverlayOptions(); + options.zIndex(zIndex); + options.transparency(1 - this.opacity); + AIRMapGSUrlTileProvider tileProvider = new AIRMapGSUrlTileProvider((int) this.tileSize, this.urlTemplate, + (int) this.maximumZ, (int) this.maximumNativeZ, (int) this.minimumZ, this.tileCachePath, + (int) this.tileCacheMaxAge, this.offlineMode, this.context, this.customTileProviderNeeded); + options.tileProvider(tileProvider); + return options; + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapWMSTileManager.java b/android/src/main/java/com/rnmaps/maps/MapWMSTileManager.java new file mode 100644 index 000000000..38071ca58 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapWMSTileManager.java @@ -0,0 +1,81 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.view.WindowManager; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +public class MapWMSTileManager extends ViewGroupManager { + + public MapWMSTileManager(ReactApplicationContext reactContext) { + super(); + DisplayMetrics metrics = new DisplayMetrics(); + ((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE)) + .getDefaultDisplay() + .getRealMetrics(metrics); + } + + @Override + public String getName() { + return "AIRMapWMSTile"; + } + + @Override + public MapWMSTile createViewInstance(ThemedReactContext context) { + return new MapWMSTile(context); + } + + @ReactProp(name = "urlTemplate") + public void setUrlTemplate(MapWMSTile view, String urlTemplate) { + view.setUrlTemplate(urlTemplate); + } + + @ReactProp(name = "zIndex", defaultFloat = -1.0f) + public void setZIndex(MapWMSTile view, float zIndex) { + view.setZIndex(zIndex); + } + + @ReactProp(name = "minimumZ", defaultFloat = 0.0f) + public void setMinimumZ(MapWMSTile view, float minimumZ) { + view.setMinimumZ(minimumZ); + } + + @ReactProp(name = "maximumZ", defaultFloat = 100.0f) + public void setMaximumZ(MapWMSTile view, float maximumZ) { + view.setMaximumZ(maximumZ); + } + + @ReactProp(name = "maximumNativeZ", defaultFloat = 100.0f) + public void setMaximumNativeZ(MapWMSTile view, float maximumNativeZ) { + view.setMaximumNativeZ(maximumNativeZ); + } + + @ReactProp(name = "tileSize", defaultFloat = 256.0f) + public void setTileSize(MapWMSTile view, float tileSize) { + view.setTileSize(tileSize); + } + + @ReactProp(name = "tileCachePath") + public void setTileCachePath(MapWMSTile view, String tileCachePath) { + view.setTileCachePath(tileCachePath); + } + + @ReactProp(name = "tileCacheMaxAge", defaultFloat = 0.0f) + public void setTileCacheMaxAge(MapWMSTile view, float tileCacheMaxAge) { + view.setTileCacheMaxAge(tileCacheMaxAge); + } + + @ReactProp(name = "offlineMode", defaultBoolean = false) + public void setOfflineMode(MapWMSTile view, boolean offlineMode) { + view.setOfflineMode(offlineMode); + } + + @ReactProp(name = "opacity", defaultFloat = 1.0f) + public void setOpacity(MapWMSTile view, float opacity) { + view.setOpacity(opacity); + } +} diff --git a/android/src/main/java/com/rnmaps/maps/MapsPackage.java b/android/src/main/java/com/rnmaps/maps/MapsPackage.java new file mode 100644 index 000000000..3bf751de9 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/MapsPackage.java @@ -0,0 +1,45 @@ +package com.rnmaps.maps; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.List; + +public class MapsPackage implements ReactPackage { + + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + + modules.add(new MapModule(reactContext)); + + return modules; + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + MapManager mapManager = new MapManager(reactContext); + MapMarkerManager annotationManager = new MapMarkerManager(); + mapManager.setMarkerManager(annotationManager); + + List viewManagers = new ArrayList<>(); + + viewManagers.add(mapManager); + viewManagers.add(annotationManager); + viewManagers.add(new MapCalloutManager()); + viewManagers.add(new MapPolylineManager(reactContext)); + viewManagers.add(new MapGradientPolylineManager(reactContext)); + viewManagers.add(new MapPolygonManager(reactContext)); + viewManagers.add(new MapCircleManager(reactContext)); + viewManagers.add(new MapUrlTileManager(reactContext)); + viewManagers.add(new MapWMSTileManager(reactContext)); + viewManagers.add(new MapLocalTileManager(reactContext)); + viewManagers.add(new MapOverlayManager(reactContext)); + viewManagers.add(new MapHeatmapManager()); + + return viewManagers; + } +} diff --git a/android/src/main/java/com/rnmaps/maps/RegionChangeEvent.java b/android/src/main/java/com/rnmaps/maps/RegionChangeEvent.java new file mode 100644 index 000000000..202998563 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/RegionChangeEvent.java @@ -0,0 +1,48 @@ +package com.rnmaps.maps; + +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.bridge.WritableNativeMap; +import com.facebook.react.uimanager.events.Event; +import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.LatLngBounds; + +public class RegionChangeEvent extends Event { + private final LatLngBounds bounds; + private final boolean continuous; + private final boolean isGesture; + + public RegionChangeEvent(int id, LatLngBounds bounds, boolean continuous, boolean isGesture) { + super(id); + this.bounds = bounds; + this.continuous = continuous; + this.isGesture = isGesture; + } + + @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(); + LatLng center = bounds.getCenter(); + 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); + event.putBoolean("isGesture", isGesture); + + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), event); + } +} diff --git a/android/lib/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java b/android/src/main/java/com/rnmaps/maps/SizeReportingShadowNode.java similarity index 62% rename from android/lib/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java rename to android/src/main/java/com/rnmaps/maps/SizeReportingShadowNode.java index 40ace480f..a49478158 100644 --- a/android/lib/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java +++ b/android/src/main/java/com/rnmaps/maps/SizeReportingShadowNode.java @@ -6,7 +6,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -package com.airbnb.android.react.maps; +package com.rnmaps.maps; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.UIViewOperationQueue; @@ -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/android/src/main/java/com/rnmaps/maps/ViewAttacherGroup.java b/android/src/main/java/com/rnmaps/maps/ViewAttacherGroup.java new file mode 100644 index 000000000..9104bb279 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/ViewAttacherGroup.java @@ -0,0 +1,26 @@ +package com.rnmaps.maps; + +import android.content.Context; +import android.graphics.Rect; + +import com.facebook.react.views.view.ReactViewGroup; + +public class ViewAttacherGroup extends ReactViewGroup { + + public ViewAttacherGroup(Context context) { + super(context); + + this.setWillNotDraw(true); + this.setVisibility(VISIBLE); + this.setAlpha(0.0f); + this.setRemoveClippedSubviews(false); + this.setClipBounds(new Rect(0, 0, 0, 0)); + this.setOverflow("hidden"); // Change to ViewProps.HIDDEN until RN 0.57 is base + } + + // This should make it more performant, avoid trying to hard to overlap layers with opacity. + @Override + public boolean hasOverlappingRendering() { + return false; + } +} diff --git a/android/src/main/java/com/rnmaps/maps/ViewChangesTracker.java b/android/src/main/java/com/rnmaps/maps/ViewChangesTracker.java new file mode 100644 index 000000000..238fcb537 --- /dev/null +++ b/android/src/main/java/com/rnmaps/maps/ViewChangesTracker.java @@ -0,0 +1,76 @@ +package com.rnmaps.maps; + +import android.os.Handler; +import android.os.Looper; + +import java.util.LinkedList; + +public class ViewChangesTracker { + + private static ViewChangesTracker instance; + private final Handler handler; + private final LinkedList markers = new LinkedList<>(); + private boolean hasScheduledFrame = false; + private final Runnable updateRunnable; + private final long fps = 40; + + private ViewChangesTracker() { + handler = new Handler(Looper.myLooper()); + updateRunnable = new Runnable() { + @Override + public void run() { + update(); + + if (markers.size() > 0) { + handler.postDelayed(updateRunnable, fps); + } else { + hasScheduledFrame = false; + } + } + }; + } + + static ViewChangesTracker getInstance() { + if (instance == null) { + synchronized (ViewChangesTracker.class) { + instance = new ViewChangesTracker(); + } + } + + return instance; + } + + public void addMarker(MapMarker marker) { + markers.add(marker); + + if (!hasScheduledFrame) { + hasScheduledFrame = true; + handler.postDelayed(updateRunnable, fps); + } + } + + public void removeMarker(MapMarker marker) { + markers.remove(marker); + } + + public boolean containsMarker(MapMarker marker) { + return markers.contains(marker); + } + + private final LinkedList markersToRemove = new LinkedList<>(); + + public void update() { + for (MapMarker marker : markers) { + if (!marker.updateCustomForTracking()) { + markersToRemove.add(marker); + } + } + + // Remove markers that are not active anymore + if (markersToRemove.size() > 0) { + markers.removeAll(markersToRemove); + markersToRemove.clear(); + } + } + +} diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 000000000..f7b3da3b3 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,3 @@ +module.exports = { + presets: ['module:@react-native/babel-preset'], +}; diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 000000000..3347cb961 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +module.exports = {extends: ['@commitlint/config-conventional']}; diff --git a/components/AnimatedRegion.js b/components/AnimatedRegion.js deleted file mode 100644 index d2f633a60..000000000 --- a/components/AnimatedRegion.js +++ /dev/null @@ -1,171 +0,0 @@ -class AnimatedRegion extends AnimatedWithChildren { - //latitude: AnimatedValue; - //longitude: AnimatedValue; - //latitudeDelta: AnimatedValue; - //longitudeDelta: AnimatedValue; - //_listeners: {[key: string]: { - // latitude: string, - // longitude: string, - // latitudeDelta: string; - // longitudeDelta: string, - //}}; - - constructor(valueIn) { - super(); - var value = valueIn || { // probably want to come up with better defaults - latitude: 0, - longitude: 0, - latitudeDelta: 0, - longitudeDelta: 0, - }; - this.latitude = value.latitude instanceof Animated - ? value.latitude - : new AnimatedValue(value.latitude); - this.longitude = value.longitude instanceof Animated - ? value.longitude - : new AnimatedValue(value.longitude); - this.latitudeDelta = value.latitudeDelta instanceof Animated - ? value.latitudeDelta - : new AnimatedValue(value.latitudeDelta); - this.longitudeDelta = value.longitudeDelta instanceof Animated - ? value.longitudeDelta - : new AnimatedValue(value.longitudeDelta); - this._listeners = {}; - } - - setValue(value) { - //this.latitude.setValue(value.latitude); - //this.longitude.setValue(value.longitude); - //this.latitudeDelta.setValue(value.latitudeDelta); - //this.longitudeDelta.setValue(value.longitudeDelta); - this.latitude._value = value.latitude; - this.longitude._value = value.longitude; - this.latitudeDelta._value = value.latitudeDelta; - this.longitudeDelta._value = value.longitudeDelta; - } - - setOffset(offset) { - this.latitude.setOffset(offset.latitude); - this.longitude.setOffset(offset.longitude); - this.latitudeDelta.setOffset(offset.latitudeDelta); - this.longitudeDelta.setOffset(offset.longitudeDelta); - } - - flattenOffset() { - this.latitude.flattenOffset(); - this.longitude.flattenOffset(); - this.latitudeDelta.flattenOffset(); - this.longitudeDelta.flattenOffset(); - } - - __getValue() { - return { - latitude: this.latitude.__getValue(), - longitude: this.longitude.__getValue(), - latitudeDelta: this.latitudeDelta.__getValue(), - longitudeDelta: this.longitudeDelta.__getValue(), - }; - } - - __attach() { - this.latitude.__addChild(this); - this.longitude.__addChild(this); - this.latitudeDelta.__addChild(this); - this.longitudeDelta.__addChild(this); - } - - __detach() { - this.latitude.__removeChild(this); - this.longitude.__removeChild(this); - this.latitudeDelta.__removeChild(this); - this.longitudeDelta.__removeChild(this); - } - - stopAnimation(callback) { - this.latitude.stopAnimation(); - this.longitude.stopAnimation(); - this.latitudeDelta.stopAnimation(); - this.longitudeDelta.stopAnimation(); - callback && callback(this.__getValue()); - } - - addListener(callback) { - var id = String(_uniqueId++); - var jointCallback = ({value: number}) => { - callback(this.__getValue()); - }; - this._listeners[id] = { - latitude: this.latitude.addListener(jointCallback), - longitude: this.longitude.addListener(jointCallback), - latitudeDelta: this.latitudeDelta.addListener(jointCallback), - longitudeDelta: this.longitudeDelta.addListener(jointCallback), - }; - return id; - } - - removeListener(id) { - this.latitude.removeListener(this._listeners[id].latitude); - this.longitude.removeListener(this._listeners[id].longitude); - this.latitudeDelta.removeListener(this._listeners[id].latitudeDelta); - this.longitudeDelta.removeListener(this._listeners[id].longitudeDelta); - delete this._listeners[id]; - } - - spring(config) { - var animations = []; - config.hasOwnProperty('latitude') && - animations.push(timing(this.latitude, { - ...config, - toValue: config.latitude, - })); - - config.hasOwnProperty('longitude') && - animations.push(timing(this.longitude, { - ...config, - toValue: config.longitude, - })); - - config.hasOwnProperty('latitudeDelta') && - animations.push(timing(this.latitudeDelta, { - ...config, - toValue: config.latitudeDelta, - })); - - config.hasOwnProperty('longitudeDelta') && - animations.push(timing(this.longitudeDelta, { - ...config, - toValue: config.longitudeDelta, - })); - - return parallel(animations); - } - - timing(config) { - var animations = []; - config.hasOwnProperty('latitude') && - animations.push(timing(this.latitude, { - ...config, - toValue: config.latitude, - })); - - config.hasOwnProperty('longitude') && - animations.push(timing(this.longitude, { - ...config, - toValue: config.longitude, - })); - - config.hasOwnProperty('latitudeDelta') && - animations.push(timing(this.latitudeDelta, { - ...config, - toValue: config.latitudeDelta, - })); - - config.hasOwnProperty('longitudeDelta') && - animations.push(timing(this.longitudeDelta, { - ...config, - toValue: config.longitudeDelta, - })); - - return parallel(animations); - } -} diff --git a/components/MapCallout.js b/components/MapCallout.js deleted file mode 100644 index a4b4f809b..000000000 --- a/components/MapCallout.js +++ /dev/null @@ -1,45 +0,0 @@ - -var React = require('react'); -var { - PropTypes, -} = React; - -var ReactNative = require('react-native'); -var { - View, - NativeMethodsMixin, - requireNativeComponent, - StyleSheet, -} = ReactNative; - -var MapCallout = React.createClass({ - mixins: [NativeMethodsMixin], - - propTypes: { - ...View.propTypes, - tooltip: PropTypes.bool, - onPress: PropTypes.func, - }, - - getDefaultProps: function() { - return { - tooltip: false, - }; - }, - - render: function() { - return ; - }, -}); - -var styles = StyleSheet.create({ - callout: { - position: 'absolute', - //flex: 0, - //backgroundColor: 'transparent', - }, -}); - -var AIRMapCallout = requireNativeComponent('AIRMapCallout', MapCallout); - -module.exports = MapCallout; diff --git a/components/MapCircle.js b/components/MapCircle.js deleted file mode 100644 index cd6fe2425..000000000 --- a/components/MapCircle.js +++ /dev/null @@ -1,153 +0,0 @@ - -var React = require('react'); -var { - PropTypes, -} = React; - -var ReactNative = require('react-native'); -var { - View, - NativeMethodsMixin, - requireNativeComponent, - StyleSheet, -} = ReactNative; - -var MapCircle = React.createClass({ - mixins: [NativeMethodsMixin], - - propTypes: { - ...View.propTypes, - - /** - * The coordinate of the center of the circle - */ - center: PropTypes.shape({ - /** - * Coordinates for the center of the circle. - */ - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - }).isRequired, - - /** - * The radius of the circle to be drawn (in meters) - */ - radius: PropTypes.number.isRequired, - - /** - * Callback that is called when the user presses on the circle - */ - onPress: PropTypes.func, - - /** - * The stroke width to use for the path. - */ - strokeWidth: PropTypes.number, - - /** - * The stroke color to use for the path. - */ - strokeColor: PropTypes.string, - - /** - * The fill color to use for the path. - */ - fillColor: PropTypes.string, - - /** - * 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. - * - * @platform android - */ - zIndex: PropTypes.number, - - /** - * The line cap style to apply to the open ends of the path. - * The default style is `round`. - * - * @platform ios - */ - lineCap: PropTypes.oneOf([ - 'butt', - 'round', - 'square', - ]), - - /** - * The line join style to apply to corners of the path. - * The default style is `round`. - * - * @platform ios - */ - lineJoin: PropTypes.oneOf([ - 'miter', - 'round', - 'bevel', - ]), - - /** - * 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. - * - * @platform ios - */ - miterLimit: PropTypes.number, - - /** - * 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. - * - * The default value of this property is 0. - * - * @platform ios - */ - lineDashPhase: PropTypes.number, - - /** - * An array of numbers specifying the dash pattern to use for the path. - * - * The array contains one or more numbers that indicate the lengths (measured in points) of the - * line segments and gaps in the pattern. The values in the array alternate, starting with the - * first line segment length, followed by the first gap length, followed by the second line - * segment length, and so on. - * - * This property is set to `null` by default, which indicates no line dash pattern. - * - * @platform ios - */ - lineDashPattern: PropTypes.arrayOf(PropTypes.number), - }, - - getDefaultProps: function() { - return { - strokeColor: '#000', - strokeWidth: 1, - }; - }, - - _onPress: function(e) { - this.props.onPress && this.props.onPress(e); - }, - - render: function() { - return ( - - ); - }, -}); - -var AIRMapCircle = requireNativeComponent('AIRMapCircle', MapCircle); - -module.exports = MapCircle; diff --git a/components/MapMarker.js b/components/MapMarker.js deleted file mode 100644 index e35302c81..000000000 --- a/components/MapMarker.js +++ /dev/null @@ -1,276 +0,0 @@ -'use strict'; - -var React = require('react'); -var { - PropTypes, -} = React; - -var ReactNative = require('react-native'); -var { - View, - NativeMethodsMixin, - requireNativeComponent, - StyleSheet, - Platform, - NativeModules, - Animated, -} = ReactNative; - -var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); - -var MapMarker = React.createClass({ - mixins: [NativeMethodsMixin], - - viewConfig: { - uiViewClassName: 'AIRMapMarker', - validAttributes: { - coordinate: true, - }, - }, - - propTypes: { - ...View.propTypes, - - // TODO(lmr): get rid of these? - identifier: PropTypes.string, - reuseIdentifier: PropTypes.string, - - /** - * The title of the marker. This is only used if the component has no children that - * are an ``, in which case the default callout behavior will be used, which - * will show both the `title` and the `description`, if provided. - */ - title: PropTypes.string, - - /** - * The description of the marker. This is only used if the component has no children - * that are an ``, in which case the default callout behavior will be used, - * which will show both the `title` and the `description`, if provided. - */ - description: PropTypes.string, - - /** - * A custom image to be used as the marker's icon. Only local image resources are allowed to be - * used. - */ - image: PropTypes.any, - - /** - * If no custom marker view or custom image is provided, the platform default pin will be used, - * which can be customized by this color. Ignored if a custom marker is being used. - */ - pinColor: PropTypes.string, - - /** - * The coordinate for the marker. - */ - coordinate: PropTypes.shape({ - /** - * Coordinates for the anchor point of the marker. - */ - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - }).isRequired, - - /** - * The offset (in points) at which to display the view. - * - * By default, the center point of an annotation view is placed at the coordinate point of the - * associated annotation. You can use this property to reposition the annotation view as - * needed. This x and y offset values are measured in points. Positive offset values move the - * annotation view down and to the right, while negative values move it up and to the left. - * - * For android, see the `anchor` prop. - * - * @platform ios - */ - centerOffset: PropTypes.shape({ - /** - * Offset from the anchor point - */ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - - /** - * The offset (in points) at which to place the callout bubble. - * - * This property determines the additional distance by which to move the callout bubble. When - * this property is set to (0, 0), the anchor point of the callout bubble is placed on the - * top-center point of the marker view’s frame. Specifying positive offset values moves the - * callout bubble down and to the right, while specifying negative values moves it up and to - * the left. - * - * For android, see the `calloutAnchor` prop. - * - * @platform ios - */ - calloutOffset: PropTypes.shape({ - /** - * Offset to the callout - */ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - - /** - * Sets the anchor point for the marker. - * - * The anchor specifies the point in the icon image that is anchored to the marker's position - * on the Earth's surface. - * - * The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0) - * is the top-left corner of the image, and (1, 1) is the bottom-right corner. The anchoring - * point in a W x H image is the nearest discrete grid point in a (W + 1) x (H + 1) grid, - * obtained by scaling the then rounding. For example, in a 4 x 2 image, the anchor point - * (0.7, 0.6) resolves to the grid point at (3, 1). - * - * For ios, see the `centerOffset` prop. - * - * @platform android - */ - anchor: PropTypes.shape({ - /** - * Offset to the callout - */ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - - /** - * 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 `andor` prop for more - * details. - * - * The default is the top middle of the image. - * - * For ios, see the `calloutOffset` prop. - * - * @platform android - */ - calloutAnchor: PropTypes.shape({ - /** - * Offset to the callout - */ - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }), - - /** - * Sets whether this marker should be flat against the map true or a billboard facing the - * camera false. - * - * @platform android - */ - flat: PropTypes.bool, - - draggable: PropTypes.bool, - - /** - * Callback that is called when the user presses on the marker - */ - onPress: PropTypes.func, - - /** - * Callback that is called when the user selects the marker, before the callout is shown. - * - * @platform ios - */ - onSelect: PropTypes.func, - - /** - * Callback that is called when the marker is deselected, before the callout is hidden. - * - * @platform ios - */ - onDeselect: PropTypes.func, - - /** - * Callback that is called when the user taps the callout view. - */ - onCalloutPress: PropTypes.func, - - /** - * Callback that is called when the user initiates a drag on this marker (if it is draggable) - */ - onDragStart: PropTypes.func, - - /** - * Callback called continuously as the marker is dragged - */ - onDrag: PropTypes.func, - - /** - * Callback that is called when a drag on this marker finishes. This is usually the point you - * will want to setState on the marker's coordinate again - */ - onDragEnd: PropTypes.func, - - }, - - showCallout: function() { - this._runCommand('showCallout', []); - }, - - hideCallout: function() { - this._runCommand('hideCallout', []); - }, - - _getHandle: function() { - return ReactNative.findNodeHandle(this.refs.marker); - }, - - _runCommand: function (name, args) { - switch (Platform.OS) { - case 'android': - NativeModules.UIManager.dispatchViewManagerCommand( - this._getHandle(), - NativeModules.UIManager.AIRMapMarker.Commands[name], - args - ); - break; - - case 'ios': - NativeModules.AIRMapMarkerManager[name].apply( - NativeModules.AIRMapMarkerManager[name], - [this._getHandle(), ...args] - ); - break; - } - }, - - _onPress: function(e) { - this.props.onPress && this.props.onPress(e); - }, - - render: function() { - var image = undefined; - if (this.props.image) { - image = resolveAssetSource(this.props.image) || {}; - image = image.uri; - } - - return ( - - ); - }, -}); - -var styles = StyleSheet.create({ - marker: { - position: 'absolute', - backgroundColor: 'transparent', - }, -}); - -var AIRMapMarker = requireNativeComponent('AIRMapMarker', MapMarker); - -MapMarker.Animated = Animated.createAnimatedComponent(MapMarker); - -module.exports = MapMarker; diff --git a/components/MapPolygon.js b/components/MapPolygon.js deleted file mode 100644 index a87ccb22d..000000000 --- a/components/MapPolygon.js +++ /dev/null @@ -1,165 +0,0 @@ -var React = require('react'); -var { - PropTypes, -} = React; - -var ReactNative = require('react-native'); -var { - View, - NativeMethodsMixin, - requireNativeComponent, - StyleSheet, -} = ReactNative; - -var MapPolygon = React.createClass({ - mixins: [NativeMethodsMixin], - - propTypes: { - ...View.propTypes, - - /** - * An array of coordinates to describe the polygon - */ - coordinates: PropTypes.arrayOf(PropTypes.shape({ - /** - * Latitude/Longitude coordinates - */ - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - })), - - /** - * Callback that is called when the user presses on the polygon - */ - onPress: PropTypes.func, - - /** - * The stroke width to use for the path. - */ - strokeWidth: PropTypes.number, - - /** - * The stroke color to use for the path. - */ - strokeColor: PropTypes.string, - - /** - * The fill color to use for the path. - */ - fillColor: PropTypes.string, - - /** - * 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. - * - * @platform android - */ - zIndex: PropTypes.number, - - /** - * The line cap style to apply to the open ends of the path. - * The default style is `round`. - * - * @platform ios - */ - lineCap: PropTypes.oneOf([ - 'butt', - 'round', - 'square', - ]), - - /** - * The line join style to apply to corners of the path. - * The default style is `round`. - * - * @platform ios - */ - lineJoin: PropTypes.oneOf([ - 'miter', - 'round', - 'bevel', - ]), - - /** - * 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. - * - * @platform ios - */ - miterLimit: PropTypes.number, - - /** - * 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. - * - * @platform android - */ - geodesic: PropTypes.bool, - - /** - * 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. - * - * The default value of this property is 0. - * - * @platform ios - */ - lineDashPhase: PropTypes.number, - - /** - * An array of numbers specifying the dash pattern to use for the path. - * - * The array contains one or more numbers that indicate the lengths (measured in points) of the - * line segments and gaps in the pattern. The values in the array alternate, starting with the - * first line segment length, followed by the first gap length, followed by the second line - * segment length, and so on. - * - * This property is set to `null` by default, which indicates no line dash pattern. - * - * @platform ios - */ - lineDashPattern: PropTypes.arrayOf(PropTypes.number), - }, - - getDefaultProps: function() { - return { - strokeColor: '#000', - strokeWidth: 1, - }; - }, - - _onPress: function(e) { - this.props.onPress && this.props.onPress(e); - }, - - render: function() { - return ( - - ); - }, -}); - -var styles = StyleSheet.create({ - polyline: { - position: 'absolute', - width: 0, - height: 0, - }, -}); - -var AIRMapPolygon = requireNativeComponent('AIRMapPolygon', MapPolygon); - -module.exports = MapPolygon; diff --git a/components/MapPolyline.js b/components/MapPolyline.js deleted file mode 100644 index c32a7db89..000000000 --- a/components/MapPolyline.js +++ /dev/null @@ -1,153 +0,0 @@ -var React = require('react'); -var { - PropTypes, -} = React; - -var ReactNative = require('react-native'); -var { - View, - NativeMethodsMixin, - requireNativeComponent, - StyleSheet, -} = ReactNative; - -var MapPolyline = React.createClass({ - mixins: [NativeMethodsMixin], - - propTypes: { - ...View.propTypes, - - /** - * An array of coordinates to describe the polygon - */ - coordinates: PropTypes.arrayOf(PropTypes.shape({ - /** - * Latitude/Longitude coordinates - */ - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - })), - - /** - * Callback that is called when the user presses on the polyline - */ - onPress: PropTypes.func, - - /** - * The stroke width to use for the path. - */ - strokeWidth: PropTypes.number, - - /** - * The stroke color to use for the path. - */ - strokeColor: PropTypes.string, - - /** - * 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. - * - * @platform android - */ - zIndex: PropTypes.number, - - /** - * The line cap style to apply to the open ends of the path. - * The default style is `round`. - * - * @platform ios - */ - lineCap: PropTypes.oneOf([ - 'butt', - 'round', - 'square', - ]), - - /** - * The line join style to apply to corners of the path. - * The default style is `round`. - * - * @platform ios - */ - lineJoin: PropTypes.oneOf([ - 'miter', - 'round', - 'bevel', - ]), - - /** - * 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. - * - * @platform ios - */ - miterLimit: PropTypes.number, - - /** - * 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. - * - * @platform android - */ - geodesic: PropTypes.bool, - - /** - * 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. - * - * The default value of this property is 0. - * - * @platform ios - */ - lineDashPhase: PropTypes.number, - - /** - * An array of numbers specifying the dash pattern to use for the path. - * - * The array contains one or more numbers that indicate the lengths (measured in points) of the - * line segments and gaps in the pattern. The values in the array alternate, starting with the - * first line segment length, followed by the first gap length, followed by the second line - * segment length, and so on. - * - * This property is set to `null` by default, which indicates no line dash pattern. - * - * @platform ios - */ - lineDashPattern: PropTypes.arrayOf(PropTypes.number), - }, - - getDefaultProps: function() { - return { - strokeColor: '#000', - strokeWidth: 1, - }; - }, - - render: function() { - return ( - - ); - }, -}); - -var styles = StyleSheet.create({ - polyline: { - position: 'absolute', - width: 0, - height: 0, - }, -}); - -var AIRMapPolyline = requireNativeComponent('AIRMapPolyline', MapPolyline); - -module.exports = MapPolyline; diff --git a/components/MapView.js b/components/MapView.js deleted file mode 100644 index 774d7c2b0..000000000 --- a/components/MapView.js +++ /dev/null @@ -1,483 +0,0 @@ -'use strict'; - -var React = require('react'); -var { - PropTypes, -} = React; -var ReactNative = require('react-native'); -var { - EdgeInsetsPropType, - NativeMethodsMixin, - Platform, - ReactNativeViewAttributes, - View, - Animated, - requireNativeComponent, - NativeModules, - ColorPropType, -} = ReactNative; - -var MapMarker = require('./MapMarker'); -var MapPolyline = require('./MapPolyline'); -var MapPolygon = require('./MapPolygon'); -var MapCircle = require('./MapCircle'); -var MapCallout = require('./MapCallout'); - -var MapView = React.createClass({ - mixins: [NativeMethodsMixin], - - viewConfig: { - uiViewClassName: 'AIRMap', - validAttributes: { - region: true, - }, - }, - - propTypes: { - ...View.propTypes, - /** - * Used to style and layout the `MapView`. See `StyleSheet.js` and - * `ViewStylePropTypes.js` for more info. - */ - style: View.propTypes.style, - - /** - * If `true` the app will ask for the user's location. - * Default value is `false`. - * - * **NOTE**: You need to add NSLocationWhenInUseUsageDescription key in - * Info.plist to enable geolocation, otherwise it is going - * to *fail silently*! - */ - showsUserLocation: PropTypes.bool, - - /** - * If `false` hide the button to move map to the current user's location. - * Default value is `true`. - * - * @platform android - */ - showsMyLocationButton: PropTypes.bool, - - /** - * 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. - * Default value is `false`. - * - * @platform ios - */ - followsUserLocation: PropTypes.bool, - - /** - * If `false` points of interest won't be displayed on the map. - * Default value is `true`. - * - */ - showsPointsOfInterest: PropTypes.bool, - - /** - * If `false` compass won't be displayed on the map. - * Default value is `true`. - * - * @platform ios - */ - showsCompass: PropTypes.bool, - - /** - * If `false` the user won't be able to pinch/zoom the map. - * Default value is `true`. - * - */ - zoomEnabled: PropTypes.bool, - - /** - * If `false` the user won't be able to pinch/rotate the map. - * Default value is `true`. - * - */ - rotateEnabled: PropTypes.bool, - - /** - * If `true` the map will be cached to an Image for performance - * Default value is `false`. - * - */ - cacheEnabled: PropTypes.bool, - - /** - * If `true` the map will be showing a loading indicator - * Default value is `false`. - * - */ - loadingEnabled: PropTypes.bool, - - /** - * Loading background color while generating map cache image or loading the map - * Default color is light gray. - * - */ - loadingBackgroundColor: ColorPropType, - - /** - * Loading indicator color while generating map cache image or loading the map - * Default color is gray color for iOS, theme color for Android. - * - */ - loadingIndicatorColor: ColorPropType, - - /** - * If `false` the user won't be able to change the map region being displayed. - * Default value is `true`. - * - */ - scrollEnabled: PropTypes.bool, - - /** - * If `false` the user won't be able to adjust the camera’s pitch angle. - * Default value is `true`. - * - */ - pitchEnabled: PropTypes.bool, - - /** - * If `false` will hide 'Navigate' and 'Open in Maps' buttons on marker press - * Default value is `true`. - * - * @platform android - */ - toolbarEnabled: PropTypes.bool, - - /** - * A Boolean indicating whether the map shows scale information. - * Default value is `false` - * - */ - showsScale: PropTypes.bool, - - /** - * A Boolean indicating whether the map displays extruded building information. - * Default value is `true`. - */ - showsBuildings: PropTypes.bool, - - /** - * A Boolean value indicating whether the map displays traffic information. - * Default value is `false`. - */ - showsTraffic: PropTypes.bool, - - /** - * A Boolean indicating whether indoor maps should be enabled. - * Default value is `false` - * - * @platform android - */ - showsIndoors: PropTypes.bool, - - /** - * 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 - */ - mapType: PropTypes.oneOf([ - 'standard', - 'satellite', - 'hybrid', - 'terrain', - ]), - - /** - * The region to be displayed by the map. - * - * The region is defined by the center coordinates and the span of - * coordinates to display. - */ - region: PropTypes.shape({ - /** - * Coordinates for the center of the map. - */ - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - - /** - * Difference between the minimun and the maximum latitude/longitude - * to be displayed. - */ - latitudeDelta: PropTypes.number.isRequired, - longitudeDelta: PropTypes.number.isRequired, - }), - - /** - * 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. - */ - initialRegion: PropTypes.shape({ - /** - * Coordinates for the center of the map. - */ - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - - /** - * Difference between the minimun and the maximum latitude/longitude - * to be displayed. - */ - latitudeDelta: PropTypes.number.isRequired, - longitudeDelta: PropTypes.number.isRequired, - }), - - /** - * Maximum size of area that can be displayed. - * - * @platform ios - */ - maxDelta: PropTypes.number, - - /** - * Minimum size of area that can be displayed. - * - * @platform ios - */ - minDelta: PropTypes.number, - - /** - * Insets for the map's legal label, originally at bottom left of the map. - * See `EdgeInsetsPropType.js` for more information. - */ - legalLabelInsets: EdgeInsetsPropType, - - /** - * Callback that is called continuously when the user is dragging the map. - */ - onRegionChange: PropTypes.func, - - /** - * Callback that is called once, when the user is done moving the map. - */ - onRegionChangeComplete: PropTypes.func, - - /** - * Callback that is called when user taps on the map. - */ - onPress: PropTypes.func, - - /** - * Callback that is called when user makes a "long press" somewhere on the map. - */ - onLongPress: PropTypes.func, - - /** - * Callback that is called when user makes a "drag" somewhere on the map - */ - onPanDrag: PropTypes.func, - - /** - * Callback that is called when a marker on the map is tapped by the user. - */ - onMarkerPress: PropTypes.func, - - /** - * Callback that is called when a marker on the map becomes selected. This will be called when - * the callout for that marker is about to be shown. - * - * @platform ios - */ - onMarkerSelect: PropTypes.func, - - /** - * Callback that is called when a marker on the map becomes deselected. This will be called when - * the callout for that marker is about to be hidden. - * - * @platform ios - */ - onMarkerDeselect: PropTypes.func, - - /** - * Callback that is called when a callout is tapped by the user. - */ - onCalloutPress: PropTypes.func, - - /** - * Callback that is called when the user initiates a drag on a marker (if it is draggable) - */ - onMarkerDragStart: PropTypes.func, - - /** - * Callback called continuously as a marker is dragged - */ - onMarkerDrag: PropTypes.func, - - /** - * Callback that is called when a drag on a marker finishes. This is usually the point you - * will want to setState on the marker's coordinate again - */ - onMarkerDragEnd: PropTypes.func, - - }, - - getInitialState: function() { - return { - isReady: Platform.OS === 'ios', - }; - }, - - componentDidMount: function() { - const { region, initialRegion } = this.props; - if (region && this.state.isReady) { - this.refs.map.setNativeProps({ region }); - } else if (initialRegion && this.state.isReady) { - this.refs.map.setNativeProps({ region: initialRegion }); - } - }, - - componentWillUpdate: function(nextProps) { - var a = this.__lastRegion; - var b = nextProps.region; - if (!a || !b) return; - if ( - a.latitude !== b.latitude || - a.longitude !== b.longitude || - a.latitudeDelta !== b.latitudeDelta || - a.longitudeDelta !== b.longitudeDelta - ) { - this.refs.map.setNativeProps({ region: b }); - } - }, - - _onMapReady: function() { - const { region, initialRegion } = this.props; - if (region) { - this.refs.map.setNativeProps({ region }); - } else if (initialRegion) { - this.refs.map.setNativeProps({ region: initialRegion }); - } - this.setState({ isReady: true }); - }, - - _onLayout: function(e) { - const { region, initialRegion, onLayout } = this.props; - const { isReady } = this.state; - if (region && isReady && !this.__layoutCalled) { - this.__layoutCalled = true; - this.refs.map.setNativeProps({ region }); - } else if (initialRegion && isReady && !this.__layoutCalled) { - this.__layoutCalled = true; - this.refs.map.setNativeProps({ region: initialRegion }); - } - onLayout && onLayout(e); - }, - - _onChange: function(event: Event) { - this.__lastRegion = event.nativeEvent.region; - if (event.nativeEvent.continuous) { - this.props.onRegionChange && - this.props.onRegionChange(event.nativeEvent.region); - } else { - this.props.onRegionChangeComplete && - this.props.onRegionChangeComplete(event.nativeEvent.region); - } - }, - - animateToRegion: function (region, duration) { - this._runCommand('animateToRegion', [region, duration || 500]); - }, - - animateToCoordinate: function (latLng, duration) { - this._runCommand('animateToCoordinate', [latLng, duration || 500]); - }, - - fitToElements: function(animated) { - this._runCommand('fitToElements', [animated]); - }, - - fitToSuppliedMarkers: function(markers, animated) { - this._runCommand('fitToSuppliedMarkers', [markers, animated]); - }, - - takeSnapshot: function (width, height, region, callback) { - if (!region) { - region = this.props.region || this.props.initialRegion; - } - this._runCommand('takeSnapshot', [width, height, region, callback]); - }, - - _getHandle: function() { - return ReactNative.findNodeHandle(this.refs.map); - }, - - _runCommand: function (name, args) { - switch (Platform.OS) { - case 'android': - NativeModules.UIManager.dispatchViewManagerCommand( - this._getHandle(), - NativeModules.UIManager.AIRMap.Commands[name], - args - ); - break; - - case 'ios': - NativeModules.AIRMapManager[name].apply( - NativeModules.AIRMapManager[name], - [this._getHandle(), ...args] - ); - break; - } - }, - - render: function() { - let props; - - if (this.state.isReady) { - props = { - ...this.props, - region: null, - initialRegion: null, - onChange: this._onChange, - onMapReady: this._onMapReady, - onLayout: this._onLayout, - }; - if (Platform.OS === 'ios' && props.mapType === 'terrain') { - props.mapType = 'standard'; - } - props.handlePanDrag = !!props.onPanDrag; - } else { - props = { - style: this.props.style, - region: null, - initialRegion: null, - onChange: this._onChange, - onMapReady: this._onMapReady, - onLayout: this._onLayout, - }; - } - - return ( - - ); - }, -}); - -var AIRMap = requireNativeComponent('AIRMap', MapView, { - nativeOnly: { - onChange: true, - onMapReady: true, - handlePanDrag: true, - }, -}); - -MapView.Marker = MapMarker; -MapView.Polyline = MapPolyline; -MapView.Polygon = MapPolygon; -MapView.Circle = MapCircle; -MapView.Callout = MapCallout; - -MapView.Animated = Animated.createAnimatedComponent(MapView); - -module.exports = MapView; diff --git a/docs/callout.md b/docs/callout.md index 58b59a6bd..e927562b6 100644 --- a/docs/callout.md +++ b/docs/callout.md @@ -1,14 +1,35 @@ -# `` Component API +# `` Component API ## Props -| Prop | Type | Default | Note | -|---|---|---|---| -| `tooltip` | `Boolean` | `false` | If `false`, a default "tooltip" bubble window will be drawn around this callouts children. If `true`, the child views can fully customize their appearance, including any "bubble" like styles. +| Prop | Type | Default | Note | +| -------------- | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `tooltip` | `Boolean` | `false` | If `false`, a default "tooltip" bubble window will be drawn around this callouts children. If `true`, the child views can fully customize their appearance, including any "bubble" like styles. | +| `alphaHitTest` | `Boolean` | `false` | If `true`, clicks on transparent areas in callout will be passed to map. **Note**: iOS only. | +## Events + +| Event Name | Returns | Notes | +| ---------- | ------- | ------------------------------------------------------------ | +| `onPress` | | Callback that is called when the user presses on the callout | + +--- + +# `` Component API + +**Note**: Supported on iOS only. +Use to handle press on specific subview of callout. +Put this component inside ``. ## Events -| Event Name | Returns | Notes -|---|---|---| -| `onPress` | | Callback that is called when the user presses on the callout +| Event Name | Returns | Notes | +| ---------- | ------- | ---------------------------------------------------------------------------- | +| `onPress` | | Callback that is called when the user presses on this subview inside callout | + +## Notes + +Native press event has property `action`, which is: + +- `callout-press` (or `marker-overlay-press` for GoogleMaps on iOS) for press on `` +- `callout-inside-press` (or `marker-inside-overlay-press` for GoogleMaps on iOS) for press on `` diff --git a/docs/circle.md b/docs/circle.md index 78a850283..5a42a2cd1 100644 --- a/docs/circle.md +++ b/docs/circle.md @@ -1,21 +1,20 @@ -# `` Component API +# `` Component API ## Props -| Prop | Type | Default | Note | -|---|---|---|---| -| `center` | `LatLng` | (Required) | The coordinate of the center of the circle -| `radius` | `Number` | (Required) | The radius of the circle to be drawn (in meters) -| `strokeWidth` | `Number` | `1` | The stroke width to use for the path. -| `strokeColor` | `String` | `#000` | The stroke color to use for the path. -| `fillColor` | `String` | | The fill color to use for the path. -| `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. -| `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. -| `lineDashPattern` | `Array` | `null` | (iOS only) An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. - +| Prop | Type | Default | Note | +| ----------------- | --------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `center` | `LatLng` | (Required) | The coordinate of the center of the circle | +| `radius` | `Number` | (Required) | The radius of the circle to be drawn (in meters) | +| `strokeWidth` | `Number` | `1` | The stroke width to use for the path. | +| `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. 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. | +| `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. | +| `lineDashPattern` | `Array` | `null` | (iOS only) An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. | ## Types diff --git a/docs/examples-setup.md b/docs/examples-setup.md new file mode 100644 index 000000000..686f96124 --- /dev/null +++ b/docs/examples-setup.md @@ -0,0 +1,12 @@ +# Examples Setup + +- Clone or download the repository. +- From the root of the project run `yarn bootstrap` +- Add your API key(s) + - Android + - Open `example/android/local.properties` (or create the file if it doesn't already exist) + - Add the following line: `MAPS_API_KEY=your_api_key_here` + - iOS + - Open `example/ios/Config.xcconfig` (or create the file if it doesn't already exist) + - Add the following line: `MAPS_API_KEY=your_api_key_here` +- Run `yarn android` or `yarn ios` within the example folder diff --git a/docs/geojson.md b/docs/geojson.md new file mode 100644 index 000000000..f4d9c9c29 --- /dev/null +++ b/docs/geojson.md @@ -0,0 +1,55 @@ +# `` Component API + +## Props + +| Prop | Type | Default | Note | +| ------------------- | ------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `geojson` | `GeoJSON` | | [Geojson](https://geojson.org/) description of object. | +| `strokeColor` | `String` | `stroke` property in GeoJson if present else `#000` | The stroke color to use for polygons and polylines. | +| `fillColor` | `String` | `fill` property in GeoJson | The fill color to use for polygons. | +| `strokeWidth` | `Number` | `stroke-width` property in Geojson if present else `1` | The stroke width to use for polygons and polylines. | +| `color` | `String` | `marker-color` property in GeoJson | The color to use for points. | +| `lineDashPhase` | `Number` | | (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. | +| `lineDashPattern` | `Array` | | An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. | +| `lineCap` | `'butt' \| 'round' \| 'square'` | | The line cap style to apply to the open ends of the path. Possible values are `butt`, `round` or `square`. Note: lineCap is not yet supported for GoogleMaps provider on iOS. | +| `lineJoin` | `'miter' \| 'round' \| 'bevel'` | | The line join style to apply to corners of the path. Possible values are `miter`, `round` or `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. | +| `zIndex` | `Number` | | Layer level of the z-index value | +| `onPress` | `Function` | | returns the selected overlay value with the onPress functionality | +| `markerComponent` | `React Node` | | Component to render in place of the default marker when the overlay type is a `point` | +| `title` | `string` | | The title of the marker. This is only used if the component has no children that are a `` | +| `tracksViewChanges` | `Boolean` | true | Sets whether this marker should track view changes. It's recommended to turn it off whenever it's possible to improve custom marker performance. This is the default value for all point markers in your geojson data. It can be overriden on a per point basis by adding a `trackViewChanges` property to the `properties` object on the point. | +| `anchor` | `Point` | (0.5, 1) | Sets the anchor point for the marker.

The anchor specifies the point in the icon image that is anchored to the marker's position on the Earth's surface.

The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0) is the top-left corner of the image, and (1, 1) is the bottom-right corner. The anchoring point in a W x H image is the nearest discrete grid point in a (W + 1) x (H + 1) grid, obtained by scaling the then rounding. For example, in a 4 x 2 image, the anchor point (0.7, 0.6) resolves to the grid point at (3, 1).

For MapKit on iOS, see the `centerOffset` prop. | +| `centerOffset` | `Point` | (0, 0) | The offset (in points) at which to display the view.

By default, the center point of an annotation view is placed at the coordinate point of the associated annotation. You can use this property to reposition the annotation view as needed. This x and y offset values are measured in points. Positive offset values move the annotation view down and to the right, while negative values move it up and to the left.

For Google Maps, see the `anchor` prop. | + +## Example + +``` +import React from 'react'; +import MapView, {Geojson} from 'react-native-maps'; + +const myPlace = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [64.165329, 48.844287], + } + } + ] +}; + +const Map = props => ( + + + +); +``` diff --git a/docs/heatmap.md b/docs/heatmap.md new file mode 100644 index 000000000..12ab3fe3a --- /dev/null +++ b/docs/heatmap.md @@ -0,0 +1,32 @@ +# `` Component API + +**Note**: Supported on Google Maps only. + +## Props + +| Prop | Type | Default | Note | +| ---------- | ----------------------- | ------- | ----------------------------------------------------------------- | +| `points` | `Array` | | Array of heatmap entries to apply towards density. | +| `radius` | `Number` | `20` | The radius of the heatmap points in pixels, between 10 and 50. | +| `opacity` | `Number` | `0.7` | The opacity of the heatmap. | +| `gradient` | `Object` | | Heatmap gradient configuration (See below for _Gradient Config_). | + +## Gradient Config + +[Android Doc](https://developers.google.com/maps/documentation/android-sdk/utility/heatmap#custom) | [iOS Doc](https://developers.google.com/maps/documentation/ios-sdk/utility/heatmap#customize) + +| Prop | Type | Default | Note | +| -------------- | --------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `colors` | `Array` | | Colors (one or more) to use for gradient. | +| `startPoints` | `Array` | | Array of floating point values from 0 to 1 representing where each color starts. Array length must be equal to `colors` array length. | +| `colorMapSize` | `Number` | `256` | Resolution of color map -- number corresponding to the number of steps colors are interpolated into. | + +## Types + +``` +type WeightedLatLng = { + latitude: Number; + longitude: Number; + weight?: Number; +} +``` diff --git a/docs/installation.md b/docs/installation.md index 3c3f18255..c39f8a72f 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,194 +1,252 @@ # Installation -First, download the library from npm: +Install the library from npm: -``` -npm install react-native-maps --save +```sh +$ npm install react-native-maps +# --- or --- +$ yarn add react-native-maps ``` -Then you must install the native dependencies. You can use [`rnpm`](https://github.com/rnpm/rnpm) to -add native dependencies automatically: +The actual map implementation depends on the platform. On Android, one has to use [Google Maps](https://developers.google.com/maps/documentation/), which in turn requires you to obtain an [API key for the Android SDK](https://developers.google.com/maps/documentation/android-sdk/signup). -`$ rnpm link` +On iOS, one can choose between Google Maps or the native [Apple Maps](https://developer.apple.com/documentation/mapkit/) implementation. -Go to step 4 to configure Google Maps API KEY in Android. +When using Google Maps on iOS, you need also to obtain an [API key for the iOS SDK](https://developers.google.com/maps/documentation/ios-sdk/get-api-key) and include the Google Maps library in your build. The native Apple Maps based implementation works out-of-the-box and is therefore simpler to use at the price of missing some of the features supported by the Google Maps backend. ->This installation should work in physical devices. For Genymotion, please check installation step 5 +> **WARNING**: Before you can start using the Google Maps Platform APIs and SDKs, you must sign up and create a [billing account](https://developers.google.com/maps/gmp-get-started#create-billing-account)! -or do it manually as described below: +--- ## iOS -### Cocoapods -To install using Cocoapods, simply insert the following line into your `Podfile` and run `pod install` - -`pod 'react-native-maps', :path => '../node_modules/react-native-maps'` - -### Manually -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`. -2. Add `libAIRMaps.a` to `Build Phases -> Link Binary With Libraries. -3. Click on `AIRMaps.xcodeproj` in `Libraries` and go the `Build - Settings` tab. Double click the text to the right of `Header Search - Paths` and verify that it has `$(SRCROOT)/../../react-native/React` as well as `$(SRCROOT)/../../react-native/Libraries/Image` - if they - aren't, then add them. This is so XCode is able to find the headers that - the `AIRMaps` source files are referring to by pointing to the - header files installed within the `react-native` `node_modules` - directory. -4. Whenever you want to use it within React code now you can: `var MapView = - require('react-native-maps');` +After installing the npm package, we need to install the pod. -## Android +```sh +$ (cd ios && pod install) +# --- or --- +$ npx pod-install +``` -1. in your `build.gradle` add: -```groovy +### Enabling Google Maps + +If you want to enable Google Maps on iOS, obtain the Google API key and edit your `AppDelegate.m(m)` as follows: + +```diff ++ #import + +@implementation AppDelegate +... + +(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ ++ [GMSServices provideAPIKey:@"_YOUR_API_KEY_"]; // add this line using the api key obtained from Google Console ... -dependencies { - ... - compile 'com.airbnb.android:react-native-maps:0.7.1' -} ``` -For React Native v0.29.0 or above: +The `[GMSServices provideAPIKey]` should be the **first call** of the method. -2. in your application object, add: +Google Maps SDK for iOS requires iOS 14, so make sure that your deployment target is >= 4 in your iOS project settings. -```java -public class MyApplication extends Application implements ReactApplication { - private final ReactNativeHost reactNativeHost = new ReactNativeHost(this) { - @Override protected List getPackages() { - return Arrays.asList( - new MainReactPackage(), - new MapsPackage()); - } - }; +Also make sure that your Podfile deployment target is set to >= 14 at the top of your Podfile, eg: - @Override public ReactNativeHost getReactNativeHost() { - return reactNativeHost; - } -} +```ruby +platform :ios, '14' ``` -For older versions of React Native: +Add the following to your Podfile above the `use_native_modules!` function and run `pod install` in the ios folder: -```java -@Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mReactRootView = new ReactRootView(this); +```ruby +# React Native Maps dependencies - mReactInstanceManager = ReactInstanceManager.builder() - .setApplication(getApplication()) - .setBundleAssetName("index.android.bundle") - .setJSMainModuleName("index.android") - .addPackage(new MainReactPackage()) - .addPackage(new MapsPackage()) // <---- and This! - .setUseDeveloperSupport(BuildConfig.DEBUG) - .setInitialLifecycleState(LifecycleState.RESUMED) - .build(); +rn_maps_path = '../node_modules/react-native-maps' +pod 'react-native-google-maps', :path => rn_maps_path +``` - mReactRootView.startReactApplication(mReactInstanceManager, "MyApp", null); +The app's Info.plist file must contain a NSLocationWhenInUseUsageDescription with a user-facing purpose string explaining clearly and completely why your app needs the location, otherwise Apple will reject your app submission. This is required whether or not you are accessing the users location, as Google Maps iOS SDK contains the code required to access the users location. - setContentView(mReactRootView); -} -``` +That's it, you made it! 👍 + +--- -4. Specify your Google Maps API Key: - > To develop is recommended a ***Browser Key*** without refeer restriction. Go to https://console.developers.google.com/apis/credentials to check your credentials. +## Android + +### Specify your Google Maps API key -Add your API key to your manifest file: +Add your API key to your manifest file (`android/app/src/main/AndroidManifest.xml`): ```xml - - + + ``` -5. ensure that you have Google Play Services installed: - * For Genymotion you can follow [these instructions](http://stackoverflow.com/a/20137324/1424349). - * For a physical device you need to search on Google 'Google Play Services'. There will be a link that takes you to the play store and from there you will see a button to update it (do not search within the Play Store). +### Upgrading to >= v0.31.0 + +The installation documentation previously specified adding `supportLibVersion`, `playServicesVersion` and `androidMapsUtilsVersion` to `build.gradle`. + +None of these keys are required anymore and can be removed, if not used by other modules in your project. + +> **ATTENTION**: If you leave `playServicesVersion` in `build.gradle`, the version must be at least `18.0.0` + +### Ensure that you have Google Play Services installed + +- For the Genymotion emulator, you can follow [these instructions](https://www.genymotion.com/help/desktop/faq/#google-play-services). +- For a physical device you need to search on Google for 'Google Play + Services'. There will be a link that takes you to the Play Store and + from there you will see a button to update it (do not search within the + Play Store). -**Troubleshooting** +## Troubleshooting -If you have a blank map issue, ([#118](https://github.com/lelandrichardson/react-native-maps/issues/118), [#176](https://github.com/lelandrichardson/react-native-maps/issues/176)) try the following lines : +### The map background is blank (Google Maps) -**On iOS :** +If google logo/markers/polylines etc are displayed but the map +background is otherwise blank, this is likely an API key issue. Verify +your API keys and their restrictions. Ensure the native `provideAPIKey` +call is the first line of `didFinishLaunchingWithOptions`. - You have to link dependencies with rnpm and re-run the build : -1. `rnpm link` -2. `react-native run-ios` +Ensure also that the relevant Google APIs have been enabled for your +project from the URLs below: -**On Android :** +- [Google Maps SDK Android](https://console.developers.google.com/apis/library/maps-android-backend.googleapis.com/) +- [Google Maps SDK iOS (if required)](https://console.developers.google.com/apis/library/maps-ios-backend.googleapis.com) -1. Set this Stylesheet in your map component +For reference, you may read the relevant issue reports: ([#118](https://github.com/react-native-maps/react-native-maps/issues/118), [#176](https://github.com/react-native-maps/react-native-maps/issues/176), [#684](https://github.com/react-native-maps/react-native-maps/issues/684)). + +### The map background is gray (Google Maps) + +If you get grey screen on android device create google_maps_api.xml in android/app/src/main/res/values. + +```xml + + (api key here) + ``` + +### No map whatsoever + +Ensure the map component and its container have viewport dimensions. An +example is below: + +```jsx +import MapView, { PROVIDER_GOOGLE } from 'react-native-maps'; // remove PROVIDER_GOOGLE import if not using Google Maps ... const styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - height: 400, - width: 400, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, + container: { + ...StyleSheet.absoluteFillObject, + height: 400, + width: 400, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, }); -module.exports = React.createClass({ - - render: function () { - const { region } = this.props; - console.log(region); - - return ( - - - - - ) - } -}) +export default () => ( + + + + +); +``` + +### Build issues with Google Maps iOS Utils (iOS) + +If your XCode project uses dynamic frameworks (e.g. you also have Swift +code in your project), you cannot install `Google-Maps-iOS-Utils` with +CocoaPods. The issue and a workaround for it has been documented +[here](https://github.com/googlemaps/google-maps-ios-utils/blob/b721e95a500d0c9a4fd93738e83fc86c2a57ac89/Swift.md). + +### Runtime errors on iOS (Apple Maps) + +If you are trying to mount the map with the `GOOGLE_PROVIDER` during +runtime, but your build has been configured for the Apple Maps backend, +a runtime exception will be raised. + +In addition, when using Apple Maps, some Google-only functionalities +have been disabled via runtime errors. + +An exception will be raised if you try to use advanced features that +depend on the [Google Maps SDK for +iOS](https://github.com/googlemaps/google-maps-ios-utils). These include + +- Making markers from KML files +- Heatmap rendering +- Marker clustering +- etc. + +### Clearing caches + +Run these commands to clean caches + +```sh +watchman watch-del-all +npm cache clean + +# Android, 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 +cd .. ``` -2. Run "android" and make sure every packages is updated. -3. If not installed yet, you have to install the following packages : - - Extras / Google Play services - - Extras / Google Repository - - Android 6.0 (API 23) / Google APIs Intel x86 Atom System Image Rev. 13 -4. Check manual installation steps -5. Generate your SHA1 key : - `keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android` -6. Go to [Google API Console](https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend) and select your project, or create one. -In `Overview -> Google Maps API -> Google Maps Android API ` -> Check if it's enabled -Create a new key by clicking on `Create credentials -> API Key -> Android Key`, enter the name of the API key and your SHA1 key, generated before, and create it. -Check installation step 4. +### When using Android studio + +Make sure your Android studio is up to date and set up following the [React Native docs](https://reactnative.dev/docs/environment-setup). + +In particular, the following packages have to be installed: + +- Extras / Google Play services +- Extras / Google Repository +- Android 6.0 (API 23) / Google APIs Intel x86 Atom System Image Rev. 19 +- Android SDK Build-tools 23.0.3 + +### Android emulator issues + +- When starting Android emulator, make sure you have enabled `Wipe user data`. +- If you are using Android Virtual Devices (AVD), ensure that `Use Host GPU` is checked in the settings for your virtual device. +- If using an emulator and the only thing that shows up on the screen is + the message: `[APPNAME] won't run without Google Play services which are not supported by your device.`, you need to change the emulator + CPU/ABI setting to a system image that includes Google APIs. These may + need to be downloaded from the Android SDK Manager first. + +### Google Play Services conflicting issues with other modules + +In case you have multiple modules using the same Google Play Services dependencies (such as `react-native-onesignal`), you can exclude the conflicting dependencies from the modules and import the Google Play Services dependencies in the project-wide `build.gradle` file like the following example: + +```groovy + implementation(project(':react-native-onesignal')){ + exclude group: 'com.google.android.gms' + } + + implementation(project(':react-native-maps')){ + exclude group: 'com.google.android.gms' + } + implementation 'com.google.android.gms:play-services-base:18.0.1' + implementation 'com.google.android.gms:play-services-location:19.0.1' + implementation 'com.google.android.gms:play-services-maps:18.0.2' +``` -7. Clean the cache : - `watchman watch-del-all` - `npm cache clean` +A list of the current dependencies can be found [here](https://developers.google.com/android/guides/setup#list-dependencies). -8. When starting emulator, make sure you have enabled `Wipe user data`. +> **ATTENTION**: `react-native-maps` requires `play-services-maps >= 18.0.0` -9. Run `react-native run-android` +### Trouble with Google Play services -10. At this step it should work, but if not, go to your [Google API Console](https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&pli=1) and create a `Browser key` instead of a `Android key` and go to step 6. +- Make sure that your emulator has Google Play (Go to Android studio -> Virtual Devices -> Check that you have icon in "Play Store" column) +- Click to bottom dots icon in the emulator +- Go to Google Play Tab and click Update diff --git a/docs/mapview.md b/docs/mapview.md index 1b8abc8d7..2cdea8809 100644 --- a/docs/mapview.md +++ b/docs/mapview.md @@ -2,60 +2,102 @@ ## Props -| Prop | Type | Default | Note | -|---|---|---|---| -| `region` | `Region` | | The region to be displayed by the map.

The region is defined by the center coordinates and the span of coordinates to display. -| `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. -| `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 -| `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*! -| `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. -| `showsPointsOfInterest` | `Boolean` | `true` | If `false` points of interest won't be displayed on the map. -| `showsCompass` | `Boolean` | `true` | If `false` compass won't be displayed on the map. -| `showsScale` | `Boolean` | `true` | A Boolean indicating whether the map shows scale information. -| `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. -| `zoomEnabled` | `Boolean` | `true` | If `false` the user won't be able to pinch/zoom the map. -| `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. -| `toolbarEnabled` | `Boolean` | `true` | `Android only` If `false` will hide 'Navigate' and 'Open in Maps' buttons on marker press -| `cacheEnabled` | `Boolean` | `false` | If `true` map will be cached and displayed as a image instead of being interactable, for performance usage. -| `loadingEnabled` | `Boolean` | `false` | If `true` a loading indicator will show while the map is loading. -| `loadingIndicatorColor` | `Color` | `#606060` | Sets loading indicator color, default to `#606060`. -| `loadingBackgroundColor` | `Color` | `#FFFFFF` | Sets loading background color, default to `#FFFFFF`. - - +| Prop | Type | Default | Note | +| --------------------------------- | ------------------------------------ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `provider` | `string` | | The map framework to use.

Either `"google"` for GoogleMaps, otherwise `null` or `undefined` to use the native map framework (`MapKit` in iOS and `GoogleMaps` in android). | +| `region` | `Region` | | The region to be displayed by the map.

The region is defined by the center coordinates and the span of coordinates to display. | +| `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. | +| `camera` | `Camera` | | The camera view the map should display. If you use this, the `region` property is ignored. | +| `initialCamera` | `Camera` | | Like `initialRegion`, use this prop instead of `camera` only if you don't want to control the viewport of the map besides the initial camera setting.

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. | +| `mapPadding` | `EdgePadding` | | Adds custom padding to each side of the map. Useful when map elements/markers are obscured. | +| `paddingAdjustmentBehavior` | 'always' \| 'automatic' \| 'never' | 'never' | Indicates how/when to affect padding with safe area insets (`GoogleMaps` in iOS only) | +| `liteMode` | `Boolean` | `false` | Enable [lite mode](https://developers.google.com/maps/documentation/android-sdk/lite#overview_of_lite_mode). **Note**: Android only. | +| `googleMapId` | `String` | | Google Map ID (only for Provider "google") [google map id](https://developers.google.com/maps/documentation/get-map-id) | +| `mapType` | `String` | `"standard"` | The map type to be displayed.

- standard: standard road map (default)
- none: no map **Note** Not available on MapKit
- satellite: satellite view
- hybrid: satellite view with roads and points of interest overlayed
- terrain: topographic view
- mutedStandard: more subtle, makes markers/lines pop more (iOS 11.0+ only)
- satelliteFlyover: 3D globe with sattelite view (iOS 13.0+ Apple Maps only)
- hybridFlyover: 3D globe with hybrid view (iOS 13.0+ Apple Maps only) | +| `customMapStyle` | `Array` | | Adds custom styling to the map component. See [README](https://github.com/react-native-maps/react-native-maps#customizing-the-map-style) for more information. | +| `userInterfaceStyle` | 'light' \| 'dark' | | Sets the map to the style selected. Default is whatever the system settings is. **Note:** iOS Maps only (aka MapKit). | +| `showsUserLocation` | `Boolean` | `false` | If `true` the users location will be shown on the map. **NOTE**: You need runtime location permissions prior to setting this to true, otherwise it is going to _fail silently_! Checkout the excellent [react-native-permissions](https://github.com/zoontek/react-native-permissions) for this. | +| `userLocationPriority` | 'balanced'\|'high'\|'low'\|'passive' | 'high' | Set power priority of user location tracking. See [Google APIs documentation](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.html). **Note:** Android only. | +| `userLocationUpdateInterval` | `Number` | 5000 | Interval of user location updates in milliseconds. See [Google APIs documentation](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.html). **Note:** Android only. | +| `userLocationFastestInterval` | `Number` | 5000 | Fastest interval the application will actively acquire locations. See [Google APIs documentation](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.html). **Note:** Android only. | +| `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**: Apple Maps only. | +| `userLocationCalloutEnabled` | `Boolean` | `false` | If `true` clicking user location will show the default callout for userLocation annotation. **Note**: Apple Maps only. | +| `showsMyLocationButton` | `Boolean` | `true` | If `false` hide the button to move map to the current user's location. | +| `showsPointsOfInterest` | `Boolean` | `true` | If `false` points of interest won't be displayed on the map. **Note**: Apple Maps only. | +| `showsCompass` | `Boolean` | `true` | If `false` compass won't be displayed on the map. | +| `showsScale` | `Boolean` | `true` | A Boolean indicating whether the map shows scale information. **Note**: Apple Maps only. | +| `showsBuildings` | `Boolean` | `true` | A Boolean indicating whether the map displays extruded building information. | +| `showsTraffic` | `Boolean` | `false` | 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:** 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. | +| `zoomTapEnabled` | `Boolean` | `true` | If `false` the user won't be able to double tap to zoom the map. **Note:** But it will greatly decrease delay of tap gesture recognition. **Note:** Google Maps on iOS only | +| `zoomControlEnabled` | `Boolean` | `true` | If `false` the zoom control at the bottom right of the map won't be visible **Note:** Android only. | +| `minZoomLevel` | `Number` | `0` | Minimum zoom value for the map, must be between 0 and 20. **Note:** Deprecated on Apple Maps. Use cameraZoomRange instead. | +| `maxZoomLevel` | `Number` | `20` | Maximum zoom value for the map, must be between 0 and 20. **Note:** Deprecated on Apple Maps. Use cameraZoomRange instead. | +| `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. | +| `scrollDuringRotateOrZoomEnabled` | `Boolean` | `true` | If `false` the map will stay centered while rotating or zooming. **Note:** Google Maps only | +| `pitchEnabled` | `Boolean` | `true` | If `false` the user won't be able to adjust the camera’s pitch angle. | +| `toolbarEnabled` | `Boolean` | `true` | `Android only` If `false` will hide 'Navigate' and 'Open in Maps' buttons on marker press. If you enable the toolbar, make sure to [edit your AndroidManifest.xml](https://github.com/react-native-maps/react-native-maps/issues/4403#issuecomment-1219856534) | +| `cacheEnabled` | `Boolean` | `false` | If `true` map will be cached and displayed as an image instead of being interactable, for performance usage. **Note:** Apple Maps only | +| `loadingEnabled` | `Boolean` | `false` | If `true` a loading indicator will show while the map is loading. | +| `loadingIndicatorColor` | `Color` | `#606060` | Sets loading indicator color, default to `#606060`. | +| `loadingBackgroundColor` | `Color` | `#FFFFFF` | Sets loading background color, default to `#FFFFFF`. | +| `tintColor` |  `color` | `null` | Sets the tint color of the map. (Changes the color of the position indicator) Defaults to system blue. **Note:** iOS (Apple maps) only. | +| `moveOnMarkerPress` | `Boolean` | `true` | `Android only` If `false` the map won't move when a marker is pressed. | +| `legalLabelInsets` | `EdgeInsets` | | If set, changes the position of the "Legal" label link from the OS default. **Note:** iOS only. | +| `kmlSrc` | `string` | | The URL from KML file. **Note:** Google Maps and Markers only (either Android or iOS with `PROVIDER_GOOGLE`). | +| `compassOffset` | `Point` | | If set, changes the position of the compass. **Note:** iOS Maps only. | +| `isAccessibilityElement` | `Boolean` | `false` | Determines whether the MapView captures VoiceOver touches or forwards them to children. When `true`, map markers are not visible to VoiceOver. **Note:** iOS Maps only. | +| `cameraZoomRange` | `CameraZoomRange` | | Map camera distance limits. `minCenterCoordinateDistance` for minimum distance, `maxCenterCoordinateDistance` for maximum, `animated` for animated zoom range changes. Takes precedence if conflicting with `minZoomLevel`, `maxZoomLevel`. **Note**: iOS 13.0+ only. | ## Events -| Event Name | Returns | Notes -|---|---|---| -| `onRegionChange` | `Region` | Fired when the map ends panning or zooming. -| `onRegionChangeComplete` | `Region` | Fired when the map ends panning or zooming. -| `onPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user taps on the map. -| `onPanDrag` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user presses and drags the map. **NOTE**: for iOS `scrollEnabled` should be set to false to trigger the event -| `onLongPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user makes a "long press" somewhere on the map. -| `onMarkerPress` | | Callback that is called when a marker on the map is tapped by the user. -| `onMarkerSelect` | | Callback that is called when a marker on the map becomes selected. This will be called when the callout for that marker is about to be shown. **Note**: iOS only. -| `onMarkerDeselect` | | Callback that is called when a marker on the map becomes deselected. This will be called when the callout for that marker is about to be hidden. **Note**: iOS only. -| `onCalloutPress` | | Callback that is called when a callout is tapped by the user. -| `onMarkerDragStart` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user initiates a drag on a marker (if it is draggable) -| `onMarkerDrag` | `{ coordinate: LatLng, position: Point }` | Callback called continuously as a marker is dragged -| `onMarkerDragEnd` | `{ coordinate: LatLng, position: Point }` | Callback that is called when a drag on a marker finishes. This is usually the point you will want to setState on the marker's coordinate again - - +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. | +| `onKmlReady` | `KmlContainer` | Callback that is called once the kml is fully loaded. | +| `onRegionChangeStart` | `{ isGesture: boolean }` | Callback that is called once before the region changes, such as when the user starts moving the map. The second parameter is an object containing more details about the move. `isGesture` property indicates if the move was from the user (true) or an animation (false). **Note**: `isGesture` is supported by Google Maps only. | +| `onRegionChange` | (`Region`, `{isGesture: boolean}`) | Callback that is called continuously when the region changes, such as when a user is dragging the map. The second parameter is an object containing more details about the move. `isGesture` property indicates if the move was from the user (true) or an animation (false). **Note**: `isGesture` is supported by Google Maps only. | +| `onRegionChangeComplete` | (`Region`, `{isGesture: boolean}`) | Callback that is called once when the region changes, such as when the user is done moving the map. The second parameter is an object containing more details about the move. `isGesture` property indicates if the move was from the user (true) or an animation (false). **Note**: `isGesture` is supported by Google Maps only. | +| `onUserLocationChange` | `{ coordinate: Location }` | Callback that is called when the underlying map figures our users current location (coordinate also includes isFromMockProvider value for Android API 18 and above). Make sure **showsUserLocation** is set to _true_. | +| `onPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user taps on the map. | +| `onDoublePress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user double taps on the map. **NOTE**: Not supported on Google Maps iOS | +| `onPanDrag` | `{ coordinate: LatLng, position: Point, numberOfTouches: number, }` | Callback that is called when user presses and drags the map. **NOTE** numberOfTouches is iOS only | +| `onPoiClick` | `{ coordinate: LatLng, position: Point, placeId: string, name: string }` | Callback that is called when user click on a POI. | +| `onLongPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when user makes a "long press" somewhere on the map. | +| `onMarkerPress` | | Callback that is called when a marker on the map is tapped by the user. | +| `onMarkerSelect` | | Callback that is called when a marker on the map becomes selected. This will be called when the callout for that marker is about to be shown. | +| `onMarkerDeselect` | | Callback that is called when a marker on the map becomes deselected. This will be called when the callout for that marker is about to be hidden. | +| `onCalloutPress` | | Callback that is called when a callout is tapped by the user. | +| `onMarkerDragStart` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user initiates a drag on a marker (if it is draggable) | +| `onMarkerDrag` | `{ coordinate: LatLng, position: Point }` | Callback called continuously as a marker is dragged | +| `onMarkerDragEnd` | `{ coordinate: LatLng, position: Point }` | Callback that is called when a drag on a marker finishes. This is usually the point you will want to setState on the marker's coordinate again | +| `onIndoorLevelActivated` | `IndoorLevel` | Callback that is called when a level on indoor building is activated | +| `onIndoorBuildingFocused` | `IndoorBuilding` | Callback that is called when a indoor building is focused/unfocused | ## Methods -| Method Name | Arguments | Notes -|---|---|---| -| `animateToRegion` | `region: Region`, `duration: Number` | -| `animateToCoordinate` | `region: Coordinate`, `duration: Number` | -| `fitToElements` | `animated: Boolean` | -| `fitToSuppliedMarkers` | `markerIDs: String[]` | - - +| Method Name | Arguments | Notes | +| --------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `getCamera` | | Returns a `Promise` structure indicating the current camera configuration. | +| `animateCamera` | `camera: Camera`, `{ duration: Number }` | Animate the camera to a new view. You can pass a partial camera object here; any property not given will remain unmodified. | +| `setCamera` | `camera: Camera`, `{ duration: Number }` | Like `animateCamera`, but sets the new view instantly, without an animation. | +| `animateToRegion` | `region: Region`, `duration: Number` | +| `getMapBoundaries` | | `Promise<{northEast: LatLng, southWest: LatLng}>` | +| `setMapBoundaries` | `northEast: LatLng`, `southWest: LatLng` | The boundary is defined by the map's center coordinates, not the device's viewport itself. **Note:** Google Maps only. | +| `setIndoorActiveLevelIndex` | `levelIndex: Number` | +| `fitToElements` | `options: { edgePadding: EdgePadding, animated: Boolean }` | **Note** edgePadding is Google Maps only | +| `fitToSuppliedMarkers` | `markerIDs: String[], options: { edgePadding: EdgePadding, animated: Boolean }` | If you need to use this in `ComponentDidMount`, make sure you put it in a timeout or it will cause performance problems. **Note** edgePadding is Google Maps only | +| `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. | +| `addressForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a address (`Address`). Returns a `Promise
` **Note** Not supported on Google Maps for iOS. | +| `pointForCoordinate` | `coordinate: LatLng` | Converts a map coordinate to a view coordinate (`Point`). Returns a `Promise`. | +| `coordinateForPoint` | `point: Point` | Converts a view coordinate (`Point`) to a map coordinate. Returns a `Promise`. | +| `getMarkersFrames` | `onlyVisible: Boolean` | Get markers' centers and frames in view coordinates. Returns a `Promise<{ "markerID" : { point: Point, frame: Frame } }>`. **Note**: iOS only. | ## Types @@ -68,6 +110,33 @@ type Region { } ``` +``` +type Camera = { + center: { + latitude: number, + longitude: number, + }, + pitch: number, + heading: number, + + // Only on iOS MapKit, in meters. The property is ignored by Google Maps. + altitude: number, + + // Only when using Google Maps. + zoom: number +} +``` + +Latitude and longitude are self explanatory while latitudeDelta and longitudeDelta may not. +On the [developer.apple.com](https://developer.apple.com/reference/mapkit/mkcoordinatespan/1452417-latitudedelta) website this is how the "latitudeDelta" property is explained: + +> The amount of north-to-south distance (measured in degrees) to display on the map. Unlike longitudinal distances, which vary based on the latitude, one degree of latitude is always approximately 111 kilometers (69 miles). + +If this is not enough, you can find a [visual explanation on stackoverflow](https://stackoverflow.com/questions/36685372/how-to-zoom-in-out-in-react-native-map/36688156#36688156). + +Note that when using the `Camera`, MapKit on iOS and Google Maps differ in how the height is specified. For a cross-platform app, it is necessary +to specify both the zoom level and the altitude separately. + ``` type LatLng { latitude: Number, @@ -75,6 +144,18 @@ type LatLng { } ``` +``` +type Location { + latitude: Number, + longitude: Number, + altitude: Number, + timestamp: Number, //Milliseconds since Unix epoch + accuracy: Number, + altitudeAccuracy: Number, + speed: Number, +} +``` + ``` type Point { x: Number, @@ -82,6 +163,15 @@ type Point { } ``` +``` +type Frame { + x: Number, + y: Number, + width: Number, + height: Number, +} +``` + ``` enum MapType : String { "standard", @@ -90,3 +180,75 @@ enum MapType : String { "terrain" //Android only } ``` + +``` +type EdgePadding { + top: Number, + right: Number, + bottom: Number, + left: Number +} +``` + +``` +type EdgeInsets { + top: Number, + left: Number, + bottom: Number, + right: Number +} +``` + +``` +type Marker { + id: String, + coordinate: LatLng, + title: String, + description: String +} +``` + +``` +type KmlContainer { + markers: [Marker] +} +``` + +``` +type IndoorBuilding { + underground: boolean, + activeLevelIndex: Number, + levels: Array, +} +``` + +``` +type IndoorLevel { + index: Number, + name: String, + shortName: String, +} +``` + +``` +type Address { + name: String, + thoroughfare: String, + subThoroughfare: String, + locality: String, + subLocality: String, + administrativeArea: String, + subAdministrativeArea: String, + postalCode: String, + countryCode: String, + country: String, +} +``` + +``` +type CameraZoomRange = { + minCenterCoordinateDistance?: number; + maxCenterCoordinateDistance?: number; + animated?: boolean; +}; +``` diff --git a/docs/marker.md b/docs/marker.md index 557490ab4..e36dfa8f2 100644 --- a/docs/marker.md +++ b/docs/marker.md @@ -1,42 +1,59 @@ -# `` Component API +# `` Component API ## Props -| Prop | Type | Default | Note | -|---|---|---|---| -| `title` | `String` | | The title of the marker. This is only used if the component has no children that are an ``, in which case the default callout behavior will be used, which will show both the `title` and the `description`, if provided. -| `description` | `String` | | The description of the marker. This is only used if the component has no children that are an ``, in which case the default callout behavior will be used, which will show both the `title` and the `description`, if provided. -| `image` | `ImageSource` | | A custom image to be used as the marker's icon. Only local image resources are allowed to be used. -| `pinColor` | `Color` | | If no custom marker view or custom image is provided, the platform default pin will be used, which can be customized by this color. Ignored if a custom marker is being used. -| `coordinate` | `LatLng` | | The coordinate for the marker. -| `centerOffset` | `Point` | | The offset (in points) at which to display the view.

By default, the center point of an annotation view is placed at the coordinate point of the associated annotation. You can use this property to reposition the annotation view as needed. This x and y offset values are measured in points. Positive offset values move the annotation view down and to the right, while negative values move it up and to the left.

For android, see the `anchor` prop. -| `calloutOffset` | `Point` | | The offset (in points) at which to place the callout bubble.

This property determines the additional distance by which to move the callout bubble. When this property is set to (0, 0), the anchor point of the callout bubble is placed on the top-center point of the marker view’s frame. Specifying positive offset values moves the callout bubble down and to the right, while specifying negative values moves it up and to the left.

For android, see the `calloutAnchor` prop. -| `anchor` | `Point` | | Sets the anchor point for the marker.

The anchor specifies the point in the icon image that is anchored to the marker's position on the Earth's surface.

The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0) is the top-left corner of the image, and (1, 1) is the bottom-right corner. The anchoring point in a W x H image is the nearest discrete grid point in a (W + 1) x (H + 1) grid, obtained by scaling the then rounding. For example, in a 4 x 2 image, the anchor point (0.7, 0.6) resolves to the grid point at (3, 1).

For ios, see the `centerOffset` prop. -| `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. +| Prop | Type | Default | Note | +| ------------------------- | ------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `title` | `String` | | The title of the marker. This is only used if the component has no children that are a ``, in which case the default callout behavior will be used, which will show both the `title` and the `description`, if provided. | +| `description` | `String` | | The description of the marker. This is only used if the component has no children that are a ``, in which case the default callout behavior will be used, which will show both the `title` and the `description`, if provided. | +| `image` | `ImageSource`\* | | A custom image to be used as the marker's icon. Only local image resources are allowed to be used. | +| `icon` | `ImageSource`\* | | Marker icon to render (equivalent to `icon` property of GMSMarker Class). Only local image resources are allowed to be used. **Note:** Google maps only! | +| `pinColor` | `Color` | | If no custom marker view or custom image is provided, the platform default pin will be used, which can be customized by this color. Ignored if a custom marker is being used.

For Android, the set of available colors is limited. Unsupported colors will fall back to red. See [#887](https://github.com/react-community/react-native-maps/issues/887) for more information. | +| `coordinate` | `LatLng` | | The coordinate for the marker. | +| `centerOffset` | `Point` | (0, 0) | The offset (in points) at which to display the view.

By default, the center point of an annotation view is placed at the coordinate point of the associated annotation. You can use this property to reposition the annotation view as needed. This x and y offset values are measured in points. Positive offset values move the annotation view down and to the right, while negative values move it up and to the left.

For Google Maps, see the `anchor` prop. | +| `calloutOffset` | `Point` | (0, 0) | The offset (in points) at which to place the callout bubble.

This property determines the additional distance by which to move the callout bubble. When this property is set to (0, 0), the anchor point of the callout bubble is placed on the top-center point of the marker view’s frame. Specifying positive offset values moves the callout bubble down and to the right, while specifying negative values moves it up and to the left.

For Google Maps, see the `calloutAnchor` prop. | +| `anchor` | `Point` | (0.5, 1) | Sets the anchor point for the marker.

The anchor specifies the point in the icon image that is anchored to the marker's position on the Earth's surface.

The anchor point is specified in the continuous space [0.0, 1.0] x [0.0, 1.0], where (0, 0) is the top-left corner of the image, and (1, 1) is the bottom-right corner. The anchoring point in a W x H image is the nearest discrete grid point in a (W + 1) x (H + 1) grid, obtained by scaling the then rounding. For example, in a 4 x 2 image, the anchor point (0.7, 0.6) resolves to the grid point at (3, 1).

For MapKit on iOS, see the `centerOffset` prop. | +| `calloutAnchor` | `Point` | (0.5, 0) | 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 MapKit on iOS, see the `calloutOffset` prop. | +| `flat` | `Boolean` | false | Sets whether this marker should be flat against the map true or a billboard facing the camera. | +| `identifier` | `String` | | An identifier used to reference this marker at a later date. | +| `rotation` | `Float` | 0 | 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). | +| `tappable` | `Boolean` | true | Sets whether marker should be tappable. If set to false, the marker will not have onPress events. **Note**: iOS Google Maps only. | +| `tracksViewChanges` | `Boolean` | true | Sets whether this marker should track changes to child views. When using a child view to create a custom marker this option determines if changes to the content will be tracked after the first render pass. This option has a performance impact so when possible it's recommended to disable it and explictly call the `redraw` method if the marker content changes. | +| `tracksInfoWindowChanges` | `Boolean` | false | Sets whether this marker should track view changes in info window. Enabling it will let marker change content of info window after first render pass, but will lead to decreased performance, so it's recommended to disable it whenever you don't need it. **Note**: iOS Google Maps only. | +| `stopPropagation` | `Boolean` | false | Sets whether this marker should propagate `onPress` events. Enabling it will stop the parent `MapView`'s `onPress` from being called. **Note**: iOS only. Android does not propagate `onPress` events. See [#1132](https://github.com/react-community/react-native-maps/issues/1132) for more information. | +| `opacity` | `Float` | 1.0 | The marker's opacity between 0.0 and 1.0. | +| `isPreselected` | `Boolean` | false | When true, the marker will be pre-selected. Setting this to true allows the user to drag the marker without needing to tap on it once to focus on it. **Note**: iOS Apple Maps only. | +| `key` | `String` | | If no key or non-unique `key` is specified, the `` will be reused, therefore there is an animation when the position is changed. If you want to disable the animation, add a `key` prop with a unique value like `key_${item.longitude}_${item.latitude}`. **Note**: iOS only. | +| `titleVisibility` | `visible \| hidden \| adaptive` | 'hidden' | Visibility of the title text rendered beneath Marker balloon. **Note**: iOS Apple Maps only. | +| `subtitleVisibility` | `visible \| hidden \| adaptive` | 'hidden' | Visibility of the subtitle text rendered beneath Marker balloon. **Note**: iOS Apple Maps only. | +| `useLegacyPinView` | `Boolean` | false | Decide whether legacy MKPinAnnotationView or new MKMarkerAnnotationView should be used. **Note**: iOS Apple Maps only. | + +\* `ImageSource` [docs](https://reactnative.dev/docs/image#imagesource) ## Events -| Event Name | Returns | Notes -|---|---|---| -| `onPress` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user presses on the marker -| `onSelect` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user selects the marker, before the callout is shown. **Note**: iOS only. -| `onDeselect` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the marker is deselected, before the callout is hidden. **Note**: iOS only. -| `onCalloutPress` | | Callback that is called when the user taps the callout view. -| `onDragStart` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user initiates a drag on this marker (if it is draggable) -| `onDrag` | `{ coordinate: LatLng, position: Point }` | Callback called continuously as the marker is dragged -| `onDragEnd` | `{ coordinate: LatLng, position: Point }` | Callback that is called when a drag on this marker finishes. This is usually the point you will want to setState on the marker's coordinate again +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 | +| `onSelect` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user selects the marker, before the callout is shown. | +| `onDeselect` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the marker is deselected, before the callout is hidden. | +| `onCalloutPress` | | Callback that is called when the user taps the callout view. | +| `onDragStart` | `{ coordinate: LatLng, position: Point }` | Callback that is called when the user initiates a drag on this marker (if it is draggable) | +| `onDrag` | `{ coordinate: LatLng, position: Point }` | Callback called continuously as the marker is dragged | +| `onDragEnd` | `{ coordinate: LatLng, position: Point }` | Callback that is called when a drag on this marker finishes. This is usually the point you will want to setState on the marker's coordinate again | ## Methods -| Method Name | Arguments | Notes -|---|---|---| -| `showCallout` | | Shows the callout for this marker -| `hideCallout` | | Hides the callout for this marker - - +| Method Name | Arguments | Notes | +| --------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | +| `showCallout` | | Shows the callout for this marker | +| `hideCallout` | | Hides the callout for this marker | +| `redrawCallout` | | Causes a redraw of the marker's callout. Useful for Google Maps on iOS. **Note**: iOS only. | +| `animateMarkerToCoordinate` | `coordinate: LatLng, duration: number` | Animates marker movement. **Note**: Android only | +| `redraw` | | Causes a redraw of the marker. Useful when there are updates to the marker and `tracksViewChanges` comes with a cost that is too high. | ## Types @@ -53,3 +70,19 @@ type Point { y: Number, } ``` + +## Children Components + +Children components can be added within a Marker and rendered content will replace the marker symbol. This is a way of creating custom markers and allowing use of native SVGs. + +Example: + +``` + + + SF + + +``` + +Displaying a large number of custom markers can have a negative performance impact when tracking changes to the marker content. If displaying a large number of markers consider disabling the `tracksViewChanges` option and manually calling the `redraw` method as required. diff --git a/docs/overlay.md b/docs/overlay.md new file mode 100644 index 000000000..8581e6c30 --- /dev/null +++ b/docs/overlay.md @@ -0,0 +1,26 @@ +# `` Component API + +## Props + +| Prop | Type | Default | Note | +| ---------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | +| `image` | `ImageSource` | A custom image to be used as the overlay. Only required local image resources and uri (as for images located in the net) are allowed to be used. | +| `bounds` | `Array` | | The coordinates for the image (bottom-left corner, top-right corner). ie.`[[lat, long], [lat, long]]` | +| `bearing` | `Number ` | `0` | `Google Maps API only` The bearing in degrees clockwise from north. Values outside the range [0, 360) will be normalized. | +| `tappable` | `Bool` | `false` | `Android only` Boolean to allow an overlay to be tappable and use the onPress function. | +| `opacity` | `Number` | `1.0` | `Google maps only` The opacity of the overlay. | + +## Events + +| Event Name | Returns | Notes | +| ---------- | ------- | --------------------------------------------------------------------------- | +| `onPress` | | `Android only` Callback that is called when the user presses on the overlay | + +## Types + +``` +type LatLng = [ + latitude: Number, + longitude: Number, +] +``` diff --git a/docs/polygon.md b/docs/polygon.md index cc9220ba0..4b18b4b32 100644 --- a/docs/polygon.md +++ b/docs/polygon.md @@ -1,20 +1,28 @@ -# `` Component API +# `` Component API ## Props -| Prop | Type | Default | Note | -|---|---|---|---| -| `coordinates` | `Array` | (Required) | An array of coordinates to describe the polygon -| `strokeWidth` | `Number` | `1` | The stroke width to use for the path. -| `strokeColor` | `String` | `#000` | The stroke color to use for the path. -| `fillColor` | `String` | | The fill color to use for the path. -| `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. -| `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. -| `lineDashPattern` | `Array` | `null` | (iOS only) An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. +| Prop | Type | Default | Note | +| ----------------- | ---------------------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `coordinates` | `Array` | (Required) | An array of coordinates to describe the polygon | +| `holes` | `Array>` | | A 2d array of coordinates to describe holes of the polygon where each hole has at least 3 points. | +| `strokeWidth` | `Number` | `1` | The stroke width to use for the path. | +| `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. | +| `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. | +| `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. | +| `lineDashPattern` | `Array` | `null` | (iOS only) An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. | +| `tappable` | `Bool` | `false` | Boolean to allow a polygon to be tappable and use the onPress function. | +| `zIndex` | `Number` | `0` | (Android Only) The order in which this polygon 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. | +## Events + +| Event Name | Returns | Notes | +| ---------- | ------- | ------------------------------------------------------------ | +| `onPress` | | Callback that is called when the user presses on the polygon | ## Types diff --git a/docs/polyline.md b/docs/polyline.md index f9950e96f..50d63d11f 100644 --- a/docs/polyline.md +++ b/docs/polyline.md @@ -1,19 +1,26 @@ -# `` Component API +# `` Component API ## Props -| Prop | Type | Default | Note | -|---|---|---|---| -| `coordinates` | `Array` | (Required) | An array of coordinates to describe the polygon -| `strokeWidth` | `Number` | `1` | The stroke width to use for the path. -| `strokeColor` | `String` | `#000` | The stroke color to use for the path. -| `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. -| `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. -| `lineDashPattern` | `Array` | `null` | (iOS only) An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. +| Prop | Type | Default | Note | +| ----------------- | --------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `coordinates` | `Array` | (Required) | An array of coordinates to describe the polyline | +| `strokeWidth` | `Number` | `1` | The stroke width to use for the path. | +| `strokeColor` | `String` | `#000, rgba(r,g,b,0.5)` | The stroke color to use for the path. | +| `strokeColors` | `Array` | `null` | The stroke colors to use for the path (iOS only). Must be the same length as `coordinates`. | +| `lineCap` | `String` | `round` | The line cap style to apply to the open ends of the path. Possible values are `butt`, `round` or `square`. Note: lineCap is not yet supported for GoogleMaps provider on iOS. | +| `lineJoin` | `String` | `round` | The line join style to apply to corners of the path. Possible values are `miter`, `round` or `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. | +| `lineDashPattern` | `Array` | `null` | An array of numbers specifying the dash pattern to use for the path. The array contains one or more numbers that indicate the lengths (measured in points) of the line segments and gaps in the pattern. The values in the array alternate, starting with the first line segment length, followed by the first gap length, followed by the second line segment length, and so on. | +| `tappable` | `Bool` | false | Boolean to allow a polyline to be tappable and use the onPress function. | +## Events + +| Event Name | Returns | Notes | +| ---------- | ------- | ------------------------------------------------------------- | +| `onPress` | | Callback that is called when the user presses on the polyline | ## Types @@ -23,3 +30,36 @@ type LatLng { longitude: Number, } ``` + +## Gradient Polylines (iOS MapKit only) + +Gradient polylines can be created by using the `strokeColors` prop. `strokeColors` must be an array with the same number of elements as `coordinates`. + +Example: + +```js +import MapView, {Polyline} from 'react-native-maps'; + + + +; +``` diff --git a/docs/tiles.md b/docs/tiles.md new file mode 100644 index 000000000..0955855d8 --- /dev/null +++ b/docs/tiles.md @@ -0,0 +1,35 @@ +# `` and ``Component API + +## Props + +| Prop | Type | Default | Note | +| ------------------------- | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `urlTemplate` | `String` | | The url template of the map tileserver.

(URLTile) The patterns {x} {y} {z} will be replaced at runtime. For example, http://c.tile.openstreetmap.org/{z}/{x}/{y}.png. It is also possible to refer to tiles in local filesystem with file:///top-level-directory/sub-directory/{z}/{x}/{y}.png URL-format.

(WMSTile) The patterns {minX} {maxX} {minY} {maxY} {width} {height} will be replaced at runtime according to EPSG:900913 specification bounding box. For example, https://demo.geo-solutions.it/geoserver/tiger/wms?service=WMS&version=1.1.0&request=GetMap&layers=tiger:poi&styles=&bbox={minX},{minY},{maxX},{maxY}&width={width}&height={height}&srs=EPSG:900913&format=image/png&transparent=true&format_options=dpi:213. | +| `minimumZ` | `Number` | | The minimum zoom level for this tile overlay. | +| `maximumZ` | `Number` | | The maximum zoom level for this tile overlay. | +| `maximumNativeZ` | `Number` | | (Optional) The maximum native zoom level for this tile overlay i.e. the highest zoom level that the tile server provides. Tiles are auto-scaled for higher zoom levels. | +| `zIndex` | `Number` | `-1` | (Optional) 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. | +| `tileSize` | `Number` | `256` | (Optional) Tile size, default size is 256 (for tiles of 256 _ 256 pixels). High-res (aka 'retina') tiles are 512 (tiles of 512 _ 512 pixels). | +| `doubleTileSize` | `Boolean` | `false` | (Optional) Doubles tile size from 256 to 512 utilising higher zoom levels i.e loading 4 higher zoom level tiles and combining them for one high-resolution tile. iOS does this automatically, even if it is not desirable always. NB! using this makes text labels smaller than in the original map style. | +| `shouldReplaceMapContent` | `Boolean` | `false` | (iOS) Corresponds to MKTileOverlay canReplaceMapContent i.e. if true then underlying iOS basemap is not shown. | +| `flipY` | `Boolean` | `false` | (Optional)Allow tiles using the TMS coordinate system (origin bottom left) to be used, and displayed at their correct coordinates. | +| `tileCachePath` | `String` | | (Optional) Enable caching of tiles in the specified directory. Directory can be specified either as a normal path or in URL format (`file://`). Tiles are stored in tileCachePath directory as `/{z}/{x}/{y}` i.e. in sub-directories 2-levels deep, filename is tile y-coordinate without any filetype-extension.

NB! All cache management needs to be implemented by client e.g. deleting tiles to manage use of storage space etc. | +| `tileCacheMaxAge` | `Number` | | (Optional) Defines maximum age in seconds for a cached tile before it's refreshed. NB! Refresh logic is "serve-stale-while-refresh" i.e. to ensure map availability a stale (over max age) tile is served while a tile refresh process is started in the background. | +| `offlineMode` | `Boolean` | `false` | (Optional) Sets offline-mode. In offline-mode tiles are not fetched from the tile servers, rather only tiles stored in the cache directory are used. Furthermore automated tile scaling is activated: if tile at a desired zoom level is not found from the cache directory, then lower zoom level tile is used (up to 4 levels lower) and scaled. | +| `opacity` | `Number` | | (Optional) Map layer opacity. Value between 0 - 1, with 0 meaning fully transparent. | + +## Using tile components + +Both `URLTile` and `WMSTile` components require access to a tileserver. Please note that due to overuse OpenStreetMap free tileservers do not work on Android with `URLTile` and are not recommended to be used for iOS either for other than small scale test use. + +`URLTile` and `WMSTile` components both implement caching and non-caching usage modes. Non-caching is the default and uses Android and iOS default implementations for tile overlays. Non-caching means that there is no caching of map tiles in React Native Maps implementation, however both iOS and Android do some limited caching with their default implementation. Non-caching mode works well when network connectivity is good and is simple to use. + +Caching mode is activated when `tileCachePath` property is defined as a valid directory where React Native app has read & write access. In caching mode any new tile fetched from the tileserver is also cached in the directory defined by `tileCachePath` property and cached version is used for any future access to the tile, which means tiles can be used also when there is no or limited network connectivity. Caching also allows pre-loading of tiles to the cache directory, enabling offline map loading functionality which is typical feature in many map applications. + +NB! There is no cache size management in React Native Maps, rather developer needs to implement in JS side code some mechanism to watch the size of tile cache directory and either automatically remove old tiles or allow user to delete the tiles. + +`tileCacheMaxAge` property enables automatic renewal of cached tiles. Refresh logic is "serve-stale-while-refresh" meaning that when expired (i.e. older than defined `tileCacheMaxAge`) tile is requested the old cached ("stale") tile is served while a background process is started to refresh ("update") the expired tile. Refresh logic is optimised for poor network connectivity situation / app offline-mode use: if tile is cached, user is always served a tile even if it is stale, before a tile refresh is attempted and a failure in tile refresh is not a problem as stale tile remains available. Refresh logic is "lazy" i.e. tile refresh check is only done when tile is accessed. Any other refresh logic needs to be implemented in JS side code e.g. at set time intervals checking through tiles in cache and refreshing tiles as needed. + +`offlineMode` property enables mode in which only cached tiles are used, tile fetch from tileserver is not even attempted. This is useful for situations when network connectivity is poor and repeated & possibly failed attempts to fetch tiles (even with caching enabled) from tileserver would increase device power consumption. Another benefit of offline-mode is that if a requested tile is missing from the cache then lower zoom level tiles are used if available and served (after scaling). This is especially useful when user has pre-loaded an area of map for certain zoom levels, allowing user to zoom in the map to higher zoom levels than which were pre-loaded. + +`maximumNativeZ` property works both in caching and non-caching mode, however it is very much recommended to be used in caching mode: it sets the highest zoom level for tiles to be fetched from the tileserver. Any higher zoom levels that `maximumNativeZ` will be created by scaling a lower zoom level tile. This will help to manage the cache size, since an increase in zoom level means 4 times as many tiles are needed as in a previous level. Depending on type of map tiles and their quality a `maximumNativeZ` set at 15 - 17 and `maximumZ` set at 18 - 20 will often give good results, allowing user to zoom in deep into the map with good enough map visual quality. diff --git a/e2e/jest.config.js b/e2e/jest.config.js new file mode 100644 index 000000000..3308ed164 --- /dev/null +++ b/e2e/jest.config.js @@ -0,0 +1,13 @@ +/** @type {import('@jest/types').Config.InitialOptions} */ +module.exports = { + preset: 'react-native', + rootDir: '..', + testMatch: ['/e2e/**/*.test.js'], + testTimeout: 120000, + maxWorkers: 1, + globalSetup: 'detox/runners/jest/globalSetup', + globalTeardown: 'detox/runners/jest/globalTeardown', + reporters: ['detox/runners/jest/reporter'], + testEnvironment: 'detox/runners/jest/testEnvironment', + verbose: true, +}; diff --git a/e2e/starter.test.js b/e2e/starter.test.js new file mode 100644 index 000000000..515ebc202 --- /dev/null +++ b/e2e/starter.test.js @@ -0,0 +1,15 @@ +import {by, device, element} from 'detox'; + +describe('Example', () => { + beforeAll(async () => { + await device.launchApp(); + }); + + beforeEach(async () => { + await device.reloadReactNative(); + }); + + it('should show static map', async () => { + await element(by.id('StaticMapButton')).tap(); + }); +}); diff --git a/example/.bundle/config b/example/.bundle/config new file mode 100644 index 000000000..848943bb5 --- /dev/null +++ b/example/.bundle/config @@ -0,0 +1,2 @@ +BUNDLE_PATH: "vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/example/.flowconfig b/example/.flowconfig deleted file mode 100644 index c3edaf946..000000000 --- a/example/.flowconfig +++ /dev/null @@ -1,65 +0,0 @@ -[ignore] - -# We fork some components by platform. -.*/*.web.js -.*/*.android.js - -# Some modules have their own node_modules with overlap -.*/node_modules/node-haste/.* - -# Ugh -.*/node_modules/babel.* -.*/node_modules/babylon.* -.*/node_modules/invariant.* - -# Ignore react and fbjs where there are overlaps, but don't ignore -# anything that react-native relies on -.*/node_modules/fbjs/lib/Map.js -.*/node_modules/fbjs/lib/Promise.js -.*/node_modules/fbjs/lib/fetch.js -.*/node_modules/fbjs/lib/ExecutionEnvironment.js -.*/node_modules/fbjs/lib/isEmpty.js -.*/node_modules/fbjs/lib/crc32.js -.*/node_modules/fbjs/lib/ErrorUtils.js - -# Flow has a built-in definition for the 'react' module which we prefer to use -# over the currently-untyped source -.*/node_modules/react/react.js -.*/node_modules/react/lib/React.js -.*/node_modules/react/lib/ReactDOM.js - -# Ignore commoner tests -.*/node_modules/commoner/test/.* - -# See https://github.com/facebook/flow/issues/442 -.*/react-tools/node_modules/commoner/lib/reader.js - -# Ignore jest -.*/node_modules/jest-cli/.* - -# Ignore Website -.*/website/.* - -[include] - -[libs] -node_modules/react-native/Libraries/react-native/react-native-interface.js - -[options] -module.system=haste - -munge_underscores=true - -module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' -module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub' - -suppress_type=$FlowIssue -suppress_type=$FlowFixMe -suppress_type=$FixMe - -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ -suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy - -[version] -0.21.0 diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index 94fc86711..000000000 --- a/example/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -project.xcworkspace - -# Android/IJ -# -.idea -.gradle -local.properties - -# node.js -# -node_modules/ -npm-debug.log diff --git a/example/.node-version b/example/.node-version new file mode 100644 index 000000000..b6a7d89c6 --- /dev/null +++ b/example/.node-version @@ -0,0 +1 @@ +16 diff --git a/example/.watchmanconfig b/example/.watchmanconfig index 9e26dfeeb..0967ef424 100644 --- a/example/.watchmanconfig +++ b/example/.watchmanconfig @@ -1 +1 @@ -{} \ No newline at end of file +{} diff --git a/example/App.js b/example/App.js deleted file mode 100644 index bec55f008..000000000 --- a/example/App.js +++ /dev/null @@ -1,130 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - View, - StyleSheet, - TouchableOpacity, - ScrollView, - Text, -} = ReactNative; -var DisplayLatLng = require('./examples/DisplayLatLng'); -var ViewsAsMarkers = require('./examples/ViewsAsMarkers'); -var EventListener = require('./examples/EventListener'); -var MarkerTypes = require('./examples/MarkerTypes'); -var DraggableMarkers = require('./examples/DraggableMarkers'); -var PolygonCreator = require('./examples/PolygonCreator'); -var PolylineCreator = require('./examples/PolylineCreator'); -var AnimatedViews = require('./examples/AnimatedViews'); -var AnimatedMarkers = require('./examples/AnimatedMarkers'); -var Callouts = require('./examples/Callouts'); -var Overlays = require('./examples/Overlays'); -var DefaultMarkers = require('./examples/DefaultMarkers'); -var CachedMap = require('./examples/CachedMap'); -var LoadingMap = require('./examples/LoadingMap'); -var TakeSnapshot = require('./examples/TakeSnapshot'); -var FitToSuppliedMarkers = require('./examples/FitToSuppliedMarkers'); - -var App = React.createClass({ - - getInitialState() { - return { Component: null }; - }, - - renderExample([Component, title], i) { - return ( - this.setState({ Component })} - > - {title} - - ); - }, - - renderBackButton() { - return ( - this.setState({ Component: null })} - > - - - ); - }, - - renderExamples(examples) { - var { Component } = this.state; - return ( - - {Component && } - {Component && this.renderBackButton()} - {!Component && ( - - {examples.map(this.renderExample)} - - )} - - ); - }, - - render() { - return this.renderExamples([ - [DisplayLatLng, 'Tracking Position'], - [ViewsAsMarkers, 'Arbitrary Views as Markers'], - [EventListener, 'Events'], - [MarkerTypes, 'Image Based Markers'], - [DraggableMarkers, 'Draggable Markers'], - [PolygonCreator, 'Polygon Creator'], - [PolylineCreator, 'Polyline Creator'], - [AnimatedViews, 'Animating with MapViews'], - [AnimatedMarkers, 'Animated Marker Position'], - [Callouts, 'Custom Callouts'], - [Overlays, 'Circles, Polygons, and Polylines'], - [DefaultMarkers, 'Default Markers'], - [TakeSnapshot, 'Take Snapshot'], - [CachedMap, 'Cached Map'], - [LoadingMap, 'Map with loading'], - [FitToSuppliedMarkers, 'Focus Map On Markers'], - ]); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - scrollview: { - alignItems: 'center', - paddingVertical: 40, - }, - button: { - flex: 1, - marginTop: 10, - backgroundColor: 'rgba(220,220,220,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - back: { - position: 'absolute', - top: 20, - left: 12, - backgroundColor: 'rgba(255,255,255,0.4)', - padding: 12, - borderRadius: 20, - width: 80, - alignItems: 'center', - justifyContent: 'center', - }, -}); - -module.exports = App; diff --git a/example/Gemfile b/example/Gemfile new file mode 100644 index 000000000..85d7f6828 --- /dev/null +++ b/example/Gemfile @@ -0,0 +1,9 @@ +source 'https://rubygems.org' + +# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version +ruby ">= 2.6.10" + +# Exclude problematic versions of cocoapods and activesupport that causes build failures. +gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' +gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' +gem 'xcodeproj', '< 1.26.0' diff --git a/example/Gemfile.lock b/example/Gemfile.lock new file mode 100644 index 000000000..433657f98 --- /dev/null +++ b/example/Gemfile.lock @@ -0,0 +1,117 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.7) + base64 + nkf + rexml + activesupport (7.2.2) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + algoliasearch (1.27.5) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + base64 (0.2.0) + benchmark (0.4.0) + bigdecimal (3.1.8) + claide (1.1.0) + cocoapods (1.15.2) + addressable (~> 2.8) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.15.2) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 2.1, < 3.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.6.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.8.0) + nap (~> 1.0) + ruby-macho (>= 2.3.0, < 3.0) + xcodeproj (>= 1.23.0, < 2.0) + cocoapods-core (1.15.2) + activesupport (>= 5.0, < 8) + addressable (~> 2.8) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + netrc (~> 0.11) + public_suffix (~> 4.0) + typhoeus (~> 1.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (2.1) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.2.0) + colored2 (3.1.2) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) + drb (2.2.1) + escape (0.0.4) + ethon (0.16.0) + ffi (>= 1.15.0) + ffi (1.17.0) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + httpclient (2.8.3) + i18n (1.14.6) + concurrent-ruby (~> 1.0) + json (2.8.1) + logger (1.6.1) + minitest (5.25.1) + molinillo (0.8.0) + nanaimo (0.3.0) + nap (1.1.0) + netrc (0.11.0) + nkf (0.2.0) + public_suffix (4.0.7) + rexml (3.3.9) + ruby-macho (2.5.1) + securerandom (0.3.1) + typhoeus (1.4.1) + ethon (>= 0.9.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + xcodeproj (1.25.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.3.0) + rexml (>= 3.3.6, < 4.0) + +PLATFORMS + ruby + +DEPENDENCIES + activesupport (>= 6.1.7.5, != 7.1.0) + cocoapods (>= 1.13, != 1.15.1, != 1.15.0) + xcodeproj (< 1.26.0) + +RUBY VERSION + ruby 3.2.2p53 + +BUNDLED WITH + 2.5.23 diff --git a/example/android/app/_BUCK b/example/android/app/_BUCK new file mode 100644 index 000000000..337313866 --- /dev/null +++ b/example/android/app/_BUCK @@ -0,0 +1,55 @@ +# To learn about Buck see [Docs](https://buckbuild.com/). +# To run your application with Buck: +# - install Buck +# - `npm start` - to start the packager +# - `cd android` +# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` +# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck +# - `buck install -r android/app` - compile, install and run application +# + +load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") + +lib_deps = [] + +create_aar_targets(glob(["libs/*.aar"])) + +create_jar_targets(glob(["libs/*.jar"])) + +android_library( + name = "all-libs", + exported_deps = lib_deps, +) + +android_library( + name = "app-code", + srcs = glob([ + "src/main/java/**/*.java", + ]), + deps = [ + ":all-libs", + ":build_config", + ":res", + ], +) + +android_build_config( + name = "build_config", + package = "com.rnmshowcase", +) + +android_resource( + name = "res", + package = "com.rnmshowcase", + res = "src/main/res", +) + +android_binary( + name = "app", + keystore = "//android/keystores:debug", + manifest = "src/main/AndroidManifest.xml", + package_type = "debug", + deps = [ + ":app-code", + ], +) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index c0fcb022f..16ec3d901 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,103 +1,122 @@ apply plugin: "com.android.application" - -import com.android.build.OutputFile +apply plugin: "org.jetbrains.kotlin.android" +apply plugin: "com.facebook.react" /** - * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets - * and bundleReleaseJsAndAssets). - * These basically call `react-native bundle` with the correct arguments during the Android build - * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the - * bundle directly from the development server. Below you can see all the possible configurations - * and their defaults. If you decide to add a configuration block, make sure to add it before the - * `apply from: "react.gradle"` line. - * - * project.ext.react = [ - * // the name of the generated asset file containing your JS bundle - * bundleAssetName: "index.android.bundle", - * - * // the entry file for bundle generation - * entryFile: "index.android.js", - * - * // whether to bundle JS and assets in debug mode - * bundleInDebug: false, - * - * // whether to bundle JS and assets in release mode - * bundleInRelease: true, - * - * // whether to bundle JS and assets in another build variant (if configured). - * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants - * // The configuration property can be in the following formats - * // 'bundleIn${productFlavor}${buildType}' - * // 'bundleIn${buildType}' - * // bundleInFreeDebug: true, - * // bundleInPaidRelease: true, - * // bundleInBeta: true, - * - * // the root of your project, i.e. where "package.json" lives - * root: "../../", - * - * // where to put the JS bundle asset in debug mode - * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", - * - * // where to put the JS bundle asset in release mode - * jsBundleDirRelease: "$buildDir/intermediates/assets/release", - * - * // where to put drawable resources / React Native assets, e.g. the ones you use via - * // require('./image.png')), in debug mode - * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", - * - * // where to put drawable resources / React Native assets, e.g. the ones you use via - * // require('./image.png')), in release mode - * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", - * - * // by default the gradle tasks are skipped if none of the JS files or assets change; this means - * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to - * // date; if you have any other folders that you want to ignore for performance reasons (gradle - * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ - * // for example, you might want to remove it from here. - * inputExcludes: ["android/**", "ios/**"] - * ] + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. */ +react { + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '../..' + // root = file("../../") + // The folder where the react-native NPM package is. Default is ../../node_modules/react-native + // reactNativeDir = file("../../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen + // codegenDir = file("../../node_modules/@react-native/codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../../node_modules/react-native/cli.js + // cliFile = file("../../node_modules/react-native/cli.js") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] -apply from: "react.gradle" + /* Autolinking */ + autolinkLibrariesWithApp() +} /** - * Run Proguard to shrink the Java bytecode in release builds. + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ def enableProguardInReleaseBuilds = false +/** + * The preferred build flavor of JavaScriptCore (JSC) + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + ndkVersion rootProject.ext.ndkVersion + buildToolsVersion rootProject.ext.buildToolsVersion + compileSdk rootProject.ext.compileSdkVersion + namespace "com.rnmshowcase" defaultConfig { - applicationId "com.airbnb.android.react.maps.example" - minSdkVersion 16 - targetSdkVersion 23 + applicationId "com.rnmshowcase" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" } + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } buildTypes { + debug { + signingConfig signingConfigs.debug + } release { + // Caution! In production, you need to generate your own keystore file. + // see https://reactnative.dev/docs/signed-apk-android. + signingConfig signingConfigs.debug minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" } } - // applicationVariants are e.g. debug, release - applicationVariants.all { variant -> - variant.outputs.each { output -> - // For each separate APK per architecture, set a unique version code as described here: - // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits - def versionCodes = ["armeabi-v7a":1, "x86":2] - def abi = output.getFilter(OutputFile.ABI) - if (abi != null) { // null for the universal-debug, universal-release variants - output.versionCodeOverride = - versionCodes.get(abi) * 1048576 + defaultConfig.versionCode - } - } - } } dependencies { - compile project(':react-native-maps') + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") + + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") + } else { + implementation jscFlavor + } } + + +apply plugin: 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' diff --git a/example/android/app/build_defs.bzl b/example/android/app/build_defs.bzl new file mode 100644 index 000000000..fff270f8d --- /dev/null +++ b/example/android/app/build_defs.bzl @@ -0,0 +1,19 @@ +"""Helper definitions to glob .aar and .jar targets""" + +def create_aar_targets(aarfiles): + for aarfile in aarfiles: + name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] + lib_deps.append(":" + name) + android_prebuilt_aar( + name = name, + aar = aarfile, + ) + +def create_jar_targets(jarfiles): + for jarfile in jarfiles: + name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] + lib_deps.append(":" + name) + prebuilt_jar( + name = name, + binary_jar = jarfile, + ) diff --git a/example/android/app/debug.keystore b/example/android/app/debug.keystore new file mode 100644 index 000000000..364e105ed Binary files /dev/null and b/example/android/app/debug.keystore differ diff --git a/example/android/app/proguard-rules.pro b/example/android/app/proguard-rules.pro index 7d72e4692..11b025724 100644 --- a/example/android/app/proguard-rules.pro +++ b/example/android/app/proguard-rules.pro @@ -8,60 +8,3 @@ # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Disabling obfuscation is useful if you collect stack traces from production crashes -# (unless you are using a system that supports de-obfuscate the stack traces). --dontobfuscate - -# React Native - -# Keep our interfaces so they can be used by other ProGuard rules. -# See http://sourceforge.net/p/proguard/bugs/466/ --keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip --keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters - -# Do not strip any method/class that is annotated with @DoNotStrip --keep @com.facebook.proguard.annotations.DoNotStrip class * --keepclassmembers class * { - @com.facebook.proguard.annotations.DoNotStrip *; -} - --keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { - void set*(***); - *** get*(); -} - --keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } --keep class * extends com.facebook.react.bridge.NativeModule { *; } --keepclassmembers,includedescriptorclasses class * { native ; } --keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } --keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } --keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } - --dontwarn com.facebook.react.** - -# okhttp - --keepattributes Signature --keepattributes *Annotation* --keep class com.squareup.okhttp.** { *; } --keep interface com.squareup.okhttp.** { *; } --dontwarn com.squareup.okhttp.** - -# okio - --keep class sun.misc.Unsafe { *; } --dontwarn java.nio.file.* --dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement --dontwarn okio.** - -# stetho - --dontwarn com.facebook.stetho.** diff --git a/example/android/app/react.gradle b/example/android/app/react.gradle deleted file mode 100644 index 11a4f8b87..000000000 --- a/example/android/app/react.gradle +++ /dev/null @@ -1,97 +0,0 @@ -import org.apache.tools.ant.taskdefs.condition.Os - -def config = project.hasProperty("react") ? project.react : []; - -def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" -def entryFile = config.entryFile ?: "index.android.js" - -// because elvis operator -def elvisFile(thing) { - return thing ? file(thing) : null; -} - -def reactRoot = elvisFile(config.root) ?: file("../../") -def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] - -void runBefore(String dependentTaskName, Task task) { - Task dependentTask = tasks.findByPath(dependentTaskName); - if (dependentTask != null) { - dependentTask.dependsOn task - } -} - -gradle.projectsEvaluated { - // Grab all build types and product flavors - def buildTypes = android.buildTypes.collect { type -> type.name } - def productFlavors = android.productFlavors.collect { flavor -> flavor.name } - - // When no product flavors defined, use empty - if (!productFlavors) productFlavors.add('') - - productFlavors.each { productFlavorName -> - buildTypes.each { buildTypeName -> - // Create variant and target names - def targetName = "${productFlavorName.capitalize()}${buildTypeName.capitalize()}" - def targetPath = productFlavorName ? - "${productFlavorName}/${buildTypeName}" : - "${buildTypeName}" - - // React js bundle directories - def jsBundleDirConfigName = "jsBundleDir${targetName}" - def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?: - file("$buildDir/intermediates/assets/${targetPath}") - - def resourcesDirConfigName = "jsBundleDir${targetName}" - def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?: - file("$buildDir/intermediates/res/merged/${targetPath}") - def jsBundleFile = file("$jsBundleDir/$bundleAssetName") - - // Bundle task name for variant - def bundleJsAndAssetsTaskName = "bundle${targetName}JsAndAssets" - - def currentBundleTask = tasks.create( - name: bundleJsAndAssetsTaskName, - type: Exec) { - group = "react" - description = "bundle JS and assets for ${targetName}." - - // Create dirs if they are not there (e.g. the "clean" task just ran) - doFirst { - jsBundleDir.mkdirs() - resourcesDir.mkdirs() - } - - // Set up inputs and outputs so gradle can cache the result - inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) - outputs.dir jsBundleDir - outputs.dir resourcesDir - - // Set up the call to the react-native cli - workingDir reactRoot - - // Set up dev mode - def devEnabled = !targetName.toLowerCase().contains("release") - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", - "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir - } else { - commandLine "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", - "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir - } - - enabled config."bundleIn${targetName}" || - config."bundleIn${buildTypeName.capitalize()}" ?: - targetName.toLowerCase().contains("release") - } - - // Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process - currentBundleTask.dependsOn("merge${targetName}Resources") - currentBundleTask.dependsOn("merge${targetName}Assets") - - runBefore("processArmeabi-v7a${targetName}Resources", currentBundleTask) - runBefore("processX86${targetName}Resources", currentBundleTask) - runBefore("processUniversal${targetName}Resources", currentBundleTask) - runBefore("process${targetName}Resources", currentBundleTask) - } - } -} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..4b185bc15 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 2742d5793..ebc562ce6 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,27 +1,32 @@ + package="com.rnmshowcase"> + + + android:roundIcon="@mipmap/ic_launcher_round" + android:allowBackup="false" + android:theme="@style/AppTheme" + android:supportsRtl="true"> + android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" + android:launchMode="singleTask" + android:windowSoftInputMode="adjustResize" + android:exported="true"> - - + android:value="${MAPS_API_KEY}"/> - diff --git a/example/android/app/src/main/java/com/airbnb/android/react/maps/example/ExampleApplication.java b/example/android/app/src/main/java/com/airbnb/android/react/maps/example/ExampleApplication.java deleted file mode 100644 index 1a7f6382f..000000000 --- a/example/android/app/src/main/java/com/airbnb/android/react/maps/example/ExampleApplication.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.airbnb.android.react.maps.example; - -import android.app.Application; - -import com.airbnb.android.react.maps.MapsPackage; -import com.facebook.react.ReactApplication; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.shell.MainReactPackage; - -import java.util.Arrays; -import java.util.List; - -public class ExampleApplication extends Application implements ReactApplication { - private final ReactNativeHost reactNativeHost = new ReactNativeHost(this) { - @Override protected boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } - - @Override protected List getPackages() { - return Arrays.asList( - new MainReactPackage(), - new MapsPackage()); - } - }; - - @Override - public ReactNativeHost getReactNativeHost() { - return reactNativeHost; - } -} \ No newline at end of file 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 deleted file mode 100644 index eec2349a4..000000000 --- a/example/android/app/src/main/java/com/airbnb/android/react/maps/example/MainActivity.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.airbnb.android.react.maps.example; - -import com.facebook.react.ReactActivity; - - -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"; - } - -} diff --git a/example/android/app/src/main/java/com/rnmshowcase/MainActivity.kt b/example/android/app/src/main/java/com/rnmshowcase/MainActivity.kt new file mode 100644 index 000000000..ac6e4d039 --- /dev/null +++ b/example/android/app/src/main/java/com/rnmshowcase/MainActivity.kt @@ -0,0 +1,19 @@ +package com.rnmshowcase + +import com.facebook.react.ReactActivity +import com.facebook.react.ReactActivityDelegate +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled +import com.facebook.react.defaults.DefaultReactActivityDelegate +class MainActivity : ReactActivity() { + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + override fun getMainComponentName(): String = "rnmshowcase" + /** + * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] + * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] + */ + override fun createReactActivityDelegate(): ReactActivityDelegate = + DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) +} diff --git a/example/android/app/src/main/java/com/rnmshowcase/MainApplication.kt b/example/android/app/src/main/java/com/rnmshowcase/MainApplication.kt new file mode 100644 index 000000000..8e6cd231b --- /dev/null +++ b/example/android/app/src/main/java/com/rnmshowcase/MainApplication.kt @@ -0,0 +1,44 @@ +package com.rnmshowcase + +import android.app.Application +import com.facebook.react.PackageList +import com.facebook.react.ReactApplication +import com.facebook.react.ReactHost +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackage +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load +import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost +import com.facebook.react.defaults.DefaultReactNativeHost +import com.facebook.react.soloader.OpenSourceMergedSoMapping +import com.facebook.soloader.SoLoader + +class MainApplication : Application(), ReactApplication { + + override val reactNativeHost: ReactNativeHost = + object : DefaultReactNativeHost(this) { + override fun getPackages(): List = + PackageList(this).packages.apply { + // Packages that cannot be autolinked yet can be added manually here, for example: + // add(MyReactNativePackage()) + } + + override fun getJSMainModuleName(): String = "index" + + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + } + + override val reactHost: ReactHost + get() = getDefaultReactHost(applicationContext, reactNativeHost) + + override fun onCreate() { + super.onCreate() + SoLoader.init(this, OpenSourceMergedSoMapping) + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + load() + } + } +} diff --git a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml new file mode 100644 index 000000000..f35d99620 --- /dev/null +++ b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bccc..a2f590828 100644 Binary files a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..1b5239980 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0cbd..ff10afd6e 100644 Binary files a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..115a4c768 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0e7..dcd3cd808 100644 Binary files a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..459ca609d Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72cdd..8ca12fe02 100644 Binary files a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..8e19b410a Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..b824ebdd4 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..4c19a13c2 Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml index 434cb5248..b62dfa7cc 100644 --- a/example/android/app/src/main/res/values/strings.xml +++ b/example/android/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - AirMapsExplorer + rnmshowcase diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 319eb0ca1..7ba83a2ad 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,9 @@ - diff --git a/example/android/build.gradle b/example/android/build.gradle index c9f24f32a..93b6ac60f 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,24 +1,24 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - buildscript { + ext { + buildToolsVersion = "35.0.0" + minSdkVersion = 24 + compileSdkVersion = 35 + targetSdkVersion = 34 + ndkVersion = "26.1.10909125" + kotlinVersion = "1.9.24" + } repositories { - jcenter() + google() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath("com.android.tools.build:gradle") + classpath("com.facebook.react:react-native-gradle-plugin") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") + classpath("de.undercouch:gradle-download-task:5.5.0") + classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1" } } -allprojects { - repositories { - mavenLocal() - jcenter() - maven { - // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm - url "$projectDir/../../node_modules/react-native/android" - } - } -} +apply plugin: "com.facebook.react.rootproject" + diff --git a/example/android/gradle.properties b/example/android/gradle.properties index c04c048e8..5e24e3aa8 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -9,11 +9,31 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +newArchEnabled=true + +# Use this property to enable or disable the Hermes JS engine. +# If set to false, you will be using JSC instead. +hermesEnabled=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar index b5166dad4..41d9927a4 100644 Binary files a/example/android/gradle/wrapper/gradle-wrapper.jar and b/example/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index b3c42aa00..79eb9d003 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip diff --git a/example/android/gradlew b/example/android/gradlew index 91a7e269e..f5feea6d6 100755 --- a/example/android/gradlew +++ b/example/android/gradlew @@ -1,79 +1,130 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -82,83 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat index aec99730b..9b42019c7 100644 --- a/example/android/gradlew.bat +++ b/example/android/gradlew.bat @@ -1,4 +1,22 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -8,26 +26,30 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -35,54 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 3574854c2..9a0c300ba 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,2 +1,6 @@ -include ':app', ':react-native-maps' -project(':react-native-maps').projectDir = new File(rootProject.projectDir, '../../android/lib') \ No newline at end of file +pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } +plugins { id("com.facebook.react.settings") } +extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } +rootProject.name = 'rnmshowcase' +include ':app' +includeBuild('../node_modules/@react-native/gradle-plugin') diff --git a/example/app.json b/example/app.json new file mode 100644 index 000000000..0ba19e0bf --- /dev/null +++ b/example/app.json @@ -0,0 +1,4 @@ +{ + "name": "rnmshowcase", + "displayName": "rnmshowcase" +} diff --git a/example/babel.config.js b/example/babel.config.js new file mode 100644 index 000000000..204c84c93 --- /dev/null +++ b/example/babel.config.js @@ -0,0 +1,20 @@ +const path = require('path'); +const pak = require('../package.json'); + +module.exports = { + presets: [ + ['module:@react-native/babel-preset'], + ['@babel/preset-typescript', {allowDeclareFields: true}], // to allow use of declare context + ], + plugins: [ + [ + 'module-resolver', + { + extensions: ['.tsx', '.ts', '.js', '.json'], + alias: { + [pak.name]: path.join(__dirname, '..', pak.source), + }, + }, + ], + ], +}; diff --git a/example/examples/AnimatedMarkers.js b/example/examples/AnimatedMarkers.js deleted file mode 100644 index 22001efd1..000000000 --- a/example/examples/AnimatedMarkers.js +++ /dev/null @@ -1,111 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Animated, - Platform, -} = ReactNative; - -var MapView = require('react-native-maps'); - -var screen = Dimensions.get('window'); - -const ASPECT_RATIO = screen.width / screen.height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; - -var AnimatedMarkers = React.createClass({ - getInitialState() { - return { - coordinate: new Animated.Region({ - latitude: LATITUDE, - longitude: LONGITUDE, - }), - }; - }, - - animate() { - var { coordinate } = this.state; - coordinate.timing({ - latitude: LATITUDE + (Math.random() - 0.5) * LATITUDE_DELTA / 2, - longitude: LONGITUDE + (Math.random() - 0.5) * LONGITUDE_DELTA / 2, - }).start(); - }, - - render() { - return ( - - - - - - - Animate - - - - ); - }, -}); - - - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - bubble: { - flex: 1, - backgroundColor: 'rgba(255,255,255,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - latlng: { - width: 200, - alignItems: 'stretch', - }, - button: { - width: 80, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - }, -}); - -module.exports = AnimatedMarkers; diff --git a/example/examples/AnimatedPriceMarker.js b/example/examples/AnimatedPriceMarker.js deleted file mode 100644 index 40047cf48..000000000 --- a/example/examples/AnimatedPriceMarker.js +++ /dev/null @@ -1,104 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - PropTypes, -} = React; - -var { - StyleSheet, - View, - Text, - Animated, -} = ReactNative; - -const PriceMarker = ({ amount, selected, style }) => { - - var background = selected.interpolate({ - inputRange: [0, 1], - outputRange: ['#FF5A5F', '#4da2ab'], - }); - - var border = selected.interpolate({ - inputRange: [0, 1], - outputRange: ['#D23F44', '#007a87'], - }); - - return ( - - - $ - {amount} - - - - - ); -}; - - -PriceMarker.propTypes = { - amount: PropTypes.number.isRequired, -}; - -const styles = StyleSheet.create({ - container: { - flexDirection: 'column', - alignSelf: 'flex-start', - }, - bubble: { - flex: 0, - flexDirection: 'row', - alignSelf: 'flex-start', - backgroundColor: '#FF5A5F', - paddingVertical: 2, - paddingHorizontal: 4, - borderRadius: 3, - borderColor: '#D23F44', - borderWidth: 0.5, - }, - dollar: { - color: '#fff', - fontSize: 10, - }, - amount: { - color: '#fff', - fontSize: 13, - }, - arrow: { - backgroundColor: 'transparent', - borderColor: 'transparent', - borderWidth: 4, - borderTopColor: '#FF5A5F', - alignSelf: 'center', - marginTop: -9, - }, - arrowBorder: { - backgroundColor: 'transparent', - borderColor: 'transparent', - borderWidth: 4, - borderTopColor: '#D23F44', - alignSelf: 'center', - marginTop: -0.5, - }, - selectedBubble: { - backgroundColor: '#4da2ab', - borderColor: '#007a87', - }, - selectedArrow: { - borderTopColor: '#4da2ab', - - }, - selectedArrowBorder: { - borderTopColor: '#007a87', - }, -}); - -module.exports = PriceMarker; diff --git a/example/examples/AnimatedViews.js b/example/examples/AnimatedViews.js deleted file mode 100644 index 519f22243..000000000 --- a/example/examples/AnimatedViews.js +++ /dev/null @@ -1,427 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Animated, - Platform, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PanController = require('./PanController'); -var PriceMarker = require('./AnimatedPriceMarker'); - -var screen = Dimensions.get('window'); - -const ASPECT_RATIO = screen.width / screen.height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; - -var ITEM_SPACING = 10; -var ITEM_PREVIEW = 10; -var ITEM_WIDTH = screen.width - 2 * ITEM_SPACING - 2 * ITEM_PREVIEW; -var SNAP_WIDTH = ITEM_WIDTH + ITEM_SPACING; -var ITEM_PREVIEW_HEIGHT = 150; -var SCALE_END = screen.width / ITEM_WIDTH; -var BREAKPOINT1 = 246; -var BREAKPOINT2 = 350; - -const ANDROID = Platform.OS === 'android'; - -var AnimatedViews = React.createClass({ - getInitialState() { - const panX = new Animated.Value(0); - const panY = new Animated.Value(0); - - const scrollY = panY.interpolate({ - inputRange: [-1, 1], - outputRange: [1, -1], - }); - - const scrollX = panX.interpolate({ - inputRange: [-1, 1], - outputRange: [1, -1], - }); - - const scale = scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [1, 1.6], - extrapolate: 'clamp', - }); - - const translateY = scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [0, -100], - extrapolate: 'clamp', - }); - - const ONE = new Animated.Value(1); - - const markers = [ - { - id: 0, - amount: 99, - coordinate: { - latitude: LATITUDE, - longitude: LONGITUDE, - }, - }, - { - id: 1, - amount: 199, - coordinate: { - latitude: LATITUDE + 0.004, - longitude: LONGITUDE - 0.004, - }, - }, - { - id: 2, - amount: 285, - coordinate: { - latitude: LATITUDE - 0.004, - longitude: LONGITUDE - 0.004, - }, - }, - ]; - - const animations = markers.map((m, i) => { - const xLeft = -SNAP_WIDTH * i + SNAP_WIDTH/2; - const xRight = -SNAP_WIDTH * i - SNAP_WIDTH/2; - const xPos = -SNAP_WIDTH * i; - - const isIndex = panX.interpolate({ - inputRange: [xRight - 1, xRight, xLeft, xLeft+1], - outputRange: [0, 1, 1, 0], - extrapolate: 'clamp', - }); - - const isNotIndex = panX.interpolate({ - inputRange: [xRight - 1, xRight, xLeft, xLeft+1], - outputRange: [1, 0, 0, 1], - extrapolate: 'clamp', - }); - - const center = panX.interpolate({ - inputRange: [xPos - 10, xPos, xPos + 10], - outputRange: [0, 1, 0], - extrapolate: 'clamp', - }); - - const selected = panX.interpolate({ - inputRange: [xRight, xPos, xLeft], - outputRange: [0, 1, 0], - extrapolate: 'clamp', - }); - - const translateY = Animated.multiply(isIndex, panY); - - const translateX = panX; - - const anim = Animated.multiply(isIndex, scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [0, 1], - extrapolate: 'clamp', - })); - - const scale = Animated.add(ONE, Animated.multiply(isIndex, scrollY.interpolate({ - inputRange: [BREAKPOINT1, BREAKPOINT2], - outputRange: [0, SCALE_END-1], - extrapolate: 'clamp', - }))); - - // [0 => 1] - var opacity = scrollY.interpolate({ - inputRange: [BREAKPOINT1, BREAKPOINT2], - outputRange: [0, 1], - extrapolate: 'clamp', - }); - - // if i === index: [0 => 0] - // if i !== index: [0 => 1] - opacity = Animated.multiply(isNotIndex, opacity); - - - // if i === index: [1 => 1] - // if i !== index: [1 => 0] - opacity = opacity.interpolate({ - inputRange: [0, 1], - outputRange: [1, 0], - }); - - var markerOpacity = scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [0, 1], - extrapolate: 'clamp', - }); - - markerOpacity = Animated.multiply(isNotIndex, markerOpacity).interpolate({ - inputRange: [0, 1], - outputRange: [1, 0], - }); - - var markerScale = selected.interpolate({ - inputRange: [0, 1], - outputRange: [1, 1.2], - }); - - return { - translateY, - translateX, - scale, - opacity, - anim, - center, - selected, - markerOpacity, - markerScale, - }; - }); - - return { - panX, - panY, - animations, - index: 0, - canMoveHorizontal: true, - scrollY, - scrollX, - scale, - translateY, - markers, - region: new Animated.Region({ - latitude: LATITUDE, - longitude: LONGITUDE, - latitudeDelta: LATITUDE_DELTA, - longitudeDelta: LONGITUDE_DELTA, - }), - }; - }, - - componentDidMount() { - var { region, panX, panY, scrollX, markers } = this.state; - - panX.addListener(this.onPanXChange); - panY.addListener(this.onPanYChange); - - region.stopAnimation(); - region.timing({ - latitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.latitude), - }), - longitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.longitude), - }), - duration: 0, - }).start(); - }, - - onStartShouldSetPanResponder(e) { - // we only want to move the view if they are starting the gesture on top - // of the view, so this calculates that and returns true if so. If we return - // false, the gesture should get passed to the map view appropriately. - var { panY } = this.state; - var { pageY } = e.nativeEvent; - var topOfMainWindow = ITEM_PREVIEW_HEIGHT + 1 * panY.__getValue(); - var topOfTap = screen.height - pageY; - - return topOfTap < topOfMainWindow; - }, - - onMoveShouldSetPanResponder(e) { - var { panY } = this.state; - var { pageY } = e.nativeEvent; - var topOfMainWindow = ITEM_PREVIEW_HEIGHT + 1 * panY.__getValue(); - var topOfTap = screen.height - pageY; - - return topOfTap < topOfMainWindow; - }, - - onPanXChange({ value }) { - var { index, region, panX, markers } = this.state; - var newIndex = Math.floor((-1 * value + SNAP_WIDTH / 2) / SNAP_WIDTH); - if (index !== newIndex) { - this.setState({ index: newIndex }); - } - }, - - onPanYChange({ value }) { - var { canMoveHorizontal, region, scrollY, scrollX, markers, index } = this.state; - var shouldBeMovable = Math.abs(value) < 2; - if (shouldBeMovable !== canMoveHorizontal) { - this.setState({ canMoveHorizontal: shouldBeMovable }); - if (!shouldBeMovable) { - var { coordinate } = markers[index]; - region.stopAnimation(); - region.timing({ - latitude: scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [coordinate.latitude, coordinate.latitude - LATITUDE_DELTA * 0.5 * 0.375], - extrapolate: 'clamp', - }), - latitudeDelta: scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [LATITUDE_DELTA, LATITUDE_DELTA * 0.5], - extrapolate: 'clamp', - }), - longitudeDelta: scrollY.interpolate({ - inputRange: [0, BREAKPOINT1], - outputRange: [LONGITUDE_DELTA, LONGITUDE_DELTA * 0.5], - extrapolate: 'clamp', - }), - duration: 0, - }).start(); - } else { - region.stopAnimation(); - region.timing({ - latitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.latitude), - }), - longitude: scrollX.interpolate({ - inputRange: markers.map((m, i) => i * SNAP_WIDTH), - outputRange: markers.map(m => m.coordinate.longitude), - }), - duration: 0, - }).start(); - } - } - }, - - onRegionChange(region) { - //this.state.region.setValue(region); - }, - - render() { - const { - panX, - panY, - animations, - canMoveHorizontal, - markers, - region, - } = this.state; - - return ( - - - - {markers.map((marker, i) => { - const { - selected, - markerOpacity, - markerScale, - } = animations[i]; - - return ( - - - - ); - })} - - - {markers.map((marker, i) => { - const { - translateY, - translateX, - scale, - opacity, - } = animations[i]; - - return ( - - - ); - })} - - - - ); - }, -}); - - - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - itemContainer: { - backgroundColor: 'transparent', - flexDirection: 'row', - paddingHorizontal: ITEM_SPACING / 2 + ITEM_PREVIEW, - position: 'absolute', - //top: screen.height - ITEM_PREVIEW_HEIGHT - 64, - paddingTop: screen.height - ITEM_PREVIEW_HEIGHT - 64, - //paddingTop: !ANDROID ? 0 : screen.height - ITEM_PREVIEW_HEIGHT - 64, - }, - map: { - backgroundColor: 'transparent', - position: 'absolute', - top: 0, - left: 0, - bottom: 0, - right: 0, - }, - item: { - width: ITEM_WIDTH, - height: screen.height + 2 * ITEM_PREVIEW_HEIGHT, - backgroundColor: 'red', - marginHorizontal: ITEM_SPACING / 2, - overflow: 'hidden', - borderRadius: 3, - borderColor: '#000', - }, -}); - -module.exports = AnimatedViews; diff --git a/example/examples/CachedMap.js b/example/examples/CachedMap.js deleted file mode 100644 index c46e8665d..000000000 --- a/example/examples/CachedMap.js +++ /dev/null @@ -1,1824 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - Text, - View, - Dimensions, - StyleSheet, - ListView, - TouchableOpacity, -} = ReactNative; - -var MapView = require('react-native-maps'); - -const HORIZONTAL_PADDING = 12; -const VERTICAL_PADDING = 6; - -var CachedMap = React.createClass({ - getInitialState() { - var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); - return { - dataSource: ds.cloneWithRows(COUNTRIES), - cache: true, - }; - }, - - toggleCache() { - // a hack to force listview to reload with the same data - this.setState({ - dataSource: this.state.dataSource.cloneWithRows([]), - }); - this.setState({ - cache: !this.state.cache, - dataSource: this.state.dataSource.cloneWithRows(COUNTRIES), - }); - }, - - render() { - var { width, height } = Dimensions.get('window'); - return ( - - - - {this.state.cache ? "Cached" : "Not cached"} - - - { - return ( - - {region.name} - - - - - - ); - }} /> - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - flex: 1, - }, - item: { - backgroundColor: 'white', - paddingHorizontal: HORIZONTAL_PADDING, - paddingVertical: VERTICAL_PADDING, - }, - bubble: { - backgroundColor: 'rgba(0,128,255,1.0)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - button: { - width: 100, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - justifyContent: 'flex-end', - }, - buttonText: { - color: 'white' - } -}); - -const COUNTRIES = [ - { - "latitude": 33.93911, - "longitudeDelta": 10.0, - "name": "Afghanistan", - "longitude": 67.709953, - "latitudeDelta": 10.0 - }, - { - "latitude": 41.153332, - "longitudeDelta": 10.0, - "name": "Albania", - "longitude": 20.168331, - "latitudeDelta": 10.0 - }, - { - "latitude": 28.033886, - "longitudeDelta": 10.0, - "name": "Algeria", - "longitude": 1.659626, - "latitudeDelta": 10.0 - }, - { - "latitude": -14.270972, - "longitudeDelta": 10.0, - "name": "American Samoa", - "longitude": -170.132217, - "latitudeDelta": 10.0 - }, - { - "latitude": 42.546245, - "longitudeDelta": 10.0, - "name": "Andorra", - "longitude": 1.601554, - "latitudeDelta": 10.0 - }, - { - "latitude": -11.202692, - "longitudeDelta": 10.0, - "name": "Angola", - "longitude": 17.873887, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.220554, - "longitudeDelta": 10.0, - "name": "Anguilla", - "longitude": -63.068615, - "latitudeDelta": 10.0 - }, - { - "latitude": -75.250973, - "longitudeDelta": 10.0, - "name": "Antarctica", - "longitude": -0.071389, - "latitudeDelta": 10.0 - }, - { - "latitude": 17.060816, - "longitudeDelta": 10.0, - "name": "Antigua and Barbuda", - "longitude": -61.796428, - "latitudeDelta": 10.0 - }, - { - "latitude": -38.416097, - "longitudeDelta": 10.0, - "name": "Argentina", - "longitude": -63.616672, - "latitudeDelta": 10.0 - }, - { - "latitude": 40.069099, - "longitudeDelta": 10.0, - "name": "Armenia", - "longitude": 45.038189, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.52111, - "longitudeDelta": 10.0, - "name": "Aruba", - "longitude": -69.968338, - "latitudeDelta": 10.0 - }, - { - "latitude": -25.274398, - "longitudeDelta": 10.0, - "name": "Australia", - "longitude": 133.775136, - "latitudeDelta": 10.0 - }, - { - "latitude": 47.516231, - "longitudeDelta": 10.0, - "name": "Austria", - "longitude": 14.550072, - "latitudeDelta": 10.0 - }, - { - "latitude": 40.143105, - "longitudeDelta": 10.0, - "name": "Azerbaijan", - "longitude": 47.576927, - "latitudeDelta": 10.0 - }, - { - "latitude": 25.03428, - "longitudeDelta": 10.0, - "name": "Bahamas", - "longitude": -77.39628, - "latitudeDelta": 10.0 - }, - { - "latitude": 25.930414, - "longitudeDelta": 10.0, - "name": "Bahrain", - "longitude": 50.637772, - "latitudeDelta": 10.0 - }, - { - "latitude": 23.684994, - "longitudeDelta": 10.0, - "name": "Bangladesh", - "longitude": 90.356331, - "latitudeDelta": 10.0 - }, - { - "latitude": 13.193887, - "longitudeDelta": 10.0, - "name": "Barbados", - "longitude": -59.543198, - "latitudeDelta": 10.0 - }, - { - "latitude": 53.709807, - "longitudeDelta": 10.0, - "name": "Belarus", - "longitude": 27.953389, - "latitudeDelta": 10.0 - }, - { - "latitude": 50.503887, - "longitudeDelta": 10.0, - "name": "Belgium", - "longitude": 4.469936, - "latitudeDelta": 10.0 - }, - { - "latitude": 17.189877, - "longitudeDelta": 10.0, - "name": "Belize", - "longitude": -88.49765, - "latitudeDelta": 10.0 - }, - { - "latitude": 9.30769, - "longitudeDelta": 10.0, - "name": "Benin", - "longitude": 2.315834, - "latitudeDelta": 10.0 - }, - { - "latitude": 32.321384, - "longitudeDelta": 10.0, - "name": "Bermuda", - "longitude": -64.75737, - "latitudeDelta": 10.0 - }, - { - "latitude": 27.514162, - "longitudeDelta": 10.0, - "name": "Bhutan", - "longitude": 90.433601, - "latitudeDelta": 10.0 - }, - { - "latitude": -16.290154, - "longitudeDelta": 10.0, - "name": "Bolivia", - "longitude": -63.588653, - "latitudeDelta": 10.0 - }, - { - "latitude": 43.915886, - "longitudeDelta": 10.0, - "name": "Bosnia and Herzegovina", - "longitude": 17.679076, - "latitudeDelta": 10.0 - }, - { - "latitude": -22.328474, - "longitudeDelta": 10.0, - "name": "Botswana", - "longitude": 24.684866, - "latitudeDelta": 10.0 - }, - { - "latitude": -54.423199, - "longitudeDelta": 10.0, - "name": "Bouvet Island", - "longitude": 3.413194, - "latitudeDelta": 10.0 - }, - { - "latitude": -14.235004, - "longitudeDelta": 10.0, - "name": "Brazil", - "longitude": -51.92528, - "latitudeDelta": 10.0 - }, - { - "latitude": -6.343194, - "longitudeDelta": 10.0, - "name": "British Indian Ocean Territory", - "longitude": 71.876519, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.420695, - "longitudeDelta": 10.0, - "name": "British Virgin Islands", - "longitude": -64.639968, - "latitudeDelta": 10.0 - }, - { - "latitude": 4.535277, - "longitudeDelta": 10.0, - "name": "Brunei", - "longitude": 114.727669, - "latitudeDelta": 10.0 - }, - { - "latitude": 42.733883, - "longitudeDelta": 10.0, - "name": "Bulgaria", - "longitude": 25.48583, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.238333, - "longitudeDelta": 10.0, - "name": "Burkina Faso", - "longitude": -1.561593, - "latitudeDelta": 10.0 - }, - { - "latitude": -3.373056, - "longitudeDelta": 10.0, - "name": "Burundi", - "longitude": 29.918886, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.565679, - "longitudeDelta": 10.0, - "name": "Cambodia", - "longitude": 104.990963, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.369722, - "longitudeDelta": 10.0, - "name": "Cameroon", - "longitude": 12.354722, - "latitudeDelta": 10.0 - }, - { - "latitude": 56.130366, - "longitudeDelta": 10.0, - "name": "Canada", - "longitude": -106.346771, - "latitudeDelta": 10.0 - }, - { - "latitude": 16.002082, - "longitudeDelta": 10.0, - "name": "Cape Verde", - "longitude": -24.013197, - "latitudeDelta": 10.0 - }, - { - "latitude": 19.513469, - "longitudeDelta": 10.0, - "name": "Cayman Islands", - "longitude": -80.566956, - "latitudeDelta": 10.0 - }, - { - "latitude": 6.611111, - "longitudeDelta": 10.0, - "name": "Central African Republic", - "longitude": 20.939444, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.454166, - "longitudeDelta": 10.0, - "name": "Chad", - "longitude": 18.732207, - "latitudeDelta": 10.0 - }, - { - "latitude": -35.675147, - "longitudeDelta": 10.0, - "name": "Chile", - "longitude": -71.542969, - "latitudeDelta": 10.0 - }, - { - "latitude": 35.86166, - "longitudeDelta": 10.0, - "name": "China", - "longitude": 104.195397, - "latitudeDelta": 10.0 - }, - { - "latitude": -10.447525, - "longitudeDelta": 10.0, - "name": "Christmas Island", - "longitude": 105.690449, - "latitudeDelta": 10.0 - }, - { - "latitude": -12.164165, - "longitudeDelta": 10.0, - "name": "Cocos [Keeling] Islands", - "longitude": 96.870956, - "latitudeDelta": 10.0 - }, - { - "latitude": 4.570868, - "longitudeDelta": 10.0, - "name": "Colombia", - "longitude": -74.297333, - "latitudeDelta": 10.0 - }, - { - "latitude": -11.875001, - "longitudeDelta": 10.0, - "name": "Comoros", - "longitude": 43.872219, - "latitudeDelta": 10.0 - }, - { - "latitude": -4.038333, - "longitudeDelta": 10.0, - "name": "Congo [DRC]", - "longitude": 21.758664, - "latitudeDelta": 10.0 - }, - { - "latitude": -0.228021, - "longitudeDelta": 10.0, - "name": "Congo [Republic]", - "longitude": 15.827659, - "latitudeDelta": 10.0 - }, - { - "latitude": -21.236736, - "longitudeDelta": 10.0, - "name": "Cook Islands", - "longitude": -159.777671, - "latitudeDelta": 10.0 - }, - { - "latitude": 9.748917, - "longitudeDelta": 10.0, - "name": "Costa Rica", - "longitude": -83.753428, - "latitudeDelta": 10.0 - }, - { - "latitude": 45.1, - "longitudeDelta": 10.0, - "name": "Croatia", - "longitude": 15.2, - "latitudeDelta": 10.0 - }, - { - "latitude": 21.521757, - "longitudeDelta": 10.0, - "name": "Cuba", - "longitude": -77.781167, - "latitudeDelta": 10.0 - }, - { - "latitude": 35.126413, - "longitudeDelta": 10.0, - "name": "Cyprus", - "longitude": 33.429859, - "latitudeDelta": 10.0 - }, - { - "latitude": 49.817492, - "longitudeDelta": 10.0, - "name": "Czech Republic", - "longitude": 15.472962, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.539989, - "longitudeDelta": 10.0, - "name": "C\u00f4te d'Ivoire", - "longitude": -5.54708, - "latitudeDelta": 10.0 - }, - { - "latitude": 56.26392, - "longitudeDelta": 10.0, - "name": "Denmark", - "longitude": 9.501785, - "latitudeDelta": 10.0 - }, - { - "latitude": 11.825138, - "longitudeDelta": 10.0, - "name": "Djibouti", - "longitude": 42.590275, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.414999, - "longitudeDelta": 10.0, - "name": "Dominica", - "longitude": -61.370976, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.735693, - "longitudeDelta": 10.0, - "name": "Dominican Republic", - "longitude": -70.162651, - "latitudeDelta": 10.0 - }, - { - "latitude": -1.831239, - "longitudeDelta": 10.0, - "name": "Ecuador", - "longitude": -78.183406, - "latitudeDelta": 10.0 - }, - { - "latitude": 26.820553, - "longitudeDelta": 10.0, - "name": "Egypt", - "longitude": 30.802498, - "latitudeDelta": 10.0 - }, - { - "latitude": 13.794185, - "longitudeDelta": 10.0, - "name": "El Salvador", - "longitude": -88.89653, - "latitudeDelta": 10.0 - }, - { - "latitude": 1.650801, - "longitudeDelta": 10.0, - "name": "Equatorial Guinea", - "longitude": 10.267895, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.179384, - "longitudeDelta": 10.0, - "name": "Eritrea", - "longitude": 39.782334, - "latitudeDelta": 10.0 - }, - { - "latitude": 58.595272, - "longitudeDelta": 10.0, - "name": "Estonia", - "longitude": 25.013607, - "latitudeDelta": 10.0 - }, - { - "latitude": 9.145, - "longitudeDelta": 10.0, - "name": "Ethiopia", - "longitude": 40.489673, - "latitudeDelta": 10.0 - }, - { - "latitude": -51.796253, - "longitudeDelta": 10.0, - "name": "Falkland Islands [Islas Malvinas]", - "longitude": -59.523613, - "latitudeDelta": 10.0 - }, - { - "latitude": 61.892635, - "longitudeDelta": 10.0, - "name": "Faroe Islands", - "longitude": -6.911806, - "latitudeDelta": 10.0 - }, - { - "latitude": -16.578193, - "longitudeDelta": 10.0, - "name": "Fiji", - "longitude": 179.414413, - "latitudeDelta": 10.0 - }, - { - "latitude": 61.92411, - "longitudeDelta": 10.0, - "name": "Finland", - "longitude": 25.748151, - "latitudeDelta": 10.0 - }, - { - "latitude": 46.227638, - "longitudeDelta": 10.0, - "name": "France", - "longitude": 2.213749, - "latitudeDelta": 10.0 - }, - { - "latitude": 3.933889, - "longitudeDelta": 10.0, - "name": "French Guiana", - "longitude": -53.125782, - "latitudeDelta": 10.0 - }, - { - "latitude": -17.679742, - "longitudeDelta": 10.0, - "name": "French Polynesia", - "longitude": -149.406843, - "latitudeDelta": 10.0 - }, - { - "latitude": -49.280366, - "longitudeDelta": 10.0, - "name": "French Southern Territories", - "longitude": 69.348557, - "latitudeDelta": 10.0 - }, - { - "latitude": -0.803689, - "longitudeDelta": 10.0, - "name": "Gabon", - "longitude": 11.609444, - "latitudeDelta": 10.0 - }, - { - "latitude": 13.443182, - "longitudeDelta": 10.0, - "name": "Gambia", - "longitude": -15.310139, - "latitudeDelta": 10.0 - }, - { - "latitude": 31.354676, - "longitudeDelta": 10.0, - "name": "Gaza Strip", - "longitude": 34.308825, - "latitudeDelta": 10.0 - }, - { - "latitude": 42.315407, - "longitudeDelta": 10.0, - "name": "Georgia", - "longitude": 43.356892, - "latitudeDelta": 10.0 - }, - { - "latitude": 51.165691, - "longitudeDelta": 10.0, - "name": "Germany", - "longitude": 10.451526, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.946527, - "longitudeDelta": 10.0, - "name": "Ghana", - "longitude": -1.023194, - "latitudeDelta": 10.0 - }, - { - "latitude": 36.137741, - "longitudeDelta": 10.0, - "name": "Gibraltar", - "longitude": -5.345374, - "latitudeDelta": 10.0 - }, - { - "latitude": 39.074208, - "longitudeDelta": 10.0, - "name": "Greece", - "longitude": 21.824312, - "latitudeDelta": 10.0 - }, - { - "latitude": 71.706936, - "longitudeDelta": 10.0, - "name": "Greenland", - "longitude": -42.604303, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.262776, - "longitudeDelta": 10.0, - "name": "Grenada", - "longitude": -61.604171, - "latitudeDelta": 10.0 - }, - { - "latitude": 16.995971, - "longitudeDelta": 10.0, - "name": "Guadeloupe", - "longitude": -62.067641, - "latitudeDelta": 10.0 - }, - { - "latitude": 13.444304, - "longitudeDelta": 10.0, - "name": "Guam", - "longitude": 144.793731, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.783471, - "longitudeDelta": 10.0, - "name": "Guatemala", - "longitude": -90.230759, - "latitudeDelta": 10.0 - }, - { - "latitude": 49.465691, - "longitudeDelta": 10.0, - "name": "Guernsey", - "longitude": -2.585278, - "latitudeDelta": 10.0 - }, - { - "latitude": 9.945587, - "longitudeDelta": 10.0, - "name": "Guinea", - "longitude": -9.696645, - "latitudeDelta": 10.0 - }, - { - "latitude": 11.803749, - "longitudeDelta": 10.0, - "name": "Guinea-Bissau", - "longitude": -15.180413, - "latitudeDelta": 10.0 - }, - { - "latitude": 4.860416, - "longitudeDelta": 10.0, - "name": "Guyana", - "longitude": -58.93018, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.971187, - "longitudeDelta": 10.0, - "name": "Haiti", - "longitude": -72.285215, - "latitudeDelta": 10.0 - }, - { - "latitude": -53.08181, - "longitudeDelta": 10.0, - "name": "Heard Island and McDonald Islands", - "longitude": 73.504158, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.199999, - "longitudeDelta": 10.0, - "name": "Honduras", - "longitude": -86.241905, - "latitudeDelta": 10.0 - }, - { - "latitude": 22.396428, - "longitudeDelta": 10.0, - "name": "Hong Kong", - "longitude": 114.109497, - "latitudeDelta": 10.0 - }, - { - "latitude": 47.162494, - "longitudeDelta": 10.0, - "name": "Hungary", - "longitude": 19.503304, - "latitudeDelta": 10.0 - }, - { - "latitude": 64.963051, - "longitudeDelta": 10.0, - "name": "Iceland", - "longitude": -19.020835, - "latitudeDelta": 10.0 - }, - { - "latitude": 20.593684, - "longitudeDelta": 10.0, - "name": "India", - "longitude": 78.96288, - "latitudeDelta": 10.0 - }, - { - "latitude": -0.789275, - "longitudeDelta": 10.0, - "name": "Indonesia", - "longitude": 113.921327, - "latitudeDelta": 10.0 - }, - { - "latitude": 32.427908, - "longitudeDelta": 10.0, - "name": "Iran", - "longitude": 53.688046, - "latitudeDelta": 10.0 - }, - { - "latitude": 33.223191, - "longitudeDelta": 10.0, - "name": "Iraq", - "longitude": 43.679291, - "latitudeDelta": 10.0 - }, - { - "latitude": 53.41291, - "longitudeDelta": 10.0, - "name": "Ireland", - "longitude": -8.24389, - "latitudeDelta": 10.0 - }, - { - "latitude": 54.236107, - "longitudeDelta": 10.0, - "name": "Isle of Man", - "longitude": -4.548056, - "latitudeDelta": 10.0 - }, - { - "latitude": 31.046051, - "longitudeDelta": 10.0, - "name": "Israel", - "longitude": 34.851612, - "latitudeDelta": 10.0 - }, - { - "latitude": 41.87194, - "longitudeDelta": 10.0, - "name": "Italy", - "longitude": 12.56738, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.109581, - "longitudeDelta": 10.0, - "name": "Jamaica", - "longitude": -77.297508, - "latitudeDelta": 10.0 - }, - { - "latitude": 36.204824, - "longitudeDelta": 10.0, - "name": "Japan", - "longitude": 138.252924, - "latitudeDelta": 10.0 - }, - { - "latitude": 49.214439, - "longitudeDelta": 10.0, - "name": "Jersey", - "longitude": -2.13125, - "latitudeDelta": 10.0 - }, - { - "latitude": 30.585164, - "longitudeDelta": 10.0, - "name": "Jordan", - "longitude": 36.238414, - "latitudeDelta": 10.0 - }, - { - "latitude": 48.019573, - "longitudeDelta": 10.0, - "name": "Kazakhstan", - "longitude": 66.923684, - "latitudeDelta": 10.0 - }, - { - "latitude": -0.023559, - "longitudeDelta": 10.0, - "name": "Kenya", - "longitude": 37.906193, - "latitudeDelta": 10.0 - }, - { - "latitude": -3.370417, - "longitudeDelta": 10.0, - "name": "Kiribati", - "longitude": -168.734039, - "latitudeDelta": 10.0 - }, - { - "latitude": 42.602636, - "longitudeDelta": 10.0, - "name": "Kosovo", - "longitude": 20.902977, - "latitudeDelta": 10.0 - }, - { - "latitude": 29.31166, - "longitudeDelta": 10.0, - "name": "Kuwait", - "longitude": 47.481766, - "latitudeDelta": 10.0 - }, - { - "latitude": 41.20438, - "longitudeDelta": 10.0, - "name": "Kyrgyzstan", - "longitude": 74.766098, - "latitudeDelta": 10.0 - }, - { - "latitude": 19.85627, - "longitudeDelta": 10.0, - "name": "Laos", - "longitude": 102.495496, - "latitudeDelta": 10.0 - }, - { - "latitude": 56.879635, - "longitudeDelta": 10.0, - "name": "Latvia", - "longitude": 24.603189, - "latitudeDelta": 10.0 - }, - { - "latitude": 33.854721, - "longitudeDelta": 10.0, - "name": "Lebanon", - "longitude": 35.862285, - "latitudeDelta": 10.0 - }, - { - "latitude": -29.609988, - "longitudeDelta": 10.0, - "name": "Lesotho", - "longitude": 28.233608, - "latitudeDelta": 10.0 - }, - { - "latitude": 6.428055, - "longitudeDelta": 10.0, - "name": "Liberia", - "longitude": -9.429499, - "latitudeDelta": 10.0 - }, - { - "latitude": 26.3351, - "longitudeDelta": 10.0, - "name": "Libya", - "longitude": 17.228331, - "latitudeDelta": 10.0 - }, - { - "latitude": 47.166, - "longitudeDelta": 10.0, - "name": "Liechtenstein", - "longitude": 9.555373, - "latitudeDelta": 10.0 - }, - { - "latitude": 55.169438, - "longitudeDelta": 10.0, - "name": "Lithuania", - "longitude": 23.881275, - "latitudeDelta": 10.0 - }, - { - "latitude": 49.815273, - "longitudeDelta": 10.0, - "name": "Luxembourg", - "longitude": 6.129583, - "latitudeDelta": 10.0 - }, - { - "latitude": 22.198745, - "longitudeDelta": 10.0, - "name": "Macau", - "longitude": 113.543873, - "latitudeDelta": 10.0 - }, - { - "latitude": 41.608635, - "longitudeDelta": 10.0, - "name": "Macedonia [FYROM]", - "longitude": 21.745275, - "latitudeDelta": 10.0 - }, - { - "latitude": -18.766947, - "longitudeDelta": 10.0, - "name": "Madagascar", - "longitude": 46.869107, - "latitudeDelta": 10.0 - }, - { - "latitude": -13.254308, - "longitudeDelta": 10.0, - "name": "Malawi", - "longitude": 34.301525, - "latitudeDelta": 10.0 - }, - { - "latitude": 4.210484, - "longitudeDelta": 10.0, - "name": "Malaysia", - "longitude": 101.975766, - "latitudeDelta": 10.0 - }, - { - "latitude": 3.202778, - "longitudeDelta": 10.0, - "name": "Maldives", - "longitude": 73.22068, - "latitudeDelta": 10.0 - }, - { - "latitude": 17.570692, - "longitudeDelta": 10.0, - "name": "Mali", - "longitude": -3.996166, - "latitudeDelta": 10.0 - }, - { - "latitude": 35.937496, - "longitudeDelta": 10.0, - "name": "Malta", - "longitude": 14.375416, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.131474, - "longitudeDelta": 10.0, - "name": "Marshall Islands", - "longitude": 171.184478, - "latitudeDelta": 10.0 - }, - { - "latitude": 14.641528, - "longitudeDelta": 10.0, - "name": "Martinique", - "longitude": -61.024174, - "latitudeDelta": 10.0 - }, - { - "latitude": 21.00789, - "longitudeDelta": 10.0, - "name": "Mauritania", - "longitude": -10.940835, - "latitudeDelta": 10.0 - }, - { - "latitude": -20.348404, - "longitudeDelta": 10.0, - "name": "Mauritius", - "longitude": 57.552152, - "latitudeDelta": 10.0 - }, - { - "latitude": -12.8275, - "longitudeDelta": 10.0, - "name": "Mayotte", - "longitude": 45.166244, - "latitudeDelta": 10.0 - }, - { - "latitude": 23.634501, - "longitudeDelta": 10.0, - "name": "Mexico", - "longitude": -102.552784, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.425554, - "longitudeDelta": 10.0, - "name": "Micronesia", - "longitude": 150.550812, - "latitudeDelta": 10.0 - }, - { - "latitude": 47.411631, - "longitudeDelta": 10.0, - "name": "Moldova", - "longitude": 28.369885, - "latitudeDelta": 10.0 - }, - { - "latitude": 43.750298, - "longitudeDelta": 10.0, - "name": "Monaco", - "longitude": 7.412841, - "latitudeDelta": 10.0 - }, - { - "latitude": 46.862496, - "longitudeDelta": 10.0, - "name": "Mongolia", - "longitude": 103.846656, - "latitudeDelta": 10.0 - }, - { - "latitude": 42.708678, - "longitudeDelta": 10.0, - "name": "Montenegro", - "longitude": 19.37439, - "latitudeDelta": 10.0 - }, - { - "latitude": 16.742498, - "longitudeDelta": 10.0, - "name": "Montserrat", - "longitude": -62.187366, - "latitudeDelta": 10.0 - }, - { - "latitude": 31.791702, - "longitudeDelta": 10.0, - "name": "Morocco", - "longitude": -7.09262, - "latitudeDelta": 10.0 - }, - { - "latitude": -18.665695, - "longitudeDelta": 10.0, - "name": "Mozambique", - "longitude": 35.529562, - "latitudeDelta": 10.0 - }, - { - "latitude": 21.913965, - "longitudeDelta": 10.0, - "name": "Myanmar [Burma]", - "longitude": 95.956223, - "latitudeDelta": 10.0 - }, - { - "latitude": -22.95764, - "longitudeDelta": 10.0, - "name": "Namibia", - "longitude": 18.49041, - "latitudeDelta": 10.0 - }, - { - "latitude": -0.522778, - "longitudeDelta": 10.0, - "name": "Nauru", - "longitude": 166.931503, - "latitudeDelta": 10.0 - }, - { - "latitude": 28.394857, - "longitudeDelta": 10.0, - "name": "Nepal", - "longitude": 84.124008, - "latitudeDelta": 10.0 - }, - { - "latitude": 52.132633, - "longitudeDelta": 10.0, - "name": "Netherlands", - "longitude": 5.291266, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.226079, - "longitudeDelta": 10.0, - "name": "Netherlands Antilles", - "longitude": -69.060087, - "latitudeDelta": 10.0 - }, - { - "latitude": -20.904305, - "longitudeDelta": 10.0, - "name": "New Caledonia", - "longitude": 165.618042, - "latitudeDelta": 10.0 - }, - { - "latitude": -40.900557, - "longitudeDelta": 10.0, - "name": "New Zealand", - "longitude": 174.885971, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.865416, - "longitudeDelta": 10.0, - "name": "Nicaragua", - "longitude": -85.207229, - "latitudeDelta": 10.0 - }, - { - "latitude": 17.607789, - "longitudeDelta": 10.0, - "name": "Niger", - "longitude": 8.081666, - "latitudeDelta": 10.0 - }, - { - "latitude": 9.081999, - "longitudeDelta": 10.0, - "name": "Nigeria", - "longitude": 8.675277, - "latitudeDelta": 10.0 - }, - { - "latitude": -19.054445, - "longitudeDelta": 10.0, - "name": "Niue", - "longitude": -169.867233, - "latitudeDelta": 10.0 - }, - { - "latitude": -29.040835, - "longitudeDelta": 10.0, - "name": "Norfolk Island", - "longitude": 167.954712, - "latitudeDelta": 10.0 - }, - { - "latitude": 40.339852, - "longitudeDelta": 10.0, - "name": "North Korea", - "longitude": 127.510093, - "latitudeDelta": 10.0 - }, - { - "latitude": 17.33083, - "longitudeDelta": 10.0, - "name": "Northern Mariana Islands", - "longitude": 145.38469, - "latitudeDelta": 10.0 - }, - { - "latitude": 60.472024, - "longitudeDelta": 10.0, - "name": "Norway", - "longitude": 8.468946, - "latitudeDelta": 10.0 - }, - { - "latitude": 21.512583, - "longitudeDelta": 10.0, - "name": "Oman", - "longitude": 55.923255, - "latitudeDelta": 10.0 - }, - { - "latitude": 30.375321, - "longitudeDelta": 10.0, - "name": "Pakistan", - "longitude": 69.345116, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.51498, - "longitudeDelta": 10.0, - "name": "Palau", - "longitude": 134.58252, - "latitudeDelta": 10.0 - }, - { - "latitude": 31.952162, - "longitudeDelta": 10.0, - "name": "Palestinian Territories", - "longitude": 35.233154, - "latitudeDelta": 10.0 - }, - { - "latitude": 8.537981, - "longitudeDelta": 10.0, - "name": "Panama", - "longitude": -80.782127, - "latitudeDelta": 10.0 - }, - { - "latitude": -6.314993, - "longitudeDelta": 10.0, - "name": "Papua New Guinea", - "longitude": 143.95555, - "latitudeDelta": 10.0 - }, - { - "latitude": -23.442503, - "longitudeDelta": 10.0, - "name": "Paraguay", - "longitude": -58.443832, - "latitudeDelta": 10.0 - }, - { - "latitude": -9.189967, - "longitudeDelta": 10.0, - "name": "Peru", - "longitude": -75.015152, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.879721, - "longitudeDelta": 10.0, - "name": "Philippines", - "longitude": 121.774017, - "latitudeDelta": 10.0 - }, - { - "latitude": -24.703615, - "longitudeDelta": 10.0, - "name": "Pitcairn Islands", - "longitude": -127.439308, - "latitudeDelta": 10.0 - }, - { - "latitude": 51.919438, - "longitudeDelta": 10.0, - "name": "Poland", - "longitude": 19.145136, - "latitudeDelta": 10.0 - }, - { - "latitude": 39.399872, - "longitudeDelta": 10.0, - "name": "Portugal", - "longitude": -8.224454, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.220833, - "longitudeDelta": 10.0, - "name": "Puerto Rico", - "longitude": -66.590149, - "latitudeDelta": 10.0 - }, - { - "latitude": 25.354826, - "longitudeDelta": 10.0, - "name": "Qatar", - "longitude": 51.183884, - "latitudeDelta": 10.0 - }, - { - "latitude": 45.943161, - "longitudeDelta": 10.0, - "name": "Romania", - "longitude": 24.96676, - "latitudeDelta": 10.0 - }, - { - "latitude": 61.52401, - "longitudeDelta": 10.0, - "name": "Russia", - "longitude": 105.318756, - "latitudeDelta": 10.0 - }, - { - "latitude": -1.940278, - "longitudeDelta": 10.0, - "name": "Rwanda", - "longitude": 29.873888, - "latitudeDelta": 10.0 - }, - { - "latitude": -21.115141, - "longitudeDelta": 10.0, - "name": "R\u00e9union", - "longitude": 55.536384, - "latitudeDelta": 10.0 - }, - { - "latitude": -24.143474, - "longitudeDelta": 10.0, - "name": "Saint Helena", - "longitude": -10.030696, - "latitudeDelta": 10.0 - }, - { - "latitude": 17.357822, - "longitudeDelta": 10.0, - "name": "Saint Kitts and Nevis", - "longitude": -62.782998, - "latitudeDelta": 10.0 - }, - { - "latitude": 13.909444, - "longitudeDelta": 10.0, - "name": "Saint Lucia", - "longitude": -60.978893, - "latitudeDelta": 10.0 - }, - { - "latitude": 46.941936, - "longitudeDelta": 10.0, - "name": "Saint Pierre and Miquelon", - "longitude": -56.27111, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.984305, - "longitudeDelta": 10.0, - "name": "Saint Vincent and the Grenadines", - "longitude": -61.287228, - "latitudeDelta": 10.0 - }, - { - "latitude": -13.759029, - "longitudeDelta": 10.0, - "name": "Samoa", - "longitude": -172.104629, - "latitudeDelta": 10.0 - }, - { - "latitude": 43.94236, - "longitudeDelta": 10.0, - "name": "San Marino", - "longitude": 12.457777, - "latitudeDelta": 10.0 - }, - { - "latitude": 23.885942, - "longitudeDelta": 10.0, - "name": "Saudi Arabia", - "longitude": 45.079162, - "latitudeDelta": 10.0 - }, - { - "latitude": 14.497401, - "longitudeDelta": 10.0, - "name": "Senegal", - "longitude": -14.452362, - "latitudeDelta": 10.0 - }, - { - "latitude": 44.016521, - "longitudeDelta": 10.0, - "name": "Serbia", - "longitude": 21.005859, - "latitudeDelta": 10.0 - }, - { - "latitude": -4.679574, - "longitudeDelta": 10.0, - "name": "Seychelles", - "longitude": 55.491977, - "latitudeDelta": 10.0 - }, - { - "latitude": 8.460555, - "longitudeDelta": 10.0, - "name": "Sierra Leone", - "longitude": -11.779889, - "latitudeDelta": 10.0 - }, - { - "latitude": 1.352083, - "longitudeDelta": 10.0, - "name": "Singapore", - "longitude": 103.819836, - "latitudeDelta": 10.0 - }, - { - "latitude": 48.669026, - "longitudeDelta": 10.0, - "name": "Slovakia", - "longitude": 19.699024, - "latitudeDelta": 10.0 - }, - { - "latitude": 46.151241, - "longitudeDelta": 10.0, - "name": "Slovenia", - "longitude": 14.995463, - "latitudeDelta": 10.0 - }, - { - "latitude": -9.64571, - "longitudeDelta": 10.0, - "name": "Solomon Islands", - "longitude": 160.156194, - "latitudeDelta": 10.0 - }, - { - "latitude": 5.152149, - "longitudeDelta": 10.0, - "name": "Somalia", - "longitude": 46.199616, - "latitudeDelta": 10.0 - }, - { - "latitude": -30.559482, - "longitudeDelta": 10.0, - "name": "South Africa", - "longitude": 22.937506, - "latitudeDelta": 10.0 - }, - { - "latitude": -54.429579, - "longitudeDelta": 10.0, - "name": "South Georgia and the South Sandwich Islands", - "longitude": -36.587909, - "latitudeDelta": 10.0 - }, - { - "latitude": 35.907757, - "longitudeDelta": 10.0, - "name": "South Korea", - "longitude": 127.766922, - "latitudeDelta": 10.0 - }, - { - "latitude": 40.463667, - "longitudeDelta": 10.0, - "name": "Spain", - "longitude": -3.74922, - "latitudeDelta": 10.0 - }, - { - "latitude": 7.873054, - "longitudeDelta": 10.0, - "name": "Sri Lanka", - "longitude": 80.771797, - "latitudeDelta": 10.0 - }, - { - "latitude": 12.862807, - "longitudeDelta": 10.0, - "name": "Sudan", - "longitude": 30.217636, - "latitudeDelta": 10.0 - }, - { - "latitude": 3.919305, - "longitudeDelta": 10.0, - "name": "Suriname", - "longitude": -56.027783, - "latitudeDelta": 10.0 - }, - { - "latitude": 77.553604, - "longitudeDelta": 10.0, - "name": "Svalbard and Jan Mayen", - "longitude": 23.670272, - "latitudeDelta": 10.0 - }, - { - "latitude": -26.522503, - "longitudeDelta": 10.0, - "name": "Swaziland", - "longitude": 31.465866, - "latitudeDelta": 10.0 - }, - { - "latitude": 60.128161, - "longitudeDelta": 10.0, - "name": "Sweden", - "longitude": 18.643501, - "latitudeDelta": 10.0 - }, - { - "latitude": 46.818188, - "longitudeDelta": 10.0, - "name": "Switzerland", - "longitude": 8.227512, - "latitudeDelta": 10.0 - }, - { - "latitude": 34.802075, - "longitudeDelta": 10.0, - "name": "Syria", - "longitude": 38.996815, - "latitudeDelta": 10.0 - }, - { - "latitude": 0.18636, - "longitudeDelta": 10.0, - "name": "S\u00e3o Tom\u00e9 and Pr\u00edncipe", - "longitude": 6.613081, - "latitudeDelta": 10.0 - }, - { - "latitude": 23.69781, - "longitudeDelta": 10.0, - "name": "Taiwan", - "longitude": 120.960515, - "latitudeDelta": 10.0 - }, - { - "latitude": 38.861034, - "longitudeDelta": 10.0, - "name": "Tajikistan", - "longitude": 71.276093, - "latitudeDelta": 10.0 - }, - { - "latitude": -6.369028, - "longitudeDelta": 10.0, - "name": "Tanzania", - "longitude": 34.888822, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.870032, - "longitudeDelta": 10.0, - "name": "Thailand", - "longitude": 100.992541, - "latitudeDelta": 10.0 - }, - { - "latitude": -8.874217, - "longitudeDelta": 10.0, - "name": "Timor-Leste", - "longitude": 125.727539, - "latitudeDelta": 10.0 - }, - { - "latitude": 8.619543, - "longitudeDelta": 10.0, - "name": "Togo", - "longitude": 0.824782, - "latitudeDelta": 10.0 - }, - { - "latitude": -8.967363, - "longitudeDelta": 10.0, - "name": "Tokelau", - "longitude": -171.855881, - "latitudeDelta": 10.0 - }, - { - "latitude": -21.178986, - "longitudeDelta": 10.0, - "name": "Tonga", - "longitude": -175.198242, - "latitudeDelta": 10.0 - }, - { - "latitude": 10.691803, - "longitudeDelta": 10.0, - "name": "Trinidad and Tobago", - "longitude": -61.222503, - "latitudeDelta": 10.0 - }, - { - "latitude": 33.886917, - "longitudeDelta": 10.0, - "name": "Tunisia", - "longitude": 9.537499, - "latitudeDelta": 10.0 - }, - { - "latitude": 38.963745, - "longitudeDelta": 10.0, - "name": "Turkey", - "longitude": 35.243322, - "latitudeDelta": 10.0 - }, - { - "latitude": 38.969719, - "longitudeDelta": 10.0, - "name": "Turkmenistan", - "longitude": 59.556278, - "latitudeDelta": 10.0 - }, - { - "latitude": 21.694025, - "longitudeDelta": 10.0, - "name": "Turks and Caicos Islands", - "longitude": -71.797928, - "latitudeDelta": 10.0 - }, - { - "latitude": -7.109535, - "longitudeDelta": 10.0, - "name": "Tuvalu", - "longitude": 177.64933, - "latitudeDelta": 10.0 - }, - { - "latitude": 18.335765, - "longitudeDelta": 10.0, - "name": "U.S. Virgin Islands", - "longitude": -64.896335, - "latitudeDelta": 10.0 - }, - { - "latitude": 1.373333, - "longitudeDelta": 10.0, - "name": "Uganda", - "longitude": 32.290275, - "latitudeDelta": 10.0 - }, - { - "latitude": 48.379433, - "longitudeDelta": 10.0, - "name": "Ukraine", - "longitude": 31.16558, - "latitudeDelta": 10.0 - }, - { - "latitude": 23.424076, - "longitudeDelta": 10.0, - "name": "United Arab Emirates", - "longitude": 53.847818, - "latitudeDelta": 10.0 - }, - { - "latitude": 55.378051, - "longitudeDelta": 10.0, - "name": "United Kingdom", - "longitude": -3.435973, - "latitudeDelta": 10.0 - }, - { - "latitude": 37.09024, - "longitudeDelta": 10.0, - "name": "United States", - "longitude": -95.712891, - "latitudeDelta": 10.0 - }, - { - "latitude": -32.522779, - "longitudeDelta": 10.0, - "name": "Uruguay", - "longitude": -55.765835, - "latitudeDelta": 10.0 - }, - { - "latitude": 41.377491, - "longitudeDelta": 10.0, - "name": "Uzbekistan", - "longitude": 64.585262, - "latitudeDelta": 10.0 - }, - { - "latitude": -15.376706, - "longitudeDelta": 10.0, - "name": "Vanuatu", - "longitude": 166.959158, - "latitudeDelta": 10.0 - }, - { - "latitude": 41.902916, - "longitudeDelta": 10.0, - "name": "Vatican City", - "longitude": 12.453389, - "latitudeDelta": 10.0 - }, - { - "latitude": 6.42375, - "longitudeDelta": 10.0, - "name": "Venezuela", - "longitude": -66.58973, - "latitudeDelta": 10.0 - }, - { - "latitude": 14.058324, - "longitudeDelta": 10.0, - "name": "Vietnam", - "longitude": 108.277199, - "latitudeDelta": 10.0 - }, - { - "latitude": -13.768752, - "longitudeDelta": 10.0, - "name": "Wallis and Futuna", - "longitude": -177.156097, - "latitudeDelta": 10.0 - }, - { - "latitude": 24.215527, - "longitudeDelta": 10.0, - "name": "Western Sahara", - "longitude": -12.885834, - "latitudeDelta": 10.0 - }, - { - "latitude": 15.552727, - "longitudeDelta": 10.0, - "name": "Yemen", - "longitude": 48.516388, - "latitudeDelta": 10.0 - }, - { - "latitude": -13.133897, - "longitudeDelta": 10.0, - "name": "Zambia", - "longitude": 27.849332, - "latitudeDelta": 10.0 - }, - { - "latitude": -19.015438, - "longitudeDelta": 10.0, - "name": "Zimbabwe", - "longitude": 29.154857, - "latitudeDelta": 10.0 - } -]; - - -module.exports = CachedMap; diff --git a/example/examples/Callouts.js b/example/examples/Callouts.js deleted file mode 100644 index 0034a30d3..000000000 --- a/example/examples/Callouts.js +++ /dev/null @@ -1,159 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Image, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); -var CustomCallout = require('./CustomCallout'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -const SPACE = 0.01; - -var Callouts = React.createClass({ - getInitialState() { - return { - region: { - latitude: LATITUDE, - longitude: LONGITUDE, - latitudeDelta: LATITUDE_DELTA, - longitudeDelta: LONGITUDE_DELTA, - }, - markers: [ - { - coordinate: { - latitude: LATITUDE + SPACE, - longitude: LONGITUDE + SPACE, - }, - }, - { - coordinate: { - latitude: LATITUDE, - longitude: LONGITUDE, - }, - }, - { - coordinate: { - latitude: LATITUDE + SPACE, - longitude: LONGITUDE - SPACE, - }, - }, - ], - }; - }, - - show() { - this.refs.m1.showCallout(); - }, - - hide() { - this.refs.m1.hideCallout(); - }, - - render() { - const { region, markers } = this.state; - return ( - - - - - - - This is a plain view - - - - - - - This is a custom callout bubble view - - - - - - - Tap on markers to see different callouts - - - - - Show - - - Hide - - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - bubble: { - flex: 1, - backgroundColor: 'rgba(255,255,255,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - latlng: { - width: 200, - alignItems: 'stretch', - }, - button: { - width: 80, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - }, -}); - -module.exports = Callouts; diff --git a/example/examples/DisplayLatLng.js b/example/examples/DisplayLatLng.js deleted file mode 100644 index 8d8d70d8e..000000000 --- a/example/examples/DisplayLatLng.js +++ /dev/null @@ -1,124 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, -} = ReactNative; - -var MapView = require('react-native-maps'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; - -var DisplayLatLng = React.createClass({ - getInitialState() { - return { - region: { - latitude: LATITUDE, - longitude: LONGITUDE, - latitudeDelta: LATITUDE_DELTA, - longitudeDelta: LONGITUDE_DELTA, - }, - }; - }, - - onRegionChange(region) { - this.setState({ region }); - }, - - jumpRandom() { - this.setState({ region: this.randomRegion() }); - }, - - animateRandom() { - this.refs.map.animateToRegion(this.randomRegion()); - }, - - randomRegion() { - var { region } = this.state; - return { - ...this.state.region, - latitude: region.latitude + (Math.random() - 0.5) * region.latitudeDelta / 2, - longitude: region.longitude + (Math.random() - 0.5) * region.longitudeDelta / 2, - }; - }, - - render() { - return ( - - - - - - {`${this.state.region.latitude.toPrecision(7)}, ${this.state.region.longitude.toPrecision(7)}`} - - - - - Jump - - - Animate - - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - bubble: { - backgroundColor: 'rgba(255,255,255,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - latlng: { - width: 200, - alignItems: 'stretch', - }, - button: { - width: 80, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - }, -}); - -module.exports = DisplayLatLng; diff --git a/example/examples/DraggableMarkers.js b/example/examples/DraggableMarkers.js deleted file mode 100644 index ad888a837..000000000 --- a/example/examples/DraggableMarkers.js +++ /dev/null @@ -1,96 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Image, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -const SPACE = 0.01; - -var MarkerTypes = React.createClass({ - getInitialState() { - return { - a: { - latitude: LATITUDE + SPACE, - longitude: LONGITUDE + SPACE, - }, - b: { - latitude: LATITUDE - SPACE, - longitude: LONGITUDE - SPACE, - }, - } - }, - render() { - return ( - - - console.log('onSelect', e)} - onDrag={(e) => console.log('onDrag', e)} - onDragStart={(e) => console.log('onDragStart', e)} - onDragEnd={(e) => console.log('onDragEnd', e)} - onPress={(e) => console.log('onPress', e)} - draggable - > - - - console.log('onSelect', e)} - onDrag={(e) => console.log('onDrag', e)} - onDragStart={(e) => console.log('onDragStart', e)} - onDragEnd={(e) => console.log('onDragEnd', e)} - onPress={(e) => console.log('onPress', e)} - draggable - /> - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, -}); - -module.exports = MarkerTypes; diff --git a/example/examples/EventListener.js b/example/examples/EventListener.js deleted file mode 100644 index 1515ee80f..000000000 --- a/example/examples/EventListener.js +++ /dev/null @@ -1,180 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - ScrollView, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -var id = 0; - -var Event = React.createClass({ - shouldComponentUpdate(nextProps) { - return this.props.event.id !== nextProps.event.id; - }, - render() { - var { event } = this.props; - return ( - - {event.name} - {JSON.stringify(event.data, null, 2)} - - ); - }, -}); - -var DisplayLatLng = React.createClass({ - getInitialState() { - return { - region: { - latitude: LATITUDE, - longitude: LONGITUDE, - latitudeDelta: LATITUDE_DELTA, - longitudeDelta: LONGITUDE_DELTA, - }, - events: [], - }; - }, - - makeEvent(e, name) { - return { - id: id++, - name: name, - data: e.nativeEvent ? e.nativeEvent : e, - }; - }, - - recordEvent(name) { - return e => { - var { events } = this.state; - this.setState({ - events: [ - this.makeEvent(e, name), - ...events.slice(0, 10), - ], - }); - }; - }, - - render() { - return ( - - - - - - - Well hello there... - - - - - - - {this.state.events.map(event => )} - - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - event: { - borderBottomWidth: 1, - borderBottomColor: '#ccc', - padding: 8, - }, - eventData: { - fontSize: 10, - fontFamily: 'courier', - color: '#555', - }, - eventName: { - fontSize: 13, - fontWeight: 'bold', - color: '#222', - }, - eventList: { - position: 'absolute', - top: height / 2, - left: 0, - right: 0, - bottom: 0, - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: height / 2, - }, - bubble: { - backgroundColor: 'rgba(255,255,255,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - latlng: { - width: 200, - alignItems: 'stretch', - }, - button: { - width: 80, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - }, -}); - -module.exports = DisplayLatLng; diff --git a/example/examples/FitToSuppliedMarkers.js b/example/examples/FitToSuppliedMarkers.js deleted file mode 100644 index bc5b86706..000000000 --- a/example/examples/FitToSuppliedMarkers.js +++ /dev/null @@ -1,166 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Image, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -const SPACE = 0.01; - -var markerIDs = ['Marker1', 'Marker2', 'Marker3', 'Marker4', 'Marker5']; -var timeout = 4000; -var animationTimeout; - -var FocusOnMarkers = React.createClass({ - getInitialState() { - return { - a: { - latitude: LATITUDE + SPACE, - longitude: LONGITUDE + SPACE, - }, - b: { - latitude: LATITUDE - SPACE, - longitude: LONGITUDE - SPACE, - }, - c: { - latitude: LATITUDE - (SPACE * 2), - longitude: LONGITUDE - (SPACE * 2), - }, - d: { - latitude: LATITUDE - (SPACE * 3), - longitude: LONGITUDE - (SPACE * 3), - }, - e: { - latitude: LATITUDE - (SPACE * 4), - longitude: LONGITUDE - (SPACE * 4), - }, - } - }, - focusMap(markers, animated) { - console.log("Markers received to populate map: " + markers); - this.refs.map.fitToSuppliedMarkers(markers, animated); - }, - focus1() { - animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[1], - markerIDs[4] - ], true); - - this.focus2(); - }, timeout); - }, - focus2() { - animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[2], - markerIDs[3] - ], false); - - this.focus3() - }, timeout); - }, - focus3() { - animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[1], - markerIDs[2] - ], false); - - this.focus4(); - }, timeout); - }, - focus4() { - animationTimeout = setTimeout(() => { - this.focusMap([ - markerIDs[0], - markerIDs[3] - ], true); - - this.focus1(); - }, timeout) - }, - componentDidMount() { - animationTimeout = setTimeout(() => { - this.focus1(); - }, timeout) - }, - componentWillUnmount() { - if (animationTimeout) { - clearTimeout(animationTimeout); - } - }, - render() { - return ( - - - - - - - - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, -}); - -module.exports = FocusOnMarkers; diff --git a/example/examples/MarkerTypes.js b/example/examples/MarkerTypes.js deleted file mode 100644 index dbd6671c5..000000000 --- a/example/examples/MarkerTypes.js +++ /dev/null @@ -1,89 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Image, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -const SPACE = 0.01; - -var MarkerTypes = React.createClass({ - render() { - return ( - - - - X - - - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - marker: { - marginLeft: 33, - marginTop: 18, - fontWeight: 'bold', - } -}); - -module.exports = MarkerTypes; diff --git a/example/examples/PanController.js b/example/examples/PanController.js deleted file mode 100644 index a5338fa74..000000000 --- a/example/examples/PanController.js +++ /dev/null @@ -1,431 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - PropTypes, -} = React; -var { - View, - Animated, - PanResponder, -} = ReactNative; - -var ModePropType = PropTypes.oneOf(["decay", "snap", "spring-origin"]); -var OvershootPropType = PropTypes.oneOf(["spring", "clamp"]); -var AnimatedPropType = PropTypes.any; - -var PanController = React.createClass({ - - propTypes: { - // Component Config - lockDirection: PropTypes.bool, - horizontal: PropTypes.bool, - vertical: PropTypes.bool, - overshootX: OvershootPropType, - overshootY: OvershootPropType, - xBounds: PropTypes.arrayOf(PropTypes.number), - yBounds: PropTypes.arrayOf(PropTypes.number), - xMode: ModePropType, - yMode: ModePropType, - snapSpacingX: PropTypes.number, // TODO: also allow an array of values? - snapSpacingY: PropTypes.number, - - // Animated Values - panX: AnimatedPropType, - panY: AnimatedPropType, - - // Animation Config - overshootSpringConfig: PropTypes.any, - momentumDecayConfig: PropTypes.any, - springOriginConfig: PropTypes.any, - directionLockDistance: PropTypes.number, - overshootReductionFactor: PropTypes.number, - - // Events - onOvershoot: PropTypes.func, - onDirectionChange: PropTypes.func, - onReleaseX: PropTypes.func, - onReleaseY: PropTypes.func, - onRelease: PropTypes.func, - - //...PanResponderPropTypes, - }, - - getDefaultProps() { - return { - horizontal: false, - vertical: false, - lockDirection: true, - overshootX: "spring", - overshootY: "spring", - panX: new Animated.Value(0), - panY: new Animated.Value(0), - xBounds: [-Infinity, Infinity], - yBounds: [-Infinity, Infinity], - yMode: "decay", - xMode: "decay", - overshootSpringConfig: { friction: 7, tension: 40 }, - momentumDecayConfig: { deceleration: 0.993 }, - springOriginConfig: { friction: 7, tension: 40 }, - overshootReductionFactor: 3, - directionLockDistance: 10, - onStartShouldSetPanResponder: () => true, - onMoveShouldSetPanResponder: () => true, - }; - }, - - // getInitialState() { - // //TODO: - // // it's possible we want to move some props over to state. - // // For example, xBounds/yBounds might need to be - // // calculated/updated automatically - // // - // // This could also be done with a higher-order component - // // that just massages props passed in... - // return { - // - // }; - // }, - - _responder: null, - _listener: null, - _direction: null, - - componentWillMount() { - this._responder = PanResponder.create({ - onStartShouldSetPanResponder: this.props.onStartShouldSetPanResponder, - onMoveShouldSetPanResponder: this.props.onMoveShouldSetPanResponder, - onPanResponderGrant: (...args) => { - if (this.props.onPanResponderGrant) { - this.props.onPanResponderGrant(...args); - } - var { panX, panY, horizontal, vertical, xMode, yMode } = this.props; - - this.handleResponderGrant(panX, xMode); - this.handleResponderGrant(panY, yMode); - - this._direction = horizontal && !vertical ? 'x' : (vertical && !horizontal ? 'y' : null); - }, - - onPanResponderMove: (_, { dx, dy, x0, y0 }) => { - var { - panX, - panY, - xBounds, - yBounds, - overshootX, - overshootY, - horizontal, - vertical, - lockDirection, - directionLockDistance, - } = this.props; - - if (!this._direction) { - var dx2 = dx * dx; - var dy2 = dy * dy; - if (dx2 + dy2 > directionLockDistance) { - this._direction = dx2 > dy2 ? 'x' : 'y'; - if (this.props.onDirectionChange) { - this.props.onDirectionChange(this._direction, { dx, dy, x0, y0 }); - } - } - } - - var dir = this._direction; - - if (this.props.onPanResponderMove) { - this.props.onPanResponderMove(_, { dx, dy, x0, y0 }); - } - - if (horizontal && (!lockDirection || dir === 'x')) { - var [xMin, xMax] = xBounds; - - this.handleResponderMove(panX, dx, xMin, xMax, overshootX); - } - - if (vertical && (!lockDirection || dir === 'y')) { - var [yMin, yMax] = yBounds; - - this.handleResponderMove(panY, dy, yMin, yMax, overshootY); - } - }, - - onPanResponderRelease: (_, { vx, vy, dx, dy }) => { - var { - panX, - panY, - xBounds, - yBounds, - overshootX, - overshootY, - horizontal, - vertical, - lockDirection, - xMode, - yMode, - snapSpacingX, - snapSpacingY, - } = this.props; - - var cancel = false; - - var dir = this._direction; - - if (this.props.onRelease) { - cancel = false === this.props.onRelease({ vx, vy, dx, dy }); - } - - if (!cancel && horizontal && (!lockDirection || dir === 'x')) { - var [xMin, xMax] = xBounds; - if (this.props.onReleaseX) { - cancel = false === this.props.onReleaseX({ vx, vy, dx, dy }); - } - !cancel && this.handleResponderRelease(panX, xMin, xMax, vx, overshootX, xMode, snapSpacingX); - } - - if (!cancel && vertical && (!lockDirection || dir === 'y')) { - var [yMin, yMax] = yBounds; - if (this.props.onReleaseY) { - cancel = false === this.props.onReleaseY({ vx, vy, dx, dy }); - } - !cancel && this.handleResponderRelease(panY, yMin, yMax, vy, overshootY, yMode, snapSpacingY); - } - - this._direction = horizontal && !vertical ? 'x' : (vertical && !horizontal ? 'y' : null); - } - }); - }, - - handleResponderMove(anim, delta, min, max, overshoot) { - var val = anim._offset + delta; - - if (val > max) { - switch (overshoot) { - case "spring": - val = max + (val - max) / this.props.overshootReductionFactor; - break; - case "clamp": - val = max; - break; - } - } - if (val < min) { - switch (overshoot) { - case "spring": - val = min - (min - val) / this.props.overshootReductionFactor; - break; - case "clamp": - val = min; - break; - } - } - val = val - anim._offset; - anim.setValue(val); - }, - - handleResponderRelease(anim, min, max, velocity, overshoot, mode, snapSpacing) { - anim.flattenOffset(); - - - if (anim._value < min) { - if (this.props.onOvershoot) { - this.props.onOvershoot(); //TODO: what args should we pass to this - } - switch (overshoot) { - case "spring": - Animated.spring(anim, { - ...this.props.overshootSpringConfig, - toValue: min, - velocity, - }).start(); - break; - case "clamp": - anim.setValue(min); - break; - } - } else if (anim._value > max) { - if (this.props.onOvershoot) { - this.props.onOvershoot(); //TODO: what args should we pass to this - } - switch (overshoot) { - case "spring": - Animated.spring(anim, { - ...this.props.overshootSpringConfig, - toValue: max, - velocity, - }).start(); - break; -case "clamp": - anim.setValue(min); - break; -} -} else { - - switch (mode) { - case "snap": - this.handleSnappedScroll(anim, min, max, velocity, snapSpacing, overshoot); - break; - - case "decay": - this.handleMomentumScroll(anim, min, max, velocity, overshoot); - break; - - case "spring-origin": - Animated.spring(anim, { - ...this.props.springOriginConfig, - toValue: 0, - velocity, - }).start(); - break; -} -} -}, - -handleResponderGrant(anim, mode) { - switch (mode) { - case "spring-origin": - anim.setValue(0); - break; - case "snap": - case "decay": - anim.setOffset(anim._value + anim._offset); - anim.setValue(0); - break; - } -}, - -handleMomentumScroll(anim, min, max, velocity, overshoot) { - Animated.decay(anim, { - ...this.props.momentumDecayConfig, - velocity, -}).start(() => { - anim.removeListener(this._listener); -}); - -this._listener = anim.addListener(({ value }) => { - if (value < min) { - anim.removeListener(this._listener); - if (this.props.onOvershoot) { - this.props.onOvershoot(); //TODO: what args should we pass to this - } - switch (overshoot) { - case "spring": - Animated.spring(anim, { - ...this.props.overshootSpringConfig, - toValue: min, - velocity, - }).start(); - break; - case "clamp": - anim.setValue(min); - break; - } -} else if (value > max) { - anim.removeListener(this._listener); - if (this.props.onOvershoot) { - this.props.onOvershoot(); //TODO: what args should we pass to this - } - switch (overshoot) { - case "spring": - Animated.spring(anim, { - ...this.props.overshootSpringConfig, - toValue: max, - velocity, - }).start(); - break; -case "clamp": - anim.setValue(min); - break; -} -} -}); -}, - -handleSnappedScroll(anim, min, max, velocity, spacing) { - var endX = this.momentumCenter(anim._value, velocity, spacing); - endX = Math.max(endX, min); - endX = Math.min(endX, max); - var bounds = [endX-spacing/2, endX+spacing/2]; - var endV = this.velocityAtBounds(anim._value, velocity, bounds); - - this._listener = anim.addListener(( { value } ) => { - if (value > bounds[0] && value < bounds[1]) { - Animated.spring(anim, { - toValue: endX, - velocity: endV, - }).start(); - } - }); - - Animated.decay(anim, { - ...this.props.momentumDecayConfig, - velocity, -}).start(()=> { - anim.removeListener(this._listener); -}); -}, - -closestCenter(x, spacing) { - var plus = (x % spacing) < spacing / 2 ? 0 : spacing; - return Math.round(x / spacing) * spacing + plus; -}, - -momentumCenter(x0, vx, spacing) { - var t = 0; - var deceleration = this.props.momentumDecayConfig.deceleration || 0.997; - var x1 = x0; - var x = x1; - - while (true) { - t += 16; - x = x0 + (vx / (1 - deceleration)) * - (1 - Math.exp(-(1 - deceleration) * t)); - if (Math.abs(x-x1) < 0.1) { - x1 = x; - break; - } - x1 = x; - } - return this.closestCenter(x1, spacing); -}, - -velocityAtBounds(x0, vx, bounds) { - var t = 0; - var deceleration = this.props.momentumDecayConfig.deceleration || 0.997; - var x1 = x0; - var x = x1; - var vf; - while (true) { - t += 16; - x = x0 + (vx / (1 - deceleration)) * - (1 - Math.exp(-(1 - deceleration) * t)); - vf = (x-x1) / 16; - if (x > bounds[0] && x < bounds[1]) { - break; - } - if (Math.abs(vf) < 0.1) { - break; - } - x1 = x; - } - return vf; -}, - -//componentDidMount() { -// //TODO: we may need to measure the children width/height here? -//}, -// -//componentWillUnmount() { -// -//}, -// -//componentDidUnmount() { -// -//}, - -render: function () { - return -}, -}); - -module.exports = PanController; diff --git a/example/examples/PolygonCreator.js b/example/examples/PolygonCreator.js deleted file mode 100644 index bd5585f35..000000000 --- a/example/examples/PolygonCreator.js +++ /dev/null @@ -1,156 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -var id = 0; - -var DisplayLatLng = React.createClass({ - getInitialState() { - return { - region: { - latitude: LATITUDE, - longitude: LONGITUDE, - latitudeDelta: LATITUDE_DELTA, - longitudeDelta: LONGITUDE_DELTA, - }, - polygons: [], - editing: null, - }; - }, - - finish() { - var { polygons, editing } = this.state; - this.setState({ - polygons: [...polygons, editing], - editing: null, - }); - }, - - onPress(e) { - var { editing } = this.state; - if (!editing) { - this.setState({ - editing: { - id: id++, - coordinates: [e.nativeEvent.coordinate], - }, - }); - } else { - this.setState({ - editing: { - ...editing, - coordinates: [ - ...editing.coordinates, - e.nativeEvent.coordinate, - ], - }, - }); - } - }, - - render() { - var mapOptions = { - scrollEnabled: true, - }; - - if (this.state.editing) { - mapOptions.scrollEnabled = false; - mapOptions.onPanDrag = this.onPress; - } - - return ( - - - {this.state.polygons.map(polygon => ( - - ))} - {this.state.editing && ( - - )} - - - {this.state.editing && ( - - Finish - - )} - - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - bubble: { - backgroundColor: 'rgba(255,255,255,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - latlng: { - width: 200, - alignItems: 'stretch', - }, - button: { - width: 80, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - }, -}); - -module.exports = DisplayLatLng; diff --git a/example/examples/TakeSnapshot.js b/example/examples/TakeSnapshot.js deleted file mode 100644 index 3a749a62c..000000000 --- a/example/examples/TakeSnapshot.js +++ /dev/null @@ -1,135 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - PropTypes, - View, - Text, - Dimensions, - TouchableOpacity, - Image, -} = ReactNative; - -var MapView = require('react-native-maps'); -var PriceMarker = require('./PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const ASPECT_RATIO = width / height; -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; -const SPACE = 0.01; - -var MarkerTypes = React.createClass({ - getInitialState() { - return { mapSnapshot: null } - }, - - takeSnapshot() { - this.refs.map.takeSnapshot(300, 300, { - latitude: LATITUDE - SPACE, - longitude: LONGITUDE - SPACE, - latitudeDelta: 0.01, - longitudeDelta: 0.01 * ASPECT_RATIO - }, (err, data) => { - if (err) console.log(err) - this.setState({ mapSnapshot: data }) - }); - }, - - render() { - return ( - - - - - - - - - Take snapshot - - - {this.state.mapSnapshot - ? this.setState({ mapSnapshot: null })}> - - - : null} - - ); - }, -}); - -var styles = StyleSheet.create({ - container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - justifyContent: 'flex-end', - alignItems: 'center', - }, - map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - }, - bubble: { - backgroundColor: 'rgba(255,255,255,0.7)', - paddingHorizontal: 18, - paddingVertical: 12, - borderRadius: 20, - }, - button: { - width: 140, - paddingHorizontal: 12, - alignItems: 'center', - marginHorizontal: 10, - }, - buttonContainer: { - flexDirection: 'row', - marginVertical: 20, - backgroundColor: 'transparent', - }, - overlay: { - backgroundColor: 'rgba(0,0,0,0.5)', - justifyContent: 'center', - } -}); - -module.exports = MarkerTypes; diff --git a/example/examples/finalindexfile.js b/example/examples/finalindexfile.js deleted file mode 100644 index ffa045044..000000000 --- a/example/examples/finalindexfile.js +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - */ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppRegistry, - StyleSheet, - Text, - View, - Dimensions, - Animated, - TouchableOpacity, -} = ReactNative; -var MapView = require('react-native-maps'); -var PriceMarker = require('./components/PriceMarker'); - -var { width, height } = Dimensions.get('window'); - -const LATITUDE = 37.78825; -const LONGITUDE = -122.4324; - -const LATD = LATITUDE - 38.89399; -const LNGD = LONGITUDE + 77.03659; - -const LATITUDE_DELTA = 0.0922; -const LONGITUDE_DELTA = 0.1218; - -var MapViewTest = React.createClass({ - getInitialState() { - const scale = new Animated.Value(1); - const latitudeDelta = scale.interpolate({ - inputRange: [1, 2], - outputRange: [LATITUDE_DELTA, LATITUDE_DELTA * 0.6], - }); - const longitudeDelta = scale.interpolate({ - inputRange: [1, 2], - outputRange: [LONGITUDE_DELTA, LONGITUDE_DELTA * 0.6], - }); - return { - val: new Animated.Value(1), - scale, - region: new Animated.Region({ - latitude: LATITUDE, - longitude: LONGITUDE, - latitudeDelta: latitudeDelta, - longitudeDelta: longitudeDelta, - }), - markers: [], - coord: { - latitude: LATITUDE, - longitude: LONGITUDE, - }, - }; - }, - - onRegionChange(region) { - //console.log("onRegionChange", region); - this.state.region.setValue(region); - //this.setState({ region }); - }, - - onAnimate() { - var { val } = this.state; - Animated.sequence([ - Animated.timing(val, { toValue: 0, duration: 500 }), - Animated.timing(val, { toValue: 1, duration: 500 }), - ]).start(); - }, - - onAddMarker() { - var coordinate = { - latitude: LATITUDE + LATITUDE_DELTA * (Math.random() - 0.5), - longitude: LONGITUDE + LONGITUDE_DELTA * (Math.random() - 0.5), - }; - var markers = [...this.state.markers, coordinate]; - if (markers.length > 5) { - markers = markers.slice(1); - } - this.setState({ markers }); - }, - - onAnimateZoom() { - var { scale, region } = this.state; - var toValue = scale.__getValue() > 1 ? 1 : 2; - Animated.parallel([ - Animated.spring(region.latitude, { - toValue: LATITUDE + LATITUDE_DELTA * (Math.random() - 0.5), - }), - Animated.spring(region.longitude, { - toValue: LONGITUDE + LONGITUDE_DELTA * (Math.random() - 0.5), - }), - Animated.spring(scale, { - toValue, - }), - ]).start(); - }, - - onMarkerMove() { - this.setState({ - coord: { - latitude: LATITUDE + LATITUDE_DELTA * (Math.random() - 0.5), - longitude: LONGITUDE + LONGITUDE_DELTA * (Math.random() - 0.5), - }, - }) - }, - - onAnimateMapRegion() { - this.refs.map.refs.node.animateToRegion({ - latitude: LATITUDE + LATITUDE_DELTA * (Math.random() - 0.5), - longitude: LONGITUDE + LONGITUDE_DELTA * (Math.random() - 0.5), - latitudeDelta: LATITUDE_DELTA, - longitudeDelta: LONGITUDE_DELTA, - }, 100); - }, - - onZoomToFit() { - this.refs.map.refs.node.fitToElements(true); - }, - - render() { - - return ( - - console.log("Map::onRegionChangeComplete", e.nativeEvent)} - onPress={(e) => console.log("Map::onPress", e.nativeEvent)} - onPanDrag={(e) => console.log("Map::onPanDrag", e.nativeEvent)} - onLongPress={(e) => console.log("Map::onLongPress", e.nativeEvent)} - onMarkerPress={(e) => console.log("Map::onMarkerPress", e.nativeEvent)} - onMarkerSelect={(e) => console.log("Map::onMarkerSelect", e.nativeEvent)} - onMarkerDeselect={(e) => console.log("Map::onMarkerDeselect", e.nativeEvent)} - onCalloutPress={(e) => console.log("Map::onCalloutPress", e.nativeEvent)} - onMapPress={(e) => console.log("Map::onMapPress", e.nativeEvent)} - > - - - - console.log("Marker::onPress", e.nativeEvent)} - onCalloutPress={(e) => console.log("Marker::onCalloutPress", e.nativeEvent)} - > - - console.log("Callout::onPress", e.nativeEvent)} - > - - Well hello there... - - - - {this.state.markers.map((coord, i) => ( - console.log("Marker::onPress", e.nativeEvent)} - onCalloutPress={(e) => console.log("Marker::onCalloutPress", e.nativeEvent)} - > - - console.log("Callout::onPress", e.nativeEvent)} - > - - Well hello there... - - - - ))} - - - - - Add Marker - - - - - - Animate Zoom - - - - - - Move Marker - - - - - - Animate Map Region - - - - - - Fit To Elements - - - - ); - }, -}); - -var styles = StyleSheet.create({ - map: { - height: 350, - margin: 10, - borderWidth: 1, - borderColor: '#000000', - }, - row: { - flexDirection: 'row', - justifyContent: 'space-between', - }, - textInput: { - width: 150, - height: 20, - borderWidth: 0.5, - borderColor: '#aaaaaa', - fontSize: 13, - padding: 4, - }, - changeButton: { - alignSelf: 'center', - marginTop: 5, - padding: 3, - borderWidth: 0.5, - borderColor: '#777777', - }, - red: { - width: 50, - height: 50, - backgroundColor: 'red', - }, - blue: { - width: 50, - height: 50, - backgroundColor: 'blue', - }, - callout: { - //flex: 0, - //flexDirection: 'column', - //position: 'absolute', - //flex: 0, - //backgroundColor: '#fff', - //width: 100, - //height: 100, - }, -}); - -module.exports = MapViewTest; -AppRegistry.registerComponent('rn_mapview', () => MapViewTest); diff --git a/example/index.android.js b/example/index.android.js deleted file mode 100644 index 7a347e3a6..000000000 --- a/example/index.android.js +++ /dev/null @@ -1,15 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppRegistry, -} = ReactNative; - -var App = require('./App'); - -var AirMapsExplorer = React.createClass({ - render() { - return - }, -}); - -AppRegistry.registerComponent('AirMapsExplorer', () => AirMapsExplorer); diff --git a/example/index.ios.js b/example/index.ios.js deleted file mode 100644 index 7a347e3a6..000000000 --- a/example/index.ios.js +++ /dev/null @@ -1,15 +0,0 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - AppRegistry, -} = ReactNative; - -var App = require('./App'); - -var AirMapsExplorer = React.createClass({ - render() { - return - }, -}); - -AppRegistry.registerComponent('AirMapsExplorer', () => AirMapsExplorer); diff --git a/example/index.js b/example/index.js new file mode 100644 index 000000000..69303b34d --- /dev/null +++ b/example/index.js @@ -0,0 +1,9 @@ +/** + * @format + */ + +import {AppRegistry} from 'react-native'; +import App from './src/App'; +import {name as appName} from './app.json'; + +AppRegistry.registerComponent(appName, () => App); diff --git a/example/ios/.xcode.env b/example/ios/.xcode.env new file mode 100644 index 000000000..3d5782c71 --- /dev/null +++ b/example/ios/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj b/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj deleted file mode 100644 index c36dd459a..000000000 --- a/example/ios/AirMapsExplorer.xcodeproj/project.pbxproj +++ /dev/null @@ -1,803 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; - 00E356F31AD99517003FC87E /* AirMapsExplorerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* AirMapsExplorerTests.m */; }; - 11E015091C4AE70F008E4ED9 /* libAirMaps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 11E015081C4AE706008E4ED9 /* libAirMaps.a */; }; - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTActionSheet; - }; - 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTGeolocation; - }; - 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5115D1A9E6B3D00147676; - remoteInfo = RCTImage; - }; - 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B511DB1A9E6C8500147676; - remoteInfo = RCTNetwork; - }; - 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; - remoteInfo = RCTVibration; - }; - 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = AirMapsExplorer; - }; - 11E015071C4AE706008E4ED9 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 11E015031C4AE706008E4ED9 /* AirMaps.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 11FA5C511C4A1296003AC2EE; - remoteInfo = AirMaps; - }; - 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTSettings; - }; - 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 3C86DF461ADF2C930047B81A; - remoteInfo = RCTWebSocket; - }; - 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; - remoteInfo = React; - }; - 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTLinking; - }; - 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 58B5119B1A9E6C1200147676; - remoteInfo = RCTText; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; - 00E356EE1AD99517003FC87E /* AirMapsExplorerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AirMapsExplorerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 00E356F21AD99517003FC87E /* AirMapsExplorerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AirMapsExplorerTests.m; sourceTree = ""; }; - 11E015031C4AE706008E4ED9 /* AirMaps.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = AirMaps.xcodeproj; path = "../node_modules/react-native-maps/ios/AirMaps.xcodeproj"; sourceTree = ""; }; - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* AirMapsExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AirMapsExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AirMapsExplorer/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = AirMapsExplorer/AppDelegate.m; sourceTree = ""; }; - 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = AirMapsExplorer/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = AirMapsExplorer/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = AirMapsExplorer/main.m; sourceTree = ""; }; - 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 00E356EB1AD99517003FC87E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 11E015091C4AE70F008E4ED9 /* libAirMaps.a in Frameworks */, - 146834051AC3E58100842450 /* libReact.a in Frameworks */, - 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, - 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, - 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, - 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, - 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, - 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, - 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, - 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, - 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 00C302A81ABCB8CE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302B61ABCB90400DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302BC1ABCB91800DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302D41ABCB9D200DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, - ); - name = Products; - sourceTree = ""; - }; - 00C302E01ABCB9EE00DB3ED1 /* Products */ = { - isa = PBXGroup; - children = ( - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, - ); - name = Products; - sourceTree = ""; - }; - 00E356EF1AD99517003FC87E /* AirMapsExplorerTests */ = { - isa = PBXGroup; - children = ( - 00E356F21AD99517003FC87E /* AirMapsExplorerTests.m */, - 00E356F01AD99517003FC87E /* Supporting Files */, - ); - path = AirMapsExplorerTests; - sourceTree = ""; - }; - 00E356F01AD99517003FC87E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 00E356F11AD99517003FC87E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 11E015041C4AE706008E4ED9 /* Products */ = { - isa = PBXGroup; - children = ( - 11E015081C4AE706008E4ED9 /* libAirMaps.a */, - ); - name = Products; - sourceTree = ""; - }; - 139105B71AF99BAD00B5F7CC /* Products */ = { - isa = PBXGroup; - children = ( - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, - ); - name = Products; - sourceTree = ""; - }; - 139FDEE71B06529A00C62182 /* Products */ = { - isa = PBXGroup; - children = ( - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, - ); - name = Products; - sourceTree = ""; - }; - 13B07FAE1A68108700A75B9A /* AirMapsExplorer */ = { - isa = PBXGroup; - children = ( - 008F07F21AC5B25A0029DE68 /* main.jsbundle */, - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.m */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - name = AirMapsExplorer; - sourceTree = ""; - }; - 146834001AC3E56700842450 /* Products */ = { - isa = PBXGroup; - children = ( - 146834041AC3E56700842450 /* libReact.a */, - ); - name = Products; - sourceTree = ""; - }; - 78C398B11ACF4ADC00677621 /* Products */ = { - isa = PBXGroup; - children = ( - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, - ); - name = Products; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - 11E015031C4AE706008E4ED9 /* AirMaps.xcodeproj */, - 146833FF1AC3E56700842450 /* React.xcodeproj */, - 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, - 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, - 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, - 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, - 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, - 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, - 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, - 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, - 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, - ); - name = Libraries; - sourceTree = ""; - }; - 832341B11AAA6A8300B99B32 /* Products */ = { - isa = PBXGroup; - children = ( - 832341B51AAA6A8300B99B32 /* libRCTText.a */, - ); - name = Products; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* AirMapsExplorer */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 00E356EF1AD99517003FC87E /* AirMapsExplorerTests */, - 83CBBA001A601CBA00E9B192 /* Products */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* AirMapsExplorer.app */, - 00E356EE1AD99517003FC87E /* AirMapsExplorerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* AirMapsExplorerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AirMapsExplorerTests" */; - buildPhases = ( - 00E356EA1AD99517003FC87E /* Sources */, - 00E356EB1AD99517003FC87E /* Frameworks */, - 00E356EC1AD99517003FC87E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 00E356F51AD99517003FC87E /* PBXTargetDependency */, - ); - name = AirMapsExplorerTests; - productName = AirMapsExplorerTests; - productReference = 00E356EE1AD99517003FC87E /* AirMapsExplorerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 13B07F861A680F5B00A75B9A /* AirMapsExplorer */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AirMapsExplorer" */; - buildPhases = ( - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = AirMapsExplorer; - productName = "Hello World"; - productReference = 13B07F961A680F5B00A75B9A /* AirMapsExplorer.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0710; - ORGANIZATIONNAME = Facebook; - TargetAttributes = { - 00E356ED1AD99517003FC87E = { - CreatedOnToolsVersion = 6.2; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AirMapsExplorer" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 11E015041C4AE706008E4ED9 /* Products */; - ProjectRef = 11E015031C4AE706008E4ED9 /* AirMaps.xcodeproj */; - }, - { - ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; - ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; - }, - { - ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; - ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; - }, - { - ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; - ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; - }, - { - ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; - ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; - }, - { - ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; - ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; - }, - { - ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; - ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; - }, - { - ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; - ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; - }, - { - ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; - ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; - }, - { - ProductGroup = 139FDEE71B06529A00C62182 /* Products */; - ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; - }, - { - ProductGroup = 146834001AC3E56700842450 /* Products */; - ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* AirMapsExplorer */, - 00E356ED1AD99517003FC87E /* AirMapsExplorerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTActionSheet.a; - remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTGeolocation.a; - remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTImage.a; - remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTNetwork.a; - remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTVibration.a; - remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 11E015081C4AE706008E4ED9 /* libAirMaps.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libAirMaps.a; - remoteRef = 11E015071C4AE706008E4ED9 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTSettings.a; - remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTWebSocket.a; - remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 146834041AC3E56700842450 /* libReact.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libReact.a; - remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTLinking.a; - remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTText.a; - remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 00E356EC1AD99517003FC87E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "../node_modules/react-native/packager/react-native-xcode.sh"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 00E356EA1AD99517003FC87E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 00E356F31AD99517003FC87E /* AirMapsExplorerTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* AirMapsExplorer */; - targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { - isa = PBXVariantGroup; - children = ( - 13B07FB21A68108700A75B9A /* Base */, - ); - name = LaunchScreen.xib; - path = AirMapsExplorer; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 00E356F61AD99517003FC87E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = AirMapsExplorerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AirMapsExplorer.app/AirMapsExplorer"; - }; - name = Debug; - }; - 00E356F71AD99517003FC87E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = AirMapsExplorerTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AirMapsExplorer.app/AirMapsExplorer"; - }; - name = Release; - }; - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEAD_CODE_STRIPPING = NO; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - ); - INFOPLIST_FILE = AirMapsExplorer/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = AirMapsExplorer; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - ); - INFOPLIST_FILE = AirMapsExplorer/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = AirMapsExplorer; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, - "$(SRCROOT)/../node_modules/react-native/React/**", - ); - IPHONEOS_DEPLOYMENT_TARGET = 7.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AirMapsExplorerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 00E356F61AD99517003FC87E /* Debug */, - 00E356F71AD99517003FC87E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AirMapsExplorer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "AirMapsExplorer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/example/ios/AirMapsExplorer/AppDelegate.m b/example/ios/AirMapsExplorer/AppDelegate.m deleted file mode 100644 index c6c639d03..000000000 --- a/example/ios/AirMapsExplorer/AppDelegate.m +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "AppDelegate.h" - -#import "RCTRootView.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - NSURL *jsCodeLocation; - - /** - * Loading JavaScript code - uncomment the one you want. - * - * OPTION 1 - * Load from development server. Start the server from the repository root: - * - * $ npm start - * - * To run on device, change `localhost` to the IP address of your computer - * (you can get this by typing `ifconfig` into the terminal and selecting the - * `inet` value under `en0:`) and make sure your computer and iOS device are - * on the same Wi-Fi network. - */ - - jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; - - /** - * OPTION 2 - * Load from pre-bundled file on disk. The static bundle is automatically - * generated by "Bundle React Native code and images" build step. - */ - -// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; - - RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation - moduleName:@"AirMapsExplorer" - initialProperties:nil - launchOptions:launchOptions]; - - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - return YES; -} - -@end diff --git a/example/ios/AirMapsExplorer/Base.lproj/LaunchScreen.xib b/example/ios/AirMapsExplorer/Base.lproj/LaunchScreen.xib deleted file mode 100644 index 75b0e27d2..000000000 --- a/example/ios/AirMapsExplorer/Base.lproj/LaunchScreen.xib +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/AirMapsExplorer/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ios/AirMapsExplorer/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f74..000000000 --- a/example/ios/AirMapsExplorer/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/example/ios/AirMapsExplorerTests/AirMapsExplorerTests.m b/example/ios/AirMapsExplorerTests/AirMapsExplorerTests.m deleted file mode 100644 index 31dab00aa..000000000 --- a/example/ios/AirMapsExplorerTests/AirMapsExplorerTests.m +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import -#import - -#import "RCTLog.h" -#import "RCTRootView.h" - -#define TIMEOUT_SECONDS 240 -#define TEXT_TO_LOOK_FOR @"Welcome to React Native!" - -@interface AirMapsExplorerTests : XCTestCase - -@end - -@implementation AirMapsExplorerTests - -- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test -{ - if (test(view)) { - return YES; - } - for (UIView *subview in [view subviews]) { - if ([self findSubviewInView:subview matching:test]) { - return YES; - } - } - return NO; -} - -- (void)testRendersWelcomeScreen -{ - UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; - BOOL foundElement = NO; - - __block NSString *redboxError = nil; - RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { - if (level >= RCTLogLevelError) { - redboxError = message; - } - }); - - while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - - foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { - if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { - return YES; - } - return NO; - }]; - } - - RCTSetLogFunction(RCTDefaultLogFunction); - - XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); - XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); -} - - -@end diff --git a/example/ios/Podfile b/example/ios/Podfile new file mode 100644 index 000000000..27518177f --- /dev/null +++ b/example/ios/Podfile @@ -0,0 +1,44 @@ +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :ios, 15.1 +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'rnmshowcase' do + config = use_native_modules! + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + rn_maps_path = '../..' + pod 'react-native-google-maps', :path => rn_maps_path + pod 'react-native-maps', :path => rn_maps_path + + target 'rnmshowcaseTests' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 000000000..9aecfd34d --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,1808 @@ +PODS: + - boost (1.84.0) + - DoubleConversion (1.1.6) + - FBLazyVector (0.76.1) + - fmt (9.1.0) + - glog (0.3.5) + - Google-Maps-iOS-Utils (5.0.0): + - GoogleMaps (~> 8.0) + - GoogleMaps (8.4.0): + - GoogleMaps/Maps (= 8.4.0) + - GoogleMaps/Base (8.4.0) + - GoogleMaps/Maps (8.4.0): + - GoogleMaps/Base + - hermes-engine (0.76.1): + - hermes-engine/Pre-built (= 0.76.1) + - hermes-engine/Pre-built (0.76.1) + - RCT-Folly (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCT-Folly/Default (= 2024.01.01.00) + - RCT-Folly/Default (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCT-Folly/Fabric (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCTDeprecation (0.76.1) + - RCTRequired (0.76.1) + - RCTTypeSafety (0.76.1): + - FBLazyVector (= 0.76.1) + - RCTRequired (= 0.76.1) + - React-Core (= 0.76.1) + - React (0.76.1): + - React-Core (= 0.76.1) + - React-Core/DevSupport (= 0.76.1) + - React-Core/RCTWebSocket (= 0.76.1) + - React-RCTActionSheet (= 0.76.1) + - React-RCTAnimation (= 0.76.1) + - React-RCTBlob (= 0.76.1) + - React-RCTImage (= 0.76.1) + - React-RCTLinking (= 0.76.1) + - React-RCTNetwork (= 0.76.1) + - React-RCTSettings (= 0.76.1) + - React-RCTText (= 0.76.1) + - React-RCTVibration (= 0.76.1) + - React-callinvoker (0.76.1) + - React-Core (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default (= 0.76.1) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/CoreModulesHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/Default (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/DevSupport (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default (= 0.76.1) + - React-Core/RCTWebSocket (= 0.76.1) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTActionSheetHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTAnimationHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTBlobHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTImageHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTLinkingHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTNetworkHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTSettingsHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTTextHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTVibrationHeaders (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-Core/RCTWebSocket (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTDeprecation + - React-Core/Default (= 0.76.1) + - React-cxxreact + - React-featureflags + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-perflogger + - React-runtimescheduler + - React-utils + - SocketRocket (= 0.7.1) + - Yoga + - React-CoreModules (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety (= 0.76.1) + - React-Core/CoreModulesHeaders (= 0.76.1) + - React-jsi (= 0.76.1) + - React-jsinspector + - React-NativeModulesApple + - React-RCTBlob + - React-RCTImage (= 0.76.1) + - ReactCodegen + - ReactCommon + - SocketRocket (= 0.7.1) + - React-cxxreact (0.76.1): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.76.1) + - React-debug (= 0.76.1) + - React-jsi (= 0.76.1) + - React-jsinspector + - React-logger (= 0.76.1) + - React-perflogger (= 0.76.1) + - React-runtimeexecutor (= 0.76.1) + - React-timing (= 0.76.1) + - React-debug (0.76.1) + - React-defaultsnativemodule (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-domnativemodule + - React-Fabric + - React-featureflags + - React-featureflagsnativemodule + - React-graphics + - React-idlecallbacksnativemodule + - React-ImageManager + - React-microtasksnativemodule + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - React-domnativemodule (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-FabricComponents + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - React-Fabric (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/animations (= 0.76.1) + - React-Fabric/attributedstring (= 0.76.1) + - React-Fabric/componentregistry (= 0.76.1) + - React-Fabric/componentregistrynative (= 0.76.1) + - React-Fabric/components (= 0.76.1) + - React-Fabric/core (= 0.76.1) + - React-Fabric/dom (= 0.76.1) + - React-Fabric/imagemanager (= 0.76.1) + - React-Fabric/leakchecker (= 0.76.1) + - React-Fabric/mounting (= 0.76.1) + - React-Fabric/observers (= 0.76.1) + - React-Fabric/scheduler (= 0.76.1) + - React-Fabric/telemetry (= 0.76.1) + - React-Fabric/templateprocessor (= 0.76.1) + - React-Fabric/uimanager (= 0.76.1) + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/animations (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/attributedstring (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/componentregistry (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/componentregistrynative (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/components/legacyviewmanagerinterop (= 0.76.1) + - React-Fabric/components/root (= 0.76.1) + - React-Fabric/components/view (= 0.76.1) + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/legacyviewmanagerinterop (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/root (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/components/view (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - Yoga + - React-Fabric/core (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/dom (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/imagemanager (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/leakchecker (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/mounting (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/observers (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/observers/events (= 0.76.1) + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/observers/events (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/scheduler (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/observers/events + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-performancetimeline + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/telemetry (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/templateprocessor (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/uimanager (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric/uimanager/consistency (= 0.76.1) + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererconsistency + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-Fabric/uimanager/consistency (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererconsistency + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCommon/turbomodule/core + - React-FabricComponents (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-FabricComponents/components (= 0.76.1) + - React-FabricComponents/textlayoutmanager (= 0.76.1) + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-FabricComponents/components/inputaccessory (= 0.76.1) + - React-FabricComponents/components/iostextinput (= 0.76.1) + - React-FabricComponents/components/modal (= 0.76.1) + - React-FabricComponents/components/rncore (= 0.76.1) + - React-FabricComponents/components/safeareaview (= 0.76.1) + - React-FabricComponents/components/scrollview (= 0.76.1) + - React-FabricComponents/components/text (= 0.76.1) + - React-FabricComponents/components/textinput (= 0.76.1) + - React-FabricComponents/components/unimplementedview (= 0.76.1) + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/inputaccessory (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/iostextinput (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/modal (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/rncore (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/safeareaview (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/scrollview (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/text (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/textinput (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/components/unimplementedview (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricComponents/textlayoutmanager (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-cxxreact + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-logger + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/core + - Yoga + - React-FabricImage (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - RCTRequired (= 0.76.1) + - RCTTypeSafety (= 0.76.1) + - React-Fabric + - React-graphics + - React-ImageManager + - React-jsi + - React-jsiexecutor (= 0.76.1) + - React-logger + - React-rendererdebug + - React-utils + - ReactCommon + - Yoga + - React-featureflags (0.76.1) + - React-featureflagsnativemodule (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - React-graphics (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-jsi + - React-jsiexecutor + - React-utils + - React-hermes (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-cxxreact (= 0.76.1) + - React-jsi + - React-jsiexecutor (= 0.76.1) + - React-jsinspector + - React-perflogger (= 0.76.1) + - React-runtimeexecutor + - React-idlecallbacksnativemodule (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - React-ImageManager (0.76.1): + - glog + - RCT-Folly/Fabric + - React-Core/Default + - React-debug + - React-Fabric + - React-graphics + - React-rendererdebug + - React-utils + - React-jserrorhandler (0.76.1): + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-cxxreact + - React-debug + - React-jsi + - React-jsi (0.76.1): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-jsiexecutor (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-cxxreact (= 0.76.1) + - React-jsi (= 0.76.1) + - React-jsinspector + - React-perflogger (= 0.76.1) + - React-jsinspector (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-featureflags + - React-jsi + - React-perflogger (= 0.76.1) + - React-runtimeexecutor (= 0.76.1) + - React-jsitracing (0.76.1): + - React-jsi + - React-logger (0.76.1): + - glog + - React-Mapbuffer (0.76.1): + - glog + - React-debug + - React-microtasksnativemodule (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-featureflags + - React-graphics + - React-ImageManager + - React-NativeModulesApple + - React-RCTFabric + - React-rendererdebug + - React-utils + - ReactCodegen + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - Yoga + - react-native-google-maps (0.0.0): + - Google-Maps-iOS-Utils (= 5.0.0) + - GoogleMaps (= 8.4.0) + - React-Core + - react-native-maps (0.0.0): + - React-Core + - React-nativeconfig (0.76.1) + - React-NativeModulesApple (0.76.1): + - glog + - hermes-engine + - React-callinvoker + - React-Core + - React-cxxreact + - React-jsi + - React-jsinspector + - React-runtimeexecutor + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-perflogger (0.76.1): + - DoubleConversion + - RCT-Folly (= 2024.01.01.00) + - React-performancetimeline (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - React-cxxreact + - React-timing + - React-RCTActionSheet (0.76.1): + - React-Core/RCTActionSheetHeaders (= 0.76.1) + - React-RCTAnimation (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Core/RCTAnimationHeaders + - React-jsi + - React-NativeModulesApple + - ReactCodegen + - ReactCommon + - React-RCTAppDelegate (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - RCTRequired + - RCTTypeSafety + - React-Core + - React-CoreModules + - React-debug + - React-defaultsnativemodule + - React-Fabric + - React-featureflags + - React-graphics + - React-hermes + - React-nativeconfig + - React-NativeModulesApple + - React-RCTFabric + - React-RCTImage + - React-RCTNetwork + - React-rendererdebug + - React-RuntimeApple + - React-RuntimeCore + - React-RuntimeHermes + - React-runtimescheduler + - React-utils + - ReactCodegen + - ReactCommon + - React-RCTBlob (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-Core/RCTBlobHeaders + - React-Core/RCTWebSocket + - React-jsi + - React-jsinspector + - React-NativeModulesApple + - React-RCTNetwork + - ReactCodegen + - ReactCommon + - React-RCTFabric (0.76.1): + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-Core + - React-debug + - React-Fabric + - React-FabricComponents + - React-FabricImage + - React-featureflags + - React-graphics + - React-ImageManager + - React-jsi + - React-jsinspector + - React-nativeconfig + - React-performancetimeline + - React-RCTImage + - React-RCTText + - React-rendererconsistency + - React-rendererdebug + - React-runtimescheduler + - React-utils + - Yoga + - React-RCTImage (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Core/RCTImageHeaders + - React-jsi + - React-NativeModulesApple + - React-RCTNetwork + - ReactCodegen + - ReactCommon + - React-RCTLinking (0.76.1): + - React-Core/RCTLinkingHeaders (= 0.76.1) + - React-jsi (= 0.76.1) + - React-NativeModulesApple + - ReactCodegen + - ReactCommon + - ReactCommon/turbomodule/core (= 0.76.1) + - React-RCTNetwork (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Core/RCTNetworkHeaders + - React-jsi + - React-NativeModulesApple + - ReactCodegen + - ReactCommon + - React-RCTSettings (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - RCTTypeSafety + - React-Core/RCTSettingsHeaders + - React-jsi + - React-NativeModulesApple + - ReactCodegen + - ReactCommon + - React-RCTText (0.76.1): + - React-Core/RCTTextHeaders (= 0.76.1) + - Yoga + - React-RCTVibration (0.76.1): + - RCT-Folly (= 2024.01.01.00) + - React-Core/RCTVibrationHeaders + - React-jsi + - React-NativeModulesApple + - ReactCodegen + - ReactCommon + - React-rendererconsistency (0.76.1) + - React-rendererdebug (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - RCT-Folly (= 2024.01.01.00) + - React-debug + - React-rncore (0.76.1) + - React-RuntimeApple (0.76.1): + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-callinvoker + - React-Core/Default + - React-CoreModules + - React-cxxreact + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-Mapbuffer + - React-NativeModulesApple + - React-RCTFabric + - React-RuntimeCore + - React-runtimeexecutor + - React-RuntimeHermes + - React-runtimescheduler + - React-utils + - React-RuntimeCore (0.76.1): + - glog + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-cxxreact + - React-featureflags + - React-jserrorhandler + - React-jsi + - React-jsiexecutor + - React-jsinspector + - React-performancetimeline + - React-runtimeexecutor + - React-runtimescheduler + - React-utils + - React-runtimeexecutor (0.76.1): + - React-jsi (= 0.76.1) + - React-RuntimeHermes (0.76.1): + - hermes-engine + - RCT-Folly/Fabric (= 2024.01.01.00) + - React-featureflags + - React-hermes + - React-jsi + - React-jsinspector + - React-jsitracing + - React-nativeconfig + - React-RuntimeCore + - React-utils + - React-runtimescheduler (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker + - React-cxxreact + - React-debug + - React-featureflags + - React-jsi + - React-performancetimeline + - React-rendererconsistency + - React-rendererdebug + - React-runtimeexecutor + - React-timing + - React-utils + - React-timing (0.76.1) + - React-utils (0.76.1): + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-debug + - React-jsi (= 0.76.1) + - ReactCodegen (0.76.1): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-debug + - React-Fabric + - React-FabricImage + - React-featureflags + - React-graphics + - React-jsi + - React-jsiexecutor + - React-NativeModulesApple + - React-rendererdebug + - React-utils + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - ReactCommon (0.76.1): + - ReactCommon/turbomodule (= 0.76.1) + - ReactCommon/turbomodule (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.76.1) + - React-cxxreact (= 0.76.1) + - React-jsi (= 0.76.1) + - React-logger (= 0.76.1) + - React-perflogger (= 0.76.1) + - ReactCommon/turbomodule/bridging (= 0.76.1) + - ReactCommon/turbomodule/core (= 0.76.1) + - ReactCommon/turbomodule/bridging (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.76.1) + - React-cxxreact (= 0.76.1) + - React-jsi (= 0.76.1) + - React-logger (= 0.76.1) + - React-perflogger (= 0.76.1) + - ReactCommon/turbomodule/core (0.76.1): + - DoubleConversion + - fmt (= 9.1.0) + - glog + - hermes-engine + - RCT-Folly (= 2024.01.01.00) + - React-callinvoker (= 0.76.1) + - React-cxxreact (= 0.76.1) + - React-debug (= 0.76.1) + - React-featureflags (= 0.76.1) + - React-jsi (= 0.76.1) + - React-logger (= 0.76.1) + - React-perflogger (= 0.76.1) + - React-utils (= 0.76.1) + - SocketRocket (0.7.1) + - Yoga (0.0.0) + +DEPENDENCIES: + - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) + - RCTRequired (from `../node_modules/react-native/Libraries/Required`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) + - React-defaultsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/defaults`) + - React-domnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/dom`) + - React-Fabric (from `../node_modules/react-native/ReactCommon`) + - React-FabricComponents (from `../node_modules/react-native/ReactCommon`) + - React-FabricImage (from `../node_modules/react-native/ReactCommon`) + - React-featureflags (from `../node_modules/react-native/ReactCommon/react/featureflags`) + - React-featureflagsnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/featureflags`) + - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) + - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) + - React-idlecallbacksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks`) + - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) + - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) + - React-jsitracing (from `../node_modules/react-native/ReactCommon/hermes/executor/`) + - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) + - React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`) + - react-native-google-maps (from `../..`) + - react-native-maps (from `../..`) + - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) + - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) + - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) + - React-performancetimeline (from `../node_modules/react-native/ReactCommon/react/performance/timeline`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTFabric (from `../node_modules/react-native/React`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - React-rendererconsistency (from `../node_modules/react-native/ReactCommon/react/renderer/consistency`) + - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) + - React-rncore (from `../node_modules/react-native/ReactCommon`) + - React-RuntimeApple (from `../node_modules/react-native/ReactCommon/react/runtime/platform/ios`) + - React-RuntimeCore (from `../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) + - React-RuntimeHermes (from `../node_modules/react-native/ReactCommon/react/runtime`) + - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) + - React-timing (from `../node_modules/react-native/ReactCommon/react/timing`) + - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) + - ReactCodegen (from `build/generated/ios`) + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - Google-Maps-iOS-Utils + - GoogleMaps + - SocketRocket + +EXTERNAL SOURCES: + boost: + :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + FBLazyVector: + :path: "../node_modules/react-native/Libraries/FBLazyVector" + fmt: + :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + hermes-engine: + :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + :tag: hermes-2024-09-09-RNv0.76.0-db6d12e202e15f7a446d8848d6ca8f7abb3cfb32 + RCT-Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + RCTDeprecation: + :path: "../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + RCTRequired: + :path: "../node_modules/react-native/Libraries/Required" + RCTTypeSafety: + :path: "../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Core: + :path: "../node_modules/react-native/" + React-CoreModules: + :path: "../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-debug: + :path: "../node_modules/react-native/ReactCommon/react/debug" + React-defaultsnativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/defaults" + React-domnativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/dom" + React-Fabric: + :path: "../node_modules/react-native/ReactCommon" + React-FabricComponents: + :path: "../node_modules/react-native/ReactCommon" + React-FabricImage: + :path: "../node_modules/react-native/ReactCommon" + React-featureflags: + :path: "../node_modules/react-native/ReactCommon/react/featureflags" + React-featureflagsnativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/featureflags" + React-graphics: + :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" + React-hermes: + :path: "../node_modules/react-native/ReactCommon/hermes" + React-idlecallbacksnativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/idlecallbacks" + React-ImageManager: + :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" + React-jserrorhandler: + :path: "../node_modules/react-native/ReactCommon/jserrorhandler" + React-jsi: + :path: "../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" + React-jsitracing: + :path: "../node_modules/react-native/ReactCommon/hermes/executor/" + React-logger: + :path: "../node_modules/react-native/ReactCommon/logger" + React-Mapbuffer: + :path: "../node_modules/react-native/ReactCommon" + React-microtasksnativemodule: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks" + react-native-google-maps: + :path: "../.." + react-native-maps: + :path: "../.." + React-nativeconfig: + :path: "../node_modules/react-native/ReactCommon" + React-NativeModulesApple: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" + React-perflogger: + :path: "../node_modules/react-native/ReactCommon/reactperflogger" + React-performancetimeline: + :path: "../node_modules/react-native/ReactCommon/react/performance/timeline" + React-RCTActionSheet: + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTAppDelegate: + :path: "../node_modules/react-native/Libraries/AppDelegate" + React-RCTBlob: + :path: "../node_modules/react-native/Libraries/Blob" + React-RCTFabric: + :path: "../node_modules/react-native/React" + React-RCTImage: + :path: "../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../node_modules/react-native/Libraries/Vibration" + React-rendererconsistency: + :path: "../node_modules/react-native/ReactCommon/react/renderer/consistency" + React-rendererdebug: + :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" + React-rncore: + :path: "../node_modules/react-native/ReactCommon" + React-RuntimeApple: + :path: "../node_modules/react-native/ReactCommon/react/runtime/platform/ios" + React-RuntimeCore: + :path: "../node_modules/react-native/ReactCommon/react/runtime" + React-runtimeexecutor: + :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" + React-RuntimeHermes: + :path: "../node_modules/react-native/ReactCommon/react/runtime" + React-runtimescheduler: + :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" + React-timing: + :path: "../node_modules/react-native/ReactCommon/react/timing" + React-utils: + :path: "../node_modules/react-native/ReactCommon/react/utils" + ReactCodegen: + :path: build/generated/ios + ReactCommon: + :path: "../node_modules/react-native/ReactCommon" + Yoga: + :path: "../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + boost: 1dca942403ed9342f98334bf4c3621f011aa7946 + DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 + FBLazyVector: 7075bb12898bc3998fd60f4b7ca422496cc2cdf7 + fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be + glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a + Google-Maps-iOS-Utils: 66d6de12be1ce6d3742a54661e7a79cb317a9321 + GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d + hermes-engine: 46f1ffbf0297f4298862068dd4c274d4ac17a1fd + RCT-Folly: bf5c0376ffe4dd2cf438dcf86db385df9fdce648 + RCTDeprecation: fde92935b3caa6cb65cbff9fbb7d3a9867ffb259 + RCTRequired: 75c6cee42d21c1530a6f204ba32ff57335d19007 + RCTTypeSafety: 7e6fe47bfb693c50d4669db1a480ca5331795f5b + React: 8e73704cdd5c7f801936776d2fc434c605a7827b + React-callinvoker: fa27d1e091e683de88f576e6a5d4efc171929a4c + React-Core: 8dd14bffcc9b877091b698e45701160669a31f91 + React-CoreModules: b4437acf2ef25ce3689c84df661dc5d806559b35 + React-cxxreact: 6125cd820da7e18f9ca8343b3c42ee61634a4e0d + React-debug: f474f5c202a277f76c81bf7cf26284f2c09880d7 + React-defaultsnativemodule: 7141fa704531cbf7a7e7af3bc02adfa367e831a7 + React-domnativemodule: c1806b8584a53ed912012a4d8b2c6f96a84c77a3 + React-Fabric: ba9636cfc7f9b77df6cb7edb2c70d0237026404b + React-FabricComponents: c408da05a4ea5ba071732245b4a7f48f904e610a + React-FabricImage: c409858f319f11709b49ffa6c5bca4faf794cb44 + React-featureflags: 929732439d139ac0662e08f009f1a51ed2b91ed3 + React-featureflagsnativemodule: 02dd903d4cbe4fae0e6cd02bc32a09d30543282f + React-graphics: a5cad35307286e9f83e212834e95fef4010d03d0 + React-hermes: 14aafa9630579b84c2167b563bdb8c811970a03e + React-idlecallbacksnativemodule: 69581ac44bd355acce3739c3fe380c0f6d7a6d09 + React-ImageManager: 41945afb3ace0c52255057ec4ae6af6f5a23539f + React-jserrorhandler: ecbc4622df7ab3d0066a4313cde4172d45745508 + React-jsi: ff383df87c7047e976a66be45df59e4e0db5346e + React-jsiexecutor: 2bb8b172f226f2f502521d33dd7666e701d45f45 + React-jsinspector: 4d51b903543f21076b658ef8412f3102778dbc92 + React-jsitracing: 654f4d9cb9fd99b3d96f239ceb215ae49ce28ac0 + React-logger: 97c9dafae1f1a638001a9d1d0e93d431f2f9cb7b + React-Mapbuffer: 3146a13424f9fec2ea1f1462d49d566e4d69b732 + React-microtasksnativemodule: 02d218c79c72d373a92a8552183f4ead0d1c6e05 + react-native-google-maps: e37f53ff027fbb3223c2f317e8010bb9633d7823 + react-native-maps: 3ae17cf31b3959bafed98adecd53d31fb2114fdc + React-nativeconfig: 93fe8c85a8c40820c57814e30f3e44b94c995a7b + React-NativeModulesApple: b3e076fd0d7b73417fe1e8c8b26e3c57ae9b74aa + React-perflogger: 1c55bcd3c392137cbaf0d21d8bb87ce9a0cebb15 + React-performancetimeline: e89249db10b8f7bf8f72c2e9bd471ac37d48b753 + React-RCTActionSheet: 9407c795fbeee35da2dae3cd6b5c4e5da6ff8bd3 + React-RCTAnimation: 7ee1c2a77aab7e5c568611d8092a994cfcbe8410 + React-RCTAppDelegate: 10c2b0c434baf5a71b53d5c86c4d8d0dbd6bb380 + React-RCTBlob: 761072706300d22624ec2d6bf860b77d95ebd3da + React-RCTFabric: 871d38933a94554d9e27963aa4bb67184dc7529e + React-RCTImage: b6614fde902ec9647f15236da94df2d24c40523f + React-RCTLinking: 25950eda5d5f786bfb3daf513ea7d848555a2a93 + React-RCTNetwork: b69407c4119fd7a1cc07db4a94563f2546f8770d + React-RCTSettings: b310a4923446c3a8950fa866c8cf83323a9e1b87 + React-RCTText: 77c6eda5be1dee657f5183f75fe0fdcdb7b2b35d + React-RCTVibration: b4889c7702aea1b07316be1ec0de2e36e9a4d077 + React-rendererconsistency: 5ef1c4642fd6365bf6d5d4e29a3ae02c3a1b8980 + React-rendererdebug: 7f6a24cbb5008a22ccb34a0d031a259b006facf6 + React-rncore: 0e5394ce20a9d2bf12409d14395588c7b9e6e9ce + React-RuntimeApple: bbe293f233d17304c9597309acde7505080fd53d + React-RuntimeCore: 5a1cbfc3e7af4fbdea2b9b1efd39cd51a4d4006f + React-runtimeexecutor: ffac5f09795a5e881477e0d72a0fa6385456bed3 + React-RuntimeHermes: 0a1fd1c150faed8341887dd89895eeb8d4d2d3c5 + React-runtimescheduler: e7df538274de0c65736068e40efc0d2228f42d0d + React-timing: b3b233fe819d9e5b6ca32b605aa732621bdfa5aa + React-utils: 5362bd16a9563f9916e7a56c011ddc533507650f + ReactCodegen: 865bafc5c17ec2181620ced1a32c39c38ab2951d + ReactCommon: 422e364463f33e336fc4db196aeb50fd801d90d6 + SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 + Yoga: db69236006b8b1c6d55ab453390c882306cbf219 + +PODFILE CHECKSUM: 90b4ba12452122f7040df0a07dde87ba0f44ed5c + +COCOAPODS: 1.16.2 diff --git a/example/ios/rnmshowcase.xcodeproj/project.pbxproj b/example/ios/rnmshowcase.xcodeproj/project.pbxproj new file mode 100644 index 000000000..77511100c --- /dev/null +++ b/example/ios/rnmshowcase.xcodeproj/project.pbxproj @@ -0,0 +1,761 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 00E356F31AD99517003FC87E /* rnmshowcaseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* rnmshowcaseTests.m */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 231400E32E29A085D951F35D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = BC9118EBBCFB2F7F115A78AF /* PrivacyInfo.xcprivacy */; }; + 4C29620DC97E48BF112C778F /* libPods-rnmshowcase-rnmshowcaseTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B3DB5BD1D8514761EF76DF81 /* libPods-rnmshowcase-rnmshowcaseTests.a */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + E488AD8C7A4525666DB1123D /* libPods-rnmshowcase.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 37D3C620F2B4F795C66DA8FC /* libPods-rnmshowcase.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = rnmshowcase; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00E356EE1AD99517003FC87E /* rnmshowcaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = rnmshowcaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* rnmshowcaseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = rnmshowcaseTests.m; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* rnmshowcase.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = rnmshowcase.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = rnmshowcase/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = rnmshowcase/AppDelegate.mm; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = rnmshowcase/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = rnmshowcase/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = rnmshowcase/main.m; sourceTree = ""; }; + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = RnDiffApp/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 37D3C620F2B4F795C66DA8FC /* libPods-rnmshowcase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-rnmshowcase.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3DCD38C83286E8B07C078CFA /* Pods-rnmshowcase.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnmshowcase.release.xcconfig"; path = "Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase.release.xcconfig"; sourceTree = ""; }; + 41E150966890F2E6247E9E4F /* Pods-rnmshowcase-rnmshowcaseTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnmshowcase-rnmshowcaseTests.debug.xcconfig"; path = "Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests.debug.xcconfig"; sourceTree = ""; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = rnmshowcase/LaunchScreen.storyboard; sourceTree = ""; }; + 8A910C563A7AD321B2AB6712 /* Pods-rnmshowcase-rnmshowcaseTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnmshowcase-rnmshowcaseTests.release.xcconfig"; path = "Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests.release.xcconfig"; sourceTree = ""; }; + B3DB5BD1D8514761EF76DF81 /* libPods-rnmshowcase-rnmshowcaseTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-rnmshowcase-rnmshowcaseTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + BC9118EBBCFB2F7F115A78AF /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = rnmshowcase/PrivacyInfo.xcprivacy; sourceTree = ""; }; + C5B6C2B3A7189624E2CE5850 /* Pods-rnmshowcase.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-rnmshowcase.debug.xcconfig"; path = "Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase.debug.xcconfig"; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4C29620DC97E48BF112C778F /* libPods-rnmshowcase-rnmshowcaseTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E488AD8C7A4525666DB1123D /* libPods-rnmshowcase.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00E356EF1AD99517003FC87E /* rnmshowcaseTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* rnmshowcaseTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = rnmshowcaseTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* rnmshowcase */ = { + isa = PBXGroup; + children = ( + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB71A68108700A75B9A /* main.m */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + BC9118EBBCFB2F7F115A78AF /* PrivacyInfo.xcprivacy */, + ); + name = rnmshowcase; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 37D3C620F2B4F795C66DA8FC /* libPods-rnmshowcase.a */, + B3DB5BD1D8514761EF76DF81 /* libPods-rnmshowcase-rnmshowcaseTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* rnmshowcase */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* rnmshowcaseTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* rnmshowcase.app */, + 00E356EE1AD99517003FC87E /* rnmshowcaseTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + C5B6C2B3A7189624E2CE5850 /* Pods-rnmshowcase.debug.xcconfig */, + 3DCD38C83286E8B07C078CFA /* Pods-rnmshowcase.release.xcconfig */, + 41E150966890F2E6247E9E4F /* Pods-rnmshowcase-rnmshowcaseTests.debug.xcconfig */, + 8A910C563A7AD321B2AB6712 /* Pods-rnmshowcase-rnmshowcaseTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* rnmshowcaseTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "rnmshowcaseTests" */; + buildPhases = ( + 6BB2B4F88862047370DBEC4E /* [CP] Check Pods Manifest.lock */, + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + 4BB5BDEB3BB3351A69AAFD04 /* [CP] Embed Pods Frameworks */, + 7149723BEA52900493758BCB /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = rnmshowcaseTests; + productName = rnmshowcaseTests; + productReference = 00E356EE1AD99517003FC87E /* rnmshowcaseTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* rnmshowcase */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "rnmshowcase" */; + buildPhases = ( + EB2F1A58A44FE0FDC95046A9 /* [CP] Check Pods Manifest.lock */, + FD10A7F022414F080027D42C /* Start Packager */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 7178DD5CCC1BA4C10BC1338D /* [CP] Embed Pods Frameworks */, + 2FEBF6F3E6705F77B8AE9357 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rnmshowcase; + productName = rnmshowcase; + productReference = 13B07F961A680F5B00A75B9A /* rnmshowcase.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1120; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "rnmshowcase" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* rnmshowcase */, + 00E356ED1AD99517003FC87E /* rnmshowcaseTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 231400E32E29A085D951F35D /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 2FEBF6F3E6705F77B8AE9357 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 4BB5BDEB3BB3351A69AAFD04 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 6BB2B4F88862047370DBEC4E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-rnmshowcase-rnmshowcaseTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 7149723BEA52900493758BCB /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnmshowcase-rnmshowcaseTests/Pods-rnmshowcase-rnmshowcaseTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 7178DD5CCC1BA4C10BC1338D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-rnmshowcase/Pods-rnmshowcase-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + EB2F1A58A44FE0FDC95046A9 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-rnmshowcase-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + FD10A7F022414F080027D42C /* Start Packager */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Start Packager"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* rnmshowcaseTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* rnmshowcase */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 41E150966890F2E6247E9E4F /* Pods-rnmshowcase-rnmshowcaseTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = rnmshowcaseTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/rnmshowcase.app/rnmshowcase"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8A910C563A7AD321B2AB6712 /* Pods-rnmshowcase-rnmshowcaseTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = rnmshowcaseTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/rnmshowcase.app/rnmshowcase"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C5B6C2B3A7189624E2CE5850 /* Pods-rnmshowcase.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 9QXDAES5A8; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = rnmshowcase/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "salah-ghanim.reactjs.native.example.--PRODUCT-NAME-rfc1034identifier-"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = "salah-ghanim.native.example.rnmshowcases"; + PRODUCT_NAME = rnmshowcase; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3DCD38C83286E8B07C078CFA /* Pods-rnmshowcase.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 9QXDAES5A8; + INFOPLIST_FILE = rnmshowcase/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=iphoneos*]" = org.reactjs.native.example.rnmshowcases; + PRODUCT_NAME = rnmshowcase; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CXX = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD = ""; + LDPLUSPLUS = ""; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "$(inherited)", + "-DRN_FABRIC_ENABLED", + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + OTHER_LDFLAGS = "$(inherited)"; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + USE_HERMES = true; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + CXX = ""; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD = ""; + LDPLUSPLUS = ""; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = ( + "$(inherited)", + "-DRN_FABRIC_ENABLED", + ); + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + OTHER_LDFLAGS = "$(inherited)"; + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + USE_HERMES = true; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "rnmshowcaseTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "rnmshowcase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "rnmshowcase" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/example/ios/AirMapsExplorer.xcodeproj/xcshareddata/xcschemes/AirMapsExplorer.xcscheme b/example/ios/rnmshowcase.xcodeproj/xcshareddata/xcschemes/rnmshowcase.xcscheme similarity index 58% rename from example/ios/AirMapsExplorer.xcodeproj/xcshareddata/xcschemes/AirMapsExplorer.xcscheme rename to example/ios/rnmshowcase.xcodeproj/xcshareddata/xcschemes/rnmshowcase.xcscheme index b4bbba00f..fb773e7bc 100644 --- a/example/ios/AirMapsExplorer.xcodeproj/xcshareddata/xcschemes/AirMapsExplorer.xcscheme +++ b/example/ios/rnmshowcase.xcodeproj/xcshareddata/xcschemes/rnmshowcase.xcscheme @@ -1,6 +1,6 @@ - - - - + BuildableName = "rnmshowcase.app" + BlueprintName = "rnmshowcase" + ReferencedContainer = "container:rnmshowcase.xcodeproj"> @@ -47,23 +33,12 @@ + BuildableName = "rnmshowcaseTests.xctest" + BlueprintName = "rnmshowcaseTests" + ReferencedContainer = "container:rnmshowcase.xcodeproj"> - - - - - - + BuildableName = "rnmshowcase.app" + BlueprintName = "rnmshowcase" + ReferencedContainer = "container:rnmshowcase.xcodeproj"> - - + BuildableName = "rnmshowcase.app" + BlueprintName = "rnmshowcase" + ReferencedContainer = "container:rnmshowcase.xcodeproj"> diff --git a/example/ios/rnmshowcase.xcworkspace/contents.xcworkspacedata b/example/ios/rnmshowcase.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..b595fb999 --- /dev/null +++ b/example/ios/rnmshowcase.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/example/ios/rnmshowcase.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/rnmshowcase.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/example/ios/rnmshowcase.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/rnmshowcase/AppDelegate.h b/example/ios/rnmshowcase/AppDelegate.h new file mode 100644 index 000000000..ea4408571 --- /dev/null +++ b/example/ios/rnmshowcase/AppDelegate.h @@ -0,0 +1,8 @@ +#import +#import + +@interface AppDelegate : RCTAppDelegate + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/example/ios/rnmshowcase/AppDelegate.mm b/example/ios/rnmshowcase/AppDelegate.mm new file mode 100644 index 000000000..29f6af080 --- /dev/null +++ b/example/ios/rnmshowcase/AppDelegate.mm @@ -0,0 +1,34 @@ +#import "AppDelegate.h" + +#import +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + [GMSServices provideAPIKey:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MAPS_API_KEY"]]; + self.moduleName = @"rnmshowcase"; + + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ + return [self bundleURL]; +} + +- (NSURL *)bundleURL +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + +@end diff --git a/example/ios/rnmshowcase/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ios/rnmshowcase/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..81213230d --- /dev/null +++ b/example/ios/rnmshowcase/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example/ios/rnmshowcase/Images.xcassets/Contents.json b/example/ios/rnmshowcase/Images.xcassets/Contents.json new file mode 100644 index 000000000..2d92bd53f --- /dev/null +++ b/example/ios/rnmshowcase/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/example/ios/AirMapsExplorer/Info.plist b/example/ios/rnmshowcase/Info.plist similarity index 84% rename from example/ios/AirMapsExplorer/Info.plist rename to example/ios/rnmshowcase/Info.plist index 682480a6e..d77401465 100644 --- a/example/ios/AirMapsExplorer/Info.plist +++ b/example/ios/rnmshowcase/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion en + CFBundleDisplayName + rnmshowcase CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -22,9 +24,13 @@ 1 LSRequiresIPhoneOS + MAPS_API_KEY + $(MAPS_API_KEY) NSAppTransportSecurity NSAllowsArbitraryLoads + + NSAllowsLocalNetworking NSLocationWhenInUseUsageDescription @@ -33,7 +39,7 @@ LaunchScreen UIRequiredDeviceCapabilities - armv7 + arm64 UISupportedInterfaceOrientations @@ -41,6 +47,8 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIUserInterfaceStyle + Light UIViewControllerBasedStatusBarAppearance diff --git a/example/ios/rnmshowcase/LaunchScreen.storyboard b/example/ios/rnmshowcase/LaunchScreen.storyboard new file mode 100644 index 000000000..72645d5ed --- /dev/null +++ b/example/ios/rnmshowcase/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/ios/rnmshowcase/PrivacyInfo.xcprivacy b/example/ios/rnmshowcase/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..41b8317f0 --- /dev/null +++ b/example/ios/rnmshowcase/PrivacyInfo.xcprivacy @@ -0,0 +1,37 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/example/ios/rnmshowcase/main.m b/example/ios/rnmshowcase/main.m new file mode 100644 index 000000000..d645c7246 --- /dev/null +++ b/example/ios/rnmshowcase/main.m @@ -0,0 +1,10 @@ +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/example/ios/AirMapsExplorerTests/Info.plist b/example/ios/rnmshowcaseTests/Info.plist similarity index 100% rename from example/ios/AirMapsExplorerTests/Info.plist rename to example/ios/rnmshowcaseTests/Info.plist diff --git a/example/ios/rnmshowcaseTests/rnmshowcaseTests.m b/example/ios/rnmshowcaseTests/rnmshowcaseTests.m new file mode 100644 index 000000000..224632375 --- /dev/null +++ b/example/ios/rnmshowcaseTests/rnmshowcaseTests.m @@ -0,0 +1,66 @@ +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React" + +@interface rnmshowcaseTests : XCTestCase + +@end + +@implementation rnmshowcaseTests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; +#ifdef DEBUG + RCTSetLogFunction( + ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); +#endif + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view + matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + +#ifdef DEBUG + RCTSetLogFunction(RCTDefaultLogFunction); +#endif + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + +@end diff --git a/example/metro.config.js b/example/metro.config.js new file mode 100644 index 000000000..674b59f37 --- /dev/null +++ b/example/metro.config.js @@ -0,0 +1,39 @@ +const path = require('path'); +const escape = require('escape-string-regexp'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); +const pak = require('../package.json'); + +const root = path.resolve(__dirname, '..'); + +const modules = Object.keys({ + ...pak.peerDependencies, +}); + +const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); + +/** + * Metro configuration + * https://reactnative.dev/docs/metro + * + * @type {import('metro-config').MetroConfig} + */ +const config = { + watchFolders: [root], + + // We need to make sure that only one version is loaded for peerDependencies + // So we block them at the root, and alias them to the versions in example's node_modules + resolver: { + blacklistRE: exclusionList( + modules.map( + m => new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`), + ), + ), + + extraNodeModules: modules.reduce((acc, name) => { + acc[name] = path.join(__dirname, 'node_modules', name); + return acc; + }, {}), + }, +}; + +module.exports = mergeConfig(getDefaultConfig(__dirname), config); diff --git a/example/package.json b/example/package.json index 125b6e5a5..35c3aac4f 100644 --- a/example/package.json +++ b/example/package.json @@ -1,23 +1,32 @@ { - "name": "AirMapsExplorer", + "name": "rnmshowcase", "version": "0.0.1", "private": true, "scripts": { + "android": "react-native run-android", + "ios": "react-native run-ios", "start": "react-native start", - "watch": "node ./scripts/watch-and-copy-src.js", - "packager": "sh ./node_modules/react-native/packager/packager.sh --root ./node_modules/react-native-maps", - "dev": "concurrently 'npm run watch' 'npm run packager'" + "test": "jest", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx" }, "dependencies": { - "react": "15.2.0", - "react-native": "^0.29.2", - "react-native-maps": "../" + "react": "18.3.1", + "react-native": "0.76.1" }, "devDependencies": { - "concurrently": "^2.2.0", - "fs-extra": "^0.30.0", - "minimatch": "^3.0.2", - "node-watch": "^0.4.0", - "rimraf": "^2.5.4" + "@babel/core": "^7.25.2", + "@babel/preset-env": "^7.25.3", + "@babel/runtime": "^7.25.0", + "@react-native-community/cli": "15.0.0", + "@react-native-community/cli-platform-android": "15.0.0", + "@react-native-community/cli-platform-ios": "15.0.0", + "@react-native/babel-preset": "0.76.1", + "@react-native/eslint-config": "0.76.1", + "@react-native/metro-config": "0.76.1", + "@react-native/typescript-config": "0.76.1", + "@types/react": "^18.2.6", + "@types/react-test-renderer": "^18.0.0", + "babel-plugin-module-resolver": "5.0.0", + "react-test-renderer": "18.3.1" } } diff --git a/example/react-native.config.js b/example/react-native.config.js new file mode 100644 index 000000000..a5166956f --- /dev/null +++ b/example/react-native.config.js @@ -0,0 +1,10 @@ +const path = require('path'); +const pak = require('../package.json'); + +module.exports = { + dependencies: { + [pak.name]: { + root: path.join(__dirname, '..'), + }, + }, +}; diff --git a/example/scripts/watch-and-copy-src.js b/example/scripts/watch-and-copy-src.js deleted file mode 100644 index ed8684894..000000000 --- a/example/scripts/watch-and-copy-src.js +++ /dev/null @@ -1,51 +0,0 @@ -const path = require('path'); -const fs = require('fs-extra'); -const watch = require('node-watch'); -const rimraf = require('rimraf'); -const minimatch = require('minimatch'); - -function copyAndWatch(source, destination, fileGlob) { - console.log(`Cleaning "${destination}"`); - rimraf(destination, () => { - console.log(`Copying "${source}" to "${destination}"`); - fs.copy(source, destination, (err) => { - if (err) console.error(err); - }); - - console.log(`Watching "${source}"`); - watch(source, (filename) => { - const localPath = filename.split(source).pop(); - if (matchesFile(localPath, fileGlob)) { - const destinationPath = `${destination}${localPath}`; - console.log(`Copying "${filename}" to "${destinationPath}"`); - fs.copy(filename, destinationPath, (err) => { - if (err) console.error(err); - }); - } - }); - }); -} - -function matchesFile(filename, fileGlob) { - if (fileGlob == null) return true; - return minimatch(path.basename(filename), fileGlob); -} - -// JavaScript -copyAndWatch( - '../components', - 'node_modules/react-native-maps/components' -); - -// Android -copyAndWatch( - '../android', - 'node_modules/react-native-maps/android' -); - -// iOS -copyAndWatch( - '../ios', - 'node_modules/react-native-maps/ios', - '{*.m,*.h,*.swift}' -); diff --git a/example/src/App.tsx b/example/src/App.tsx new file mode 100644 index 000000000..da6ce7da2 --- /dev/null +++ b/example/src/App.tsx @@ -0,0 +1,230 @@ +import React from 'react'; +import { + Platform, + View, + StyleSheet, + TouchableOpacity, + ScrollView, + Text, + Switch, +} from 'react-native'; +import {PROVIDER_GOOGLE, PROVIDER_DEFAULT} from 'react-native-maps'; +import DisplayLatLng from './examples/DisplayLatLng'; +import ViewsAsMarkers from './examples/ViewsAsMarkers'; +import EventListener from './examples/EventListener'; +import MarkerTypes from './examples/MarkerTypes'; +import DraggableMarkers from './examples/DraggableMarkers'; +import PolygonCreator from './examples/PolygonCreator'; +import PolylineCreator from './examples/PolylineCreator'; +import GradientPolylines from './examples/GradientPolylines'; +import GradientPolylinesFunctional from './examples/GradientPolylinesFunctional'; +import AnimatedViews from './examples/AnimatedViews'; +import AnimatedMarkers from './examples/AnimatedMarkers'; +import Callouts from './examples/Callouts'; +import Overlays from './examples/Overlays'; +import DefaultMarkers from './examples/DefaultMarkers'; +import CustomMarkers from './examples/CustomMarkers'; +import CachedMap from './examples/CachedMap'; +import LoadingMap from './examples/LoadingMap'; +import MapBoundaries from './examples/MapBoundaries'; +import TakeSnapshot from './examples/TakeSnapshot'; +import FitToSuppliedMarkers from './examples/FitToSuppliedMarkers'; +import FitToCoordinates from './examples/FitToCoordinates'; +import LiteMapView from './examples/LiteMapView'; +import CustomTiles from './examples/CustomTiles'; +import WMSTiles from './examples/WMSTiles'; +import ZIndexMarkers from './examples/ZIndexMarkers'; +import StaticMap from './examples/StaticMap'; +import ThemeMap from './examples/ThemeMap'; +import MapStyle from './examples/MapStyle'; +import LegalLabel from './examples/LegalLabel'; +import SetNativePropsOverlays from './examples/SetNativePropsOverlays'; +import CustomOverlay from './examples/CustomOverlay'; +import MapKml from './examples/MapKml'; +import BugMarkerWontUpdate from './examples/BugMarkerWontUpdate'; +import ImageOverlayWithAssets from './examples/ImageOverlayWithAssets'; +import ImageOverlayWithURL from './examples/ImageOverlayWithURL'; +import ImageOverlayWithBearing from './examples/ImageOverlayWithBearing'; +import AnimatedNavigation from './examples/AnimatedNavigation'; +import OnPoiClick from './examples/OnPoiClick'; +import TestIdMarkers from './examples/TestIdMarkers'; +import IndoorMap from './examples/IndoorMap'; +import CameraControl from './examples/CameraControl'; +import MassiveCustomMarkers from './examples/MassiveCustomMarkers'; +import GeojsonMap from './examples/Geojson'; +import CacheURLTiles from './examples/CacheURLTiles'; +import CacheWMSTiles from './examples/CacheWMSTiles'; + +const IOS = Platform.OS === 'ios'; +const ANDROID = Platform.OS === 'android'; + +function makeExampleMapper(useGoogleMaps: boolean) { + if (useGoogleMaps) { + return (example: any) => [ + example[0], + [example[1], example[3]].filter(Boolean).join(' '), + ]; + } + return (example: any) => example; +} + +export default class App extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + Component: null, + useGoogleMaps: ANDROID, + }; + } + + renderExample([Component, title]: any) { + return ( + this.setState({Component})}> + {title} + + ); + } + + renderBackButton() { + return ( + this.setState({Component: null})}> + + + ); + } + + renderGoogleSwitch() { + return ( + + Use GoogleMaps? + this.setState({useGoogleMaps: value})} + style={styles.googleSwitch} + value={this.state.useGoogleMaps} + /> + + ); + } + + renderExamples(examples: any) { + const {Component, useGoogleMaps} = this.state; + + return ( + + {Component && ( + + )} + {Component && this.renderBackButton()} + {!Component && ( + + {IOS && this.renderGoogleSwitch()} + {examples.map((example: any) => this.renderExample(example))} + + )} + + ); + } + + render() { + return this.renderExamples( + [ + // [, , , ] + [StaticMap, 'StaticMap', true], + [ThemeMap, 'ThemeMap', true], + [DisplayLatLng, 'Tracking Position', true, '(incomplete)'], + [ViewsAsMarkers, 'Arbitrary Views as Markers', true], + [EventListener, 'Events', true, '(incomplete)'], + [MarkerTypes, 'Image Based Markers', true], + [DraggableMarkers, 'Draggable Markers', true], + [PolygonCreator, 'Polygon Creator', true], + [PolylineCreator, 'Polyline Creator', true], + [GradientPolylines, 'Gradient Polylines', true], + [GradientPolylinesFunctional, 'Gradient Polylines Functional', true], + [AnimatedViews, 'Animating with MapViews'], + [AnimatedMarkers, 'Animated Marker Position'], + [Callouts, 'Custom Callouts', true], + [Overlays, 'Circles, Polygons, and Polylines', true], + [DefaultMarkers, 'Default Markers', true], + [CustomMarkers, 'Custom Markers', true], + [TakeSnapshot, 'Take Snapshot', true, '(incomplete)'], + [CachedMap, 'Cached Map'], + [LoadingMap, 'Map with loading', true], + [MapBoundaries, 'Get visible map boundaries', true], + [FitToSuppliedMarkers, 'Focus Map On Markers', true], + [FitToCoordinates, 'Fit Map To Coordinates', true], + [LiteMapView, 'Android Lite MapView'], + [CustomTiles, 'Custom Tiles', true], + [WMSTiles, 'WMS Tiles', true], + [ZIndexMarkers, 'Position Markers with Z-index', true], + [MapStyle, 'Customize the style of the map', true], + [LegalLabel, 'Reposition the legal label', true], + [SetNativePropsOverlays, 'Update native props', true], + [CustomOverlay, 'Custom Overlay Component', true], + [TestIdMarkers, 'Test ID for Automation', true], + [MapKml, 'Load Map with KML', true], + [BugMarkerWontUpdate, "BUG: Marker Won't Update (Android)", true], + [ImageOverlayWithAssets, 'Image Overlay Component with Assets', true], + [ImageOverlayWithURL, 'Image Overlay Component with URL', true], + [ImageOverlayWithBearing, 'Image Overlay with Bearing', true], + [AnimatedNavigation, 'Animated Map Navigation', true], + [OnPoiClick, 'On Poi Click', true], + [IndoorMap, 'Indoor Map', true], + [CameraControl, 'CameraControl', true], + [MassiveCustomMarkers, 'MassiveCustomMarkers', true], + [GeojsonMap, 'Geojson', true], + [CacheURLTiles, 'CacheURLTiles', true], + [CacheWMSTiles, 'CacheWMSTiles', true], + ] + // Filter out examples that are not yet supported for Google Maps on iOS. + .filter( + example => + ANDROID || (IOS && (example[2] || !this.state.useGoogleMaps)), + ) + .map(makeExampleMapper(IOS && this.state.useGoogleMaps)), + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + scrollview: { + alignItems: 'center', + paddingVertical: 40, + }, + button: { + flex: 1, + marginTop: 10, + backgroundColor: 'rgba(220,220,220,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + back: { + position: 'absolute', + top: 20, + left: 12, + backgroundColor: 'rgba(255,255,255,0.4)', + padding: 12, + borderRadius: 20, + width: 80, + alignItems: 'center', + justifyContent: 'center', + }, + backButton: {fontWeight: 'bold', fontSize: 30}, + googleSwitch: {marginBottom: 10}, +}); diff --git a/example/src/declaration.d.ts b/example/src/declaration.d.ts new file mode 100644 index 000000000..7c0da476b --- /dev/null +++ b/example/src/declaration.d.ts @@ -0,0 +1,2 @@ +declare module '*.jpg'; +declare module '*.png'; diff --git a/example/src/examples/AnimatedMarkers.tsx b/example/src/examples/AnimatedMarkers.tsx new file mode 100644 index 000000000..049c72110 --- /dev/null +++ b/example/src/examples/AnimatedMarkers.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, + TouchableOpacity, + Platform, +} from 'react-native'; + +import MapView, {Marker, AnimatedRegion} from 'react-native-maps'; + +const screen = Dimensions.get('window'); + +const ASPECT_RATIO = screen.width / screen.height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class AnimatedMarkers extends React.Component { + marker: any; + constructor(props: any) { + super(props); + + this.state = { + coordinate: new AnimatedRegion({ + latitude: LATITUDE, + longitude: LONGITUDE, + }), + }; + } + + animate() { + const {coordinate} = this.state; + const newCoordinate = { + latitude: LATITUDE + (Math.random() - 0.5) * (LATITUDE_DELTA / 2), + longitude: LONGITUDE + (Math.random() - 0.5) * (LONGITUDE_DELTA / 2), + }; + + if (Platform.OS === 'android') { + if (this.marker) { + this.marker._component.animateMarkerToCoordinate(newCoordinate, 500); + } + } else { + // `useNativeDriver` defaults to false if not passed explicitly + coordinate.timing({...newCoordinate, useNativeDriver: true}).start(); + } + } + + render() { + return ( + + + { + this.marker = marker; + }} + coordinate={this.state.coordinate} + /> + + + this.animate()} + style={[styles.bubble, styles.button]}> + Animate + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default AnimatedMarkers; diff --git a/example/src/examples/AnimatedNavigation.tsx b/example/src/examples/AnimatedNavigation.tsx new file mode 100644 index 000000000..23d2d9d09 --- /dev/null +++ b/example/src/examples/AnimatedNavigation.tsx @@ -0,0 +1,133 @@ +import React, {Component} from 'react'; + +import {View, StyleSheet, TouchableOpacity, Text} from 'react-native'; + +import MapView, {Marker} from 'react-native-maps'; +// @ts-ignore +import carImage from './assets/car.png'; + +export default class NavigationMap extends Component { + map: any; + constructor(props: any) { + super(props); + this.state = { + prevPos: null, + curPos: {latitude: 37.420814, longitude: -122.081949}, + curAng: 45, + latitudeDelta: 0.0922, + longitudeDelta: 0.0421, + }; + this.changePosition = this.changePosition.bind(this); + this.getRotation = this.getRotation.bind(this); + this.updateMap = this.updateMap.bind(this); + } + + changePosition(latOffset: number, lonOffset: number) { + const latitude = this.state.curPos.latitude + latOffset; + const longitude = this.state.curPos.longitude + lonOffset; + this.setState({ + prevPos: this.state.curPos, + curPos: {latitude, longitude}, + }); + this.updateMap(); + } + + getRotation(prevPos: any, curPos: any) { + if (!prevPos) { + return 0; + } + const xDiff = curPos.latitude - prevPos.latitude; + const yDiff = curPos.longitude - prevPos.longitude; + return (Math.atan2(yDiff, xDiff) * 180.0) / Math.PI; + } + + updateMap() { + const {curPos, prevPos, curAng} = this.state; + const curRot = this.getRotation(prevPos, curPos); + this.map.animateCamera({heading: curRot, center: curPos, pitch: curAng}); + } + + render() { + return ( + + (this.map = el)} + style={styles.flex} + minZoomLevel={15} + initialRegion={{ + ...this.state.curPos, + latitudeDelta: this.state.latitudeDelta, + longitudeDelta: this.state.longitudeDelta, + }}> + + + + this.changePosition(0.0001, 0)}> + + Lat + + this.changePosition(-0.0001, 0)}> + - Lat + + + + this.changePosition(0, -0.0001)}> + - Lon + + this.changePosition(0, 0.0001)}> + + Lon + + + + ); + } +} + +const styles = StyleSheet.create({ + flex: { + flex: 1, + width: '100%', + }, + buttonContainerUpDown: { + ...StyleSheet.absoluteFillObject, + flexDirection: 'row', + justifyContent: 'center', + }, + buttonContainerLeftRight: { + ...StyleSheet.absoluteFillObject, + flexDirection: 'column', + justifyContent: 'center', + }, + button: { + backgroundColor: 'rgba(100,100,100,0.2)', + position: 'absolute', + alignItems: 'center', + justifyContent: 'center', + borderRadius: 20, + height: 50, + width: 50, + }, + up: { + alignSelf: 'flex-start', + }, + down: { + alignSelf: 'flex-end', + }, + left: { + alignSelf: 'flex-start', + }, + right: { + alignSelf: 'flex-end', + }, +}); diff --git a/example/src/examples/AnimatedPriceMarker.tsx b/example/src/examples/AnimatedPriceMarker.tsx new file mode 100644 index 000000000..3fed53780 --- /dev/null +++ b/example/src/examples/AnimatedPriceMarker.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import {StyleSheet, Text, Animated} from 'react-native'; + +class AnimatedPriceMarker extends React.Component { + render() { + const {amount, selected, style} = this.props; + + const background = selected.interpolate({ + inputRange: [0, 1], + outputRange: ['#FF5A5F', '#4da2ab'], + }); + + const border = selected.interpolate({ + inputRange: [0, 1], + outputRange: ['#D23F44', '#007a87'], + }); + + return ( + + + $ + {amount} + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'column', + alignSelf: 'flex-start', + }, + bubble: { + flex: 0, + flexDirection: 'row', + alignSelf: 'flex-start', + backgroundColor: '#FF5A5F', + paddingVertical: 2, + paddingHorizontal: 4, + borderRadius: 3, + borderColor: '#D23F44', + borderWidth: 0.5, + }, + dollar: { + color: '#fff', + fontSize: 10, + }, + amount: { + color: '#fff', + fontSize: 13, + }, + arrow: { + backgroundColor: 'transparent', + borderColor: 'transparent', + borderWidth: 4, + borderTopColor: '#FF5A5F', + alignSelf: 'center', + marginTop: -9, + }, + arrowBorder: { + backgroundColor: 'transparent', + borderColor: 'transparent', + borderWidth: 4, + borderTopColor: '#D23F44', + alignSelf: 'center', + marginTop: -0.5, + }, + selectedBubble: { + backgroundColor: '#4da2ab', + borderColor: '#007a87', + }, + selectedArrow: { + borderTopColor: '#4da2ab', + }, + selectedArrowBorder: { + borderTopColor: '#007a87', + }, +}); + +export default AnimatedPriceMarker; diff --git a/example/src/examples/AnimatedViews.tsx b/example/src/examples/AnimatedViews.tsx new file mode 100644 index 000000000..0f685b671 --- /dev/null +++ b/example/src/examples/AnimatedViews.tsx @@ -0,0 +1,412 @@ +import React from 'react'; +import {StyleSheet, View, Dimensions, Animated} from 'react-native'; + +import { + Animated as AnimatedMap, + AnimatedRegion, + Marker, +} from 'react-native-maps'; +import PanController from './PanController'; +import PriceMarker from './AnimatedPriceMarker'; + +const screen = Dimensions.get('window'); + +const ASPECT_RATIO = screen.width / screen.height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +const ITEM_SPACING = 10; +const ITEM_PREVIEW = 10; +const ITEM_WIDTH = screen.width - 2 * ITEM_SPACING - 2 * ITEM_PREVIEW; +const SNAP_WIDTH = ITEM_WIDTH + ITEM_SPACING; +const ITEM_PREVIEW_HEIGHT = 150; +const SCALE_END = screen.width / ITEM_WIDTH; +const BREAKPOINT1 = 246; +const BREAKPOINT2 = 350; +const ONE = new Animated.Value(1); + +function getMarkerState(panX: any, panY: any, scrollY: any, i: any) { + const xLeft = -SNAP_WIDTH * i + SNAP_WIDTH / 2; + const xRight = -SNAP_WIDTH * i - SNAP_WIDTH / 2; + const xPos = -SNAP_WIDTH * i; + + const isIndex = panX.interpolate({ + inputRange: [xRight - 1, xRight, xLeft, xLeft + 1], + outputRange: [0, 1, 1, 0], + extrapolate: 'clamp', + }); + + const isNotIndex = panX.interpolate({ + inputRange: [xRight - 1, xRight, xLeft, xLeft + 1], + outputRange: [1, 0, 0, 1], + extrapolate: 'clamp', + }); + + const center = panX.interpolate({ + inputRange: [xPos - 10, xPos, xPos + 10], + outputRange: [0, 1, 0], + extrapolate: 'clamp', + }); + + const selected = panX.interpolate({ + inputRange: [xRight, xPos, xLeft], + outputRange: [0, 1, 0], + extrapolate: 'clamp', + }); + + const translateY = Animated.multiply(isIndex, panY); + + const translateX = panX; + + const anim = Animated.multiply( + isIndex, + scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [0, 1], + extrapolate: 'clamp', + }), + ); + + const scale = Animated.add( + ONE, + Animated.multiply( + isIndex, + scrollY.interpolate({ + inputRange: [BREAKPOINT1, BREAKPOINT2], + outputRange: [0, SCALE_END - 1], + extrapolate: 'clamp', + }), + ), + ); + + // [0 => 1] + let opacity = scrollY.interpolate({ + inputRange: [BREAKPOINT1, BREAKPOINT2], + outputRange: [0, 1], + extrapolate: 'clamp', + }); + + // if i === index: [0 => 0] + // if i !== index: [0 => 1] + opacity = Animated.multiply(isNotIndex, opacity); + + // if i === index: [1 => 1] + // if i !== index: [1 => 0] + opacity = opacity.interpolate({ + inputRange: [0, 1], + outputRange: [1, 0], + }); + + let markerOpacity = scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [0, 1], + extrapolate: 'clamp', + }); + + markerOpacity = Animated.multiply(isNotIndex, markerOpacity).interpolate({ + inputRange: [0, 1], + outputRange: [1, 0], + }); + + const markerScale = selected.interpolate({ + inputRange: [0, 1], + outputRange: [1, 1.2], + }); + + return { + translateY, + translateX, + scale, + opacity, + anim, + center, + selected, + markerOpacity, + markerScale, + }; +} + +class AnimatedViews extends React.Component { + constructor(props: any) { + super(props); + + const panX = new Animated.Value(0); + const panY = new Animated.Value(0); + + const scrollY = panY.interpolate({ + inputRange: [-1, 1], + outputRange: [1, -1], + }); + + const scrollX = panX.interpolate({ + inputRange: [-1, 1], + outputRange: [1, -1], + }); + + const scale = scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [1, 1.6], + extrapolate: 'clamp', + }); + + const translateY = scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [0, -100], + extrapolate: 'clamp', + }); + + const markers = [ + { + id: 0, + amount: 99, + coordinate: { + latitude: LATITUDE, + longitude: LONGITUDE, + }, + }, + { + id: 1, + amount: 199, + coordinate: { + latitude: LATITUDE + 0.004, + longitude: LONGITUDE - 0.004, + }, + }, + { + id: 2, + amount: 285, + coordinate: { + latitude: LATITUDE - 0.004, + longitude: LONGITUDE - 0.004, + }, + }, + ]; + + const animations = markers.map((m, i) => + getMarkerState(panX, panY, scrollY, i), + ); + + this.state = { + panX, + panY, + animations, + index: 0, + canMoveHorizontal: true, + scrollY, + scrollX, + scale, + translateY, + markers, + region: new AnimatedRegion({ + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }), + }; + } + + componentDidMount() { + const {region, panX, panY, scrollX, markers} = this.state; + + panX.addListener(this.onPanXChange); + panY.addListener(this.onPanYChange); + + region.stopAnimation(); + region + .timing({ + latitude: scrollX.interpolate({ + inputRange: markers.map((m: any, i: any) => i * SNAP_WIDTH), + outputRange: markers.map((m: any) => m.coordinate.latitude), + }), + longitude: scrollX.interpolate({ + inputRange: markers.map((m: any, i: any) => i * SNAP_WIDTH), + outputRange: markers.map((m: any) => m.coordinate.longitude), + }), + useNativeDriver: true, // defaults to false if not passed explicitly + duration: 0, + }) + .start(); + } + + onStartShouldSetPanResponder = (e: any) => { + // we only want to move the view if they are starting the gesture on top + // of the view, so this calculates that and returns true if so. If we return + // false, the gesture should get passed to the map view appropriately. + const {panY} = this.state; + const {pageY} = e.nativeEvent; + const topOfMainWindow = ITEM_PREVIEW_HEIGHT + panY.__getValue(); + const topOfTap = screen.height - pageY; + + return topOfTap < topOfMainWindow; + }; + + onMoveShouldSetPanResponder = (e: any) => { + const {panY} = this.state; + const {pageY} = e.nativeEvent; + const topOfMainWindow = ITEM_PREVIEW_HEIGHT + panY.__getValue(); + const topOfTap = screen.height - pageY; + + return topOfTap < topOfMainWindow; + }; + + onPanXChange = ({value}: any) => { + const {index} = this.state; + const newIndex = Math.floor((-1 * value + SNAP_WIDTH / 2) / SNAP_WIDTH); + if (index !== newIndex) { + this.setState({index: newIndex}); + } + }; + + onPanYChange = ({value}: any) => { + const {canMoveHorizontal, region, scrollY, scrollX, markers, index} = + this.state; + const shouldBeMovable = Math.abs(value) < 2; + if (shouldBeMovable !== canMoveHorizontal) { + this.setState({canMoveHorizontal: shouldBeMovable}); + if (!shouldBeMovable) { + const {coordinate} = markers[index]; + region.stopAnimation(); + region + .timing({ + latitude: scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [ + coordinate.latitude, + coordinate.latitude - LATITUDE_DELTA * 0.5 * 0.375, + ], + extrapolate: 'clamp', + }), + latitudeDelta: scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [LATITUDE_DELTA, LATITUDE_DELTA * 0.5], + extrapolate: 'clamp', + }), + longitudeDelta: scrollY.interpolate({ + inputRange: [0, BREAKPOINT1], + outputRange: [LONGITUDE_DELTA, LONGITUDE_DELTA * 0.5], + extrapolate: 'clamp', + }), + useNativeDriver: true, // defaults to false if not passed explictly + duration: 0, + }) + .start(); + } else { + region.stopAnimation(); + region + .timing({ + latitude: scrollX.interpolate({ + inputRange: markers.map((m: any, i: any) => i * SNAP_WIDTH), + outputRange: markers.map((m: any) => m.coordinate.latitude), + }), + longitude: scrollX.interpolate({ + inputRange: markers.map((m: any, i: any) => i * SNAP_WIDTH), + outputRange: markers.map((m: any) => m.coordinate.longitude), + }), + useNativeDriver: true, // defaults to false if not passed explictly + duration: 0, + }) + .start(); + } + } + }; + + onRegionChange(/* region */) { + // this.state.region.setValue(region); + } + + render() { + const {panX, panY, animations, canMoveHorizontal, markers, region} = + this.state; + + return ( + + + + {markers.map((marker: any, i: any) => { + const {selected, markerOpacity, markerScale} = animations[i]; + + return ( + + + + ); + })} + + + {markers.map((marker: any, i: any) => { + const {translateY, translateX, scale, opacity} = animations[i]; + + return ( + + ); + })} + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + }, + itemContainer: { + backgroundColor: 'transparent', + flexDirection: 'row', + paddingHorizontal: ITEM_SPACING / 2 + ITEM_PREVIEW, + position: 'absolute', + // top: screen.height - ITEM_PREVIEW_HEIGHT - 64, + paddingTop: screen.height - ITEM_PREVIEW_HEIGHT - 64, + // paddingTop: !ANDROID ? 0 : screen.height - ITEM_PREVIEW_HEIGHT - 64, + }, + map: { + backgroundColor: 'transparent', + ...StyleSheet.absoluteFillObject, + }, + item: { + width: ITEM_WIDTH, + height: screen.height + 2 * ITEM_PREVIEW_HEIGHT, + backgroundColor: 'red', + marginHorizontal: ITEM_SPACING / 2, + overflow: 'hidden', + borderRadius: 3, + borderColor: '#000', + }, +}); + +export default AnimatedViews; diff --git a/example/src/examples/BugMarkerWontUpdate.tsx b/example/src/examples/BugMarkerWontUpdate.tsx new file mode 100644 index 000000000..e10442a43 --- /dev/null +++ b/example/src/examples/BugMarkerWontUpdate.tsx @@ -0,0 +1,128 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, + TouchableOpacity, +} from 'react-native'; +import MapView from 'react-native-maps'; +import MyLocationMapMarker from './MyLocationMapMarker'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class BugMarkerWontUpdate extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + coordinate: { + latitude: LATITUDE, + longitude: LONGITUDE, + }, + amount: 0, + enableHack: false, + }; + } + + increment() { + this.setState({amount: this.state.amount + 10}); + } + + decrement() { + this.setState({amount: this.state.amount - 10}); + } + + toggleHack() { + this.setState({enableHack: !this.state.enableHack}); + } + + render() { + return ( + + + + + + this.toggleHack()} + style={[styles.bubble, styles.button, styles.hackButton]}> + + {this.state.enableHack ? 'Disable Hack' : 'Enable Hack'} + + + + + this.decrement()} + style={[styles.bubble, styles.button]}> + - + + this.increment()} + style={[styles.bubble, styles.button]}> + + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + hackButton: { + width: 200, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, + toggleHack: {fontSize: 12, fontWeight: 'bold'}, + ammountButton: {fontSize: 20, fontWeight: 'bold'}, +}); + +export default BugMarkerWontUpdate; diff --git a/example/src/examples/CacheURLTiles.tsx b/example/src/examples/CacheURLTiles.tsx new file mode 100644 index 000000000..86d8818ea --- /dev/null +++ b/example/src/examples/CacheURLTiles.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import {StyleSheet, View, Text, Dimensions} from 'react-native'; + +import MapView, {MAP_TYPES, PROVIDER_DEFAULT, UrlTile} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class CustomTiles extends React.Component { + constructor(props: any, context: any) { + super(props, context); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + }; + } + + get mapType() { + // MapKit does not support 'none' as a base map + return this.props.provider === PROVIDER_DEFAULT + ? MAP_TYPES.STANDARD + : MAP_TYPES.NONE; + } + + render() { + const {region} = this.state; + return ( + + + + + + + Cached URLTiles + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default CustomTiles; diff --git a/example/src/examples/CacheWMSTiles.tsx b/example/src/examples/CacheWMSTiles.tsx new file mode 100644 index 000000000..90c93cafe --- /dev/null +++ b/example/src/examples/CacheWMSTiles.tsx @@ -0,0 +1,120 @@ +import React from 'react'; +import {StyleSheet, View, Text, Dimensions} from 'react-native'; + +import MapView, {MAP_TYPES, PROVIDER_DEFAULT, WMSTile} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 63.5; +const LONGITUDE = 23.5; +const LATITUDE_DELTA = 0.152; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class CustomTiles extends React.Component { + constructor(props: any, context: any) { + super(props, context); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + }; + } + + get mapType() { + // MapKit does not support 'none' as a base map + return this.props.provider === PROVIDER_DEFAULT + ? MAP_TYPES.STANDARD + : MAP_TYPES.NONE; + } + + render() { + const {region} = this.state; + return ( + + + + + + + Cached WMSTiles + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default CustomTiles; diff --git a/example/src/examples/CachedMap.tsx b/example/src/examples/CachedMap.tsx new file mode 100644 index 000000000..246c63e03 --- /dev/null +++ b/example/src/examples/CachedMap.tsx @@ -0,0 +1,1826 @@ +import React from 'react'; +import { + Text, + View, + Dimensions, + StyleSheet, + FlatList, + TouchableOpacity, +} from 'react-native'; + +import MapView, {Marker} from 'react-native-maps'; +import flagImg from './assets/flag-blue.png'; + +const HORIZONTAL_PADDING = 12; +const VERTICAL_PADDING = 6; + +class CachedMap extends React.Component { + constructor(props: any) { + super(props); + this.state = { + data: {...COUNTRIES}, + cache: true, + }; + } + + toggleCache() { + // a hack to force listview to reload with the same data + this.setState({ + data: this.state.data, + }); + this.setState({ + cache: !this.state.cache, + data: {...COUNTRIES}, + }); + } + + render() { + const {width} = Dimensions.get('window'); + const mapSize = width - HORIZONTAL_PADDING * 2; + return ( + + + this.toggleCache()} + style={[styles.bubble, styles.button]}> + + {this.state.cache ? 'Cached' : 'Not cached'} + + + + ( + + {region.name} + + + + + )} + keyExtractor={(item, index) => index.toString()} + /> + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + item: { + backgroundColor: 'white', + paddingHorizontal: HORIZONTAL_PADDING, + paddingVertical: VERTICAL_PADDING, + }, + bubble: { + backgroundColor: 'rgba(0,128,255,1.0)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + button: { + width: 100, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + justifyContent: 'flex-end', + }, + buttonText: { + color: 'white', + }, +}); + +const COUNTRIES = [ + { + latitude: 33.93911, + longitudeDelta: 10.0, + name: 'Afghanistan', + longitude: 67.709953, + latitudeDelta: 10.0, + }, + { + latitude: 41.153332, + longitudeDelta: 10.0, + name: 'Albania', + longitude: 20.168331, + latitudeDelta: 10.0, + }, + { + latitude: 28.033886, + longitudeDelta: 10.0, + name: 'Algeria', + longitude: 1.659626, + latitudeDelta: 10.0, + }, + { + latitude: -14.270972, + longitudeDelta: 10.0, + name: 'American Samoa', + longitude: -170.132217, + latitudeDelta: 10.0, + }, + { + latitude: 42.546245, + longitudeDelta: 10.0, + name: 'Andorra', + longitude: 1.601554, + latitudeDelta: 10.0, + }, + { + latitude: -11.202692, + longitudeDelta: 10.0, + name: 'Angola', + longitude: 17.873887, + latitudeDelta: 10.0, + }, + { + latitude: 18.220554, + longitudeDelta: 10.0, + name: 'Anguilla', + longitude: -63.068615, + latitudeDelta: 10.0, + }, + { + latitude: -75.250973, + longitudeDelta: 10.0, + name: 'Antarctica', + longitude: -0.071389, + latitudeDelta: 10.0, + }, + { + latitude: 17.060816, + longitudeDelta: 10.0, + name: 'Antigua and Barbuda', + longitude: -61.796428, + latitudeDelta: 10.0, + }, + { + latitude: -38.416097, + longitudeDelta: 10.0, + name: 'Argentina', + longitude: -63.616672, + latitudeDelta: 10.0, + }, + { + latitude: 40.069099, + longitudeDelta: 10.0, + name: 'Armenia', + longitude: 45.038189, + latitudeDelta: 10.0, + }, + { + latitude: 12.52111, + longitudeDelta: 10.0, + name: 'Aruba', + longitude: -69.968338, + latitudeDelta: 10.0, + }, + { + latitude: -25.274398, + longitudeDelta: 10.0, + name: 'Australia', + longitude: 133.775136, + latitudeDelta: 10.0, + }, + { + latitude: 47.516231, + longitudeDelta: 10.0, + name: 'Austria', + longitude: 14.550072, + latitudeDelta: 10.0, + }, + { + latitude: 40.143105, + longitudeDelta: 10.0, + name: 'Azerbaijan', + longitude: 47.576927, + latitudeDelta: 10.0, + }, + { + latitude: 25.03428, + longitudeDelta: 10.0, + name: 'Bahamas', + longitude: -77.39628, + latitudeDelta: 10.0, + }, + { + latitude: 25.930414, + longitudeDelta: 10.0, + name: 'Bahrain', + longitude: 50.637772, + latitudeDelta: 10.0, + }, + { + latitude: 23.684994, + longitudeDelta: 10.0, + name: 'Bangladesh', + longitude: 90.356331, + latitudeDelta: 10.0, + }, + { + latitude: 13.193887, + longitudeDelta: 10.0, + name: 'Barbados', + longitude: -59.543198, + latitudeDelta: 10.0, + }, + { + latitude: 53.709807, + longitudeDelta: 10.0, + name: 'Belarus', + longitude: 27.953389, + latitudeDelta: 10.0, + }, + { + latitude: 50.503887, + longitudeDelta: 10.0, + name: 'Belgium', + longitude: 4.469936, + latitudeDelta: 10.0, + }, + { + latitude: 17.189877, + longitudeDelta: 10.0, + name: 'Belize', + longitude: -88.49765, + latitudeDelta: 10.0, + }, + { + latitude: 9.30769, + longitudeDelta: 10.0, + name: 'Benin', + longitude: 2.315834, + latitudeDelta: 10.0, + }, + { + latitude: 32.321384, + longitudeDelta: 10.0, + name: 'Bermuda', + longitude: -64.75737, + latitudeDelta: 10.0, + }, + { + latitude: 27.514162, + longitudeDelta: 10.0, + name: 'Bhutan', + longitude: 90.433601, + latitudeDelta: 10.0, + }, + { + latitude: -16.290154, + longitudeDelta: 10.0, + name: 'Bolivia', + longitude: -63.588653, + latitudeDelta: 10.0, + }, + { + latitude: 43.915886, + longitudeDelta: 10.0, + name: 'Bosnia and Herzegovina', + longitude: 17.679076, + latitudeDelta: 10.0, + }, + { + latitude: -22.328474, + longitudeDelta: 10.0, + name: 'Botswana', + longitude: 24.684866, + latitudeDelta: 10.0, + }, + { + latitude: -54.423199, + longitudeDelta: 10.0, + name: 'Bouvet Island', + longitude: 3.413194, + latitudeDelta: 10.0, + }, + { + latitude: -14.235004, + longitudeDelta: 10.0, + name: 'Brazil', + longitude: -51.92528, + latitudeDelta: 10.0, + }, + { + latitude: -6.343194, + longitudeDelta: 10.0, + name: 'British Indian Ocean Territory', + longitude: 71.876519, + latitudeDelta: 10.0, + }, + { + latitude: 18.420695, + longitudeDelta: 10.0, + name: 'British Virgin Islands', + longitude: -64.639968, + latitudeDelta: 10.0, + }, + { + latitude: 4.535277, + longitudeDelta: 10.0, + name: 'Brunei', + longitude: 114.727669, + latitudeDelta: 10.0, + }, + { + latitude: 42.733883, + longitudeDelta: 10.0, + name: 'Bulgaria', + longitude: 25.48583, + latitudeDelta: 10.0, + }, + { + latitude: 12.238333, + longitudeDelta: 10.0, + name: 'Burkina Faso', + longitude: -1.561593, + latitudeDelta: 10.0, + }, + { + latitude: -3.373056, + longitudeDelta: 10.0, + name: 'Burundi', + longitude: 29.918886, + latitudeDelta: 10.0, + }, + { + latitude: 12.565679, + longitudeDelta: 10.0, + name: 'Cambodia', + longitude: 104.990963, + latitudeDelta: 10.0, + }, + { + latitude: 7.369722, + longitudeDelta: 10.0, + name: 'Cameroon', + longitude: 12.354722, + latitudeDelta: 10.0, + }, + { + latitude: 56.130366, + longitudeDelta: 10.0, + name: 'Canada', + longitude: -106.346771, + latitudeDelta: 10.0, + }, + { + latitude: 16.002082, + longitudeDelta: 10.0, + name: 'Cape Verde', + longitude: -24.013197, + latitudeDelta: 10.0, + }, + { + latitude: 19.513469, + longitudeDelta: 10.0, + name: 'Cayman Islands', + longitude: -80.566956, + latitudeDelta: 10.0, + }, + { + latitude: 6.611111, + longitudeDelta: 10.0, + name: 'Central African Republic', + longitude: 20.939444, + latitudeDelta: 10.0, + }, + { + latitude: 15.454166, + longitudeDelta: 10.0, + name: 'Chad', + longitude: 18.732207, + latitudeDelta: 10.0, + }, + { + latitude: -35.675147, + longitudeDelta: 10.0, + name: 'Chile', + longitude: -71.542969, + latitudeDelta: 10.0, + }, + { + latitude: 35.86166, + longitudeDelta: 10.0, + name: 'China', + longitude: 104.195397, + latitudeDelta: 10.0, + }, + { + latitude: -10.447525, + longitudeDelta: 10.0, + name: 'Christmas Island', + longitude: 105.690449, + latitudeDelta: 10.0, + }, + { + latitude: -12.164165, + longitudeDelta: 10.0, + name: 'Cocos [Keeling] Islands', + longitude: 96.870956, + latitudeDelta: 10.0, + }, + { + latitude: 4.570868, + longitudeDelta: 10.0, + name: 'Colombia', + longitude: -74.297333, + latitudeDelta: 10.0, + }, + { + latitude: -11.875001, + longitudeDelta: 10.0, + name: 'Comoros', + longitude: 43.872219, + latitudeDelta: 10.0, + }, + { + latitude: -4.038333, + longitudeDelta: 10.0, + name: 'Congo [DRC]', + longitude: 21.758664, + latitudeDelta: 10.0, + }, + { + latitude: -0.228021, + longitudeDelta: 10.0, + name: 'Congo [Republic]', + longitude: 15.827659, + latitudeDelta: 10.0, + }, + { + latitude: -21.236736, + longitudeDelta: 10.0, + name: 'Cook Islands', + longitude: -159.777671, + latitudeDelta: 10.0, + }, + { + latitude: 9.748917, + longitudeDelta: 10.0, + name: 'Costa Rica', + longitude: -83.753428, + latitudeDelta: 10.0, + }, + { + latitude: 45.1, + longitudeDelta: 10.0, + name: 'Croatia', + longitude: 15.2, + latitudeDelta: 10.0, + }, + { + latitude: 21.521757, + longitudeDelta: 10.0, + name: 'Cuba', + longitude: -77.781167, + latitudeDelta: 10.0, + }, + { + latitude: 35.126413, + longitudeDelta: 10.0, + name: 'Cyprus', + longitude: 33.429859, + latitudeDelta: 10.0, + }, + { + latitude: 49.817492, + longitudeDelta: 10.0, + name: 'Czech Republic', + longitude: 15.472962, + latitudeDelta: 10.0, + }, + { + latitude: 7.539989, + longitudeDelta: 10.0, + name: "C\u00f4te d'Ivoire", + longitude: -5.54708, + latitudeDelta: 10.0, + }, + { + latitude: 56.26392, + longitudeDelta: 10.0, + name: 'Denmark', + longitude: 9.501785, + latitudeDelta: 10.0, + }, + { + latitude: 11.825138, + longitudeDelta: 10.0, + name: 'Djibouti', + longitude: 42.590275, + latitudeDelta: 10.0, + }, + { + latitude: 15.414999, + longitudeDelta: 10.0, + name: 'Dominica', + longitude: -61.370976, + latitudeDelta: 10.0, + }, + { + latitude: 18.735693, + longitudeDelta: 10.0, + name: 'Dominican Republic', + longitude: -70.162651, + latitudeDelta: 10.0, + }, + { + latitude: -1.831239, + longitudeDelta: 10.0, + name: 'Ecuador', + longitude: -78.183406, + latitudeDelta: 10.0, + }, + { + latitude: 26.820553, + longitudeDelta: 10.0, + name: 'Egypt', + longitude: 30.802498, + latitudeDelta: 10.0, + }, + { + latitude: 13.794185, + longitudeDelta: 10.0, + name: 'El Salvador', + longitude: -88.89653, + latitudeDelta: 10.0, + }, + { + latitude: 1.650801, + longitudeDelta: 10.0, + name: 'Equatorial Guinea', + longitude: 10.267895, + latitudeDelta: 10.0, + }, + { + latitude: 15.179384, + longitudeDelta: 10.0, + name: 'Eritrea', + longitude: 39.782334, + latitudeDelta: 10.0, + }, + { + latitude: 58.595272, + longitudeDelta: 10.0, + name: 'Estonia', + longitude: 25.013607, + latitudeDelta: 10.0, + }, + { + latitude: 9.145, + longitudeDelta: 10.0, + name: 'Ethiopia', + longitude: 40.489673, + latitudeDelta: 10.0, + }, + { + latitude: -51.796253, + longitudeDelta: 10.0, + name: 'Falkland Islands [Islas Malvinas]', + longitude: -59.523613, + latitudeDelta: 10.0, + }, + { + latitude: 61.892635, + longitudeDelta: 10.0, + name: 'Faroe Islands', + longitude: -6.911806, + latitudeDelta: 10.0, + }, + { + latitude: -16.578193, + longitudeDelta: 10.0, + name: 'Fiji', + longitude: 179.414413, + latitudeDelta: 10.0, + }, + { + latitude: 61.92411, + longitudeDelta: 10.0, + name: 'Finland', + longitude: 25.748151, + latitudeDelta: 10.0, + }, + { + latitude: 46.227638, + longitudeDelta: 10.0, + name: 'France', + longitude: 2.213749, + latitudeDelta: 10.0, + }, + { + latitude: 3.933889, + longitudeDelta: 10.0, + name: 'French Guiana', + longitude: -53.125782, + latitudeDelta: 10.0, + }, + { + latitude: -17.679742, + longitudeDelta: 10.0, + name: 'French Polynesia', + longitude: -149.406843, + latitudeDelta: 10.0, + }, + { + latitude: -49.280366, + longitudeDelta: 10.0, + name: 'French Southern Territories', + longitude: 69.348557, + latitudeDelta: 10.0, + }, + { + latitude: -0.803689, + longitudeDelta: 10.0, + name: 'Gabon', + longitude: 11.609444, + latitudeDelta: 10.0, + }, + { + latitude: 13.443182, + longitudeDelta: 10.0, + name: 'Gambia', + longitude: -15.310139, + latitudeDelta: 10.0, + }, + { + latitude: 31.354676, + longitudeDelta: 10.0, + name: 'Gaza Strip', + longitude: 34.308825, + latitudeDelta: 10.0, + }, + { + latitude: 42.315407, + longitudeDelta: 10.0, + name: 'Georgia', + longitude: 43.356892, + latitudeDelta: 10.0, + }, + { + latitude: 51.165691, + longitudeDelta: 10.0, + name: 'Germany', + longitude: 10.451526, + latitudeDelta: 10.0, + }, + { + latitude: 7.946527, + longitudeDelta: 10.0, + name: 'Ghana', + longitude: -1.023194, + latitudeDelta: 10.0, + }, + { + latitude: 36.137741, + longitudeDelta: 10.0, + name: 'Gibraltar', + longitude: -5.345374, + latitudeDelta: 10.0, + }, + { + latitude: 39.074208, + longitudeDelta: 10.0, + name: 'Greece', + longitude: 21.824312, + latitudeDelta: 10.0, + }, + { + latitude: 71.706936, + longitudeDelta: 10.0, + name: 'Greenland', + longitude: -42.604303, + latitudeDelta: 10.0, + }, + { + latitude: 12.262776, + longitudeDelta: 10.0, + name: 'Grenada', + longitude: -61.604171, + latitudeDelta: 10.0, + }, + { + latitude: 16.995971, + longitudeDelta: 10.0, + name: 'Guadeloupe', + longitude: -62.067641, + latitudeDelta: 10.0, + }, + { + latitude: 13.444304, + longitudeDelta: 10.0, + name: 'Guam', + longitude: 144.793731, + latitudeDelta: 10.0, + }, + { + latitude: 15.783471, + longitudeDelta: 10.0, + name: 'Guatemala', + longitude: -90.230759, + latitudeDelta: 10.0, + }, + { + latitude: 49.465691, + longitudeDelta: 10.0, + name: 'Guernsey', + longitude: -2.585278, + latitudeDelta: 10.0, + }, + { + latitude: 9.945587, + longitudeDelta: 10.0, + name: 'Guinea', + longitude: -9.696645, + latitudeDelta: 10.0, + }, + { + latitude: 11.803749, + longitudeDelta: 10.0, + name: 'Guinea-Bissau', + longitude: -15.180413, + latitudeDelta: 10.0, + }, + { + latitude: 4.860416, + longitudeDelta: 10.0, + name: 'Guyana', + longitude: -58.93018, + latitudeDelta: 10.0, + }, + { + latitude: 18.971187, + longitudeDelta: 10.0, + name: 'Haiti', + longitude: -72.285215, + latitudeDelta: 10.0, + }, + { + latitude: -53.08181, + longitudeDelta: 10.0, + name: 'Heard Island and McDonald Islands', + longitude: 73.504158, + latitudeDelta: 10.0, + }, + { + latitude: 15.199999, + longitudeDelta: 10.0, + name: 'Honduras', + longitude: -86.241905, + latitudeDelta: 10.0, + }, + { + latitude: 22.396428, + longitudeDelta: 10.0, + name: 'Hong Kong', + longitude: 114.109497, + latitudeDelta: 10.0, + }, + { + latitude: 47.162494, + longitudeDelta: 10.0, + name: 'Hungary', + longitude: 19.503304, + latitudeDelta: 10.0, + }, + { + latitude: 64.963051, + longitudeDelta: 10.0, + name: 'Iceland', + longitude: -19.020835, + latitudeDelta: 10.0, + }, + { + latitude: 20.593684, + longitudeDelta: 10.0, + name: 'India', + longitude: 78.96288, + latitudeDelta: 10.0, + }, + { + latitude: -0.789275, + longitudeDelta: 10.0, + name: 'Indonesia', + longitude: 113.921327, + latitudeDelta: 10.0, + }, + { + latitude: 32.427908, + longitudeDelta: 10.0, + name: 'Iran', + longitude: 53.688046, + latitudeDelta: 10.0, + }, + { + latitude: 33.223191, + longitudeDelta: 10.0, + name: 'Iraq', + longitude: 43.679291, + latitudeDelta: 10.0, + }, + { + latitude: 53.41291, + longitudeDelta: 10.0, + name: 'Ireland', + longitude: -8.24389, + latitudeDelta: 10.0, + }, + { + latitude: 54.236107, + longitudeDelta: 10.0, + name: 'Isle of Man', + longitude: -4.548056, + latitudeDelta: 10.0, + }, + { + latitude: 31.046051, + longitudeDelta: 10.0, + name: 'Israel', + longitude: 34.851612, + latitudeDelta: 10.0, + }, + { + latitude: 41.87194, + longitudeDelta: 10.0, + name: 'Italy', + longitude: 12.56738, + latitudeDelta: 10.0, + }, + { + latitude: 18.109581, + longitudeDelta: 10.0, + name: 'Jamaica', + longitude: -77.297508, + latitudeDelta: 10.0, + }, + { + latitude: 36.204824, + longitudeDelta: 10.0, + name: 'Japan', + longitude: 138.252924, + latitudeDelta: 10.0, + }, + { + latitude: 49.214439, + longitudeDelta: 10.0, + name: 'Jersey', + longitude: -2.13125, + latitudeDelta: 10.0, + }, + { + latitude: 30.585164, + longitudeDelta: 10.0, + name: 'Jordan', + longitude: 36.238414, + latitudeDelta: 10.0, + }, + { + latitude: 48.019573, + longitudeDelta: 10.0, + name: 'Kazakhstan', + longitude: 66.923684, + latitudeDelta: 10.0, + }, + { + latitude: -0.023559, + longitudeDelta: 10.0, + name: 'Kenya', + longitude: 37.906193, + latitudeDelta: 10.0, + }, + { + latitude: -3.370417, + longitudeDelta: 10.0, + name: 'Kiribati', + longitude: -168.734039, + latitudeDelta: 10.0, + }, + { + latitude: 42.602636, + longitudeDelta: 10.0, + name: 'Kosovo', + longitude: 20.902977, + latitudeDelta: 10.0, + }, + { + latitude: 29.31166, + longitudeDelta: 10.0, + name: 'Kuwait', + longitude: 47.481766, + latitudeDelta: 10.0, + }, + { + latitude: 41.20438, + longitudeDelta: 10.0, + name: 'Kyrgyzstan', + longitude: 74.766098, + latitudeDelta: 10.0, + }, + { + latitude: 19.85627, + longitudeDelta: 10.0, + name: 'Laos', + longitude: 102.495496, + latitudeDelta: 10.0, + }, + { + latitude: 56.879635, + longitudeDelta: 10.0, + name: 'Latvia', + longitude: 24.603189, + latitudeDelta: 10.0, + }, + { + latitude: 33.854721, + longitudeDelta: 10.0, + name: 'Lebanon', + longitude: 35.862285, + latitudeDelta: 10.0, + }, + { + latitude: -29.609988, + longitudeDelta: 10.0, + name: 'Lesotho', + longitude: 28.233608, + latitudeDelta: 10.0, + }, + { + latitude: 6.428055, + longitudeDelta: 10.0, + name: 'Liberia', + longitude: -9.429499, + latitudeDelta: 10.0, + }, + { + latitude: 26.3351, + longitudeDelta: 10.0, + name: 'Libya', + longitude: 17.228331, + latitudeDelta: 10.0, + }, + { + latitude: 47.166, + longitudeDelta: 10.0, + name: 'Liechtenstein', + longitude: 9.555373, + latitudeDelta: 10.0, + }, + { + latitude: 55.169438, + longitudeDelta: 10.0, + name: 'Lithuania', + longitude: 23.881275, + latitudeDelta: 10.0, + }, + { + latitude: 49.815273, + longitudeDelta: 10.0, + name: 'Luxembourg', + longitude: 6.129583, + latitudeDelta: 10.0, + }, + { + latitude: 22.198745, + longitudeDelta: 10.0, + name: 'Macau', + longitude: 113.543873, + latitudeDelta: 10.0, + }, + { + latitude: 41.608635, + longitudeDelta: 10.0, + name: 'Macedonia [FYROM]', + longitude: 21.745275, + latitudeDelta: 10.0, + }, + { + latitude: -18.766947, + longitudeDelta: 10.0, + name: 'Madagascar', + longitude: 46.869107, + latitudeDelta: 10.0, + }, + { + latitude: -13.254308, + longitudeDelta: 10.0, + name: 'Malawi', + longitude: 34.301525, + latitudeDelta: 10.0, + }, + { + latitude: 4.210484, + longitudeDelta: 10.0, + name: 'Malaysia', + longitude: 101.975766, + latitudeDelta: 10.0, + }, + { + latitude: 3.202778, + longitudeDelta: 10.0, + name: 'Maldives', + longitude: 73.22068, + latitudeDelta: 10.0, + }, + { + latitude: 17.570692, + longitudeDelta: 10.0, + name: 'Mali', + longitude: -3.996166, + latitudeDelta: 10.0, + }, + { + latitude: 35.937496, + longitudeDelta: 10.0, + name: 'Malta', + longitude: 14.375416, + latitudeDelta: 10.0, + }, + { + latitude: 7.131474, + longitudeDelta: 10.0, + name: 'Marshall Islands', + longitude: 171.184478, + latitudeDelta: 10.0, + }, + { + latitude: 14.641528, + longitudeDelta: 10.0, + name: 'Martinique', + longitude: -61.024174, + latitudeDelta: 10.0, + }, + { + latitude: 21.00789, + longitudeDelta: 10.0, + name: 'Mauritania', + longitude: -10.940835, + latitudeDelta: 10.0, + }, + { + latitude: -20.348404, + longitudeDelta: 10.0, + name: 'Mauritius', + longitude: 57.552152, + latitudeDelta: 10.0, + }, + { + latitude: -12.8275, + longitudeDelta: 10.0, + name: 'Mayotte', + longitude: 45.166244, + latitudeDelta: 10.0, + }, + { + latitude: 23.634501, + longitudeDelta: 10.0, + name: 'Mexico', + longitude: -102.552784, + latitudeDelta: 10.0, + }, + { + latitude: 7.425554, + longitudeDelta: 10.0, + name: 'Micronesia', + longitude: 150.550812, + latitudeDelta: 10.0, + }, + { + latitude: 47.411631, + longitudeDelta: 10.0, + name: 'Moldova', + longitude: 28.369885, + latitudeDelta: 10.0, + }, + { + latitude: 43.750298, + longitudeDelta: 10.0, + name: 'Monaco', + longitude: 7.412841, + latitudeDelta: 10.0, + }, + { + latitude: 46.862496, + longitudeDelta: 10.0, + name: 'Mongolia', + longitude: 103.846656, + latitudeDelta: 10.0, + }, + { + latitude: 42.708678, + longitudeDelta: 10.0, + name: 'Montenegro', + longitude: 19.37439, + latitudeDelta: 10.0, + }, + { + latitude: 16.742498, + longitudeDelta: 10.0, + name: 'Montserrat', + longitude: -62.187366, + latitudeDelta: 10.0, + }, + { + latitude: 31.791702, + longitudeDelta: 10.0, + name: 'Morocco', + longitude: -7.09262, + latitudeDelta: 10.0, + }, + { + latitude: -18.665695, + longitudeDelta: 10.0, + name: 'Mozambique', + longitude: 35.529562, + latitudeDelta: 10.0, + }, + { + latitude: 21.913965, + longitudeDelta: 10.0, + name: 'Myanmar [Burma]', + longitude: 95.956223, + latitudeDelta: 10.0, + }, + { + latitude: -22.95764, + longitudeDelta: 10.0, + name: 'Namibia', + longitude: 18.49041, + latitudeDelta: 10.0, + }, + { + latitude: -0.522778, + longitudeDelta: 10.0, + name: 'Nauru', + longitude: 166.931503, + latitudeDelta: 10.0, + }, + { + latitude: 28.394857, + longitudeDelta: 10.0, + name: 'Nepal', + longitude: 84.124008, + latitudeDelta: 10.0, + }, + { + latitude: 52.132633, + longitudeDelta: 10.0, + name: 'Netherlands', + longitude: 5.291266, + latitudeDelta: 10.0, + }, + { + latitude: 12.226079, + longitudeDelta: 10.0, + name: 'Netherlands Antilles', + longitude: -69.060087, + latitudeDelta: 10.0, + }, + { + latitude: -20.904305, + longitudeDelta: 10.0, + name: 'New Caledonia', + longitude: 165.618042, + latitudeDelta: 10.0, + }, + { + latitude: -40.900557, + longitudeDelta: 10.0, + name: 'New Zealand', + longitude: 174.885971, + latitudeDelta: 10.0, + }, + { + latitude: 12.865416, + longitudeDelta: 10.0, + name: 'Nicaragua', + longitude: -85.207229, + latitudeDelta: 10.0, + }, + { + latitude: 17.607789, + longitudeDelta: 10.0, + name: 'Niger', + longitude: 8.081666, + latitudeDelta: 10.0, + }, + { + latitude: 9.081999, + longitudeDelta: 10.0, + name: 'Nigeria', + longitude: 8.675277, + latitudeDelta: 10.0, + }, + { + latitude: -19.054445, + longitudeDelta: 10.0, + name: 'Niue', + longitude: -169.867233, + latitudeDelta: 10.0, + }, + { + latitude: -29.040835, + longitudeDelta: 10.0, + name: 'Norfolk Island', + longitude: 167.954712, + latitudeDelta: 10.0, + }, + { + latitude: 40.339852, + longitudeDelta: 10.0, + name: 'North Korea', + longitude: 127.510093, + latitudeDelta: 10.0, + }, + { + latitude: 17.33083, + longitudeDelta: 10.0, + name: 'Northern Mariana Islands', + longitude: 145.38469, + latitudeDelta: 10.0, + }, + { + latitude: 60.472024, + longitudeDelta: 10.0, + name: 'Norway', + longitude: 8.468946, + latitudeDelta: 10.0, + }, + { + latitude: 21.512583, + longitudeDelta: 10.0, + name: 'Oman', + longitude: 55.923255, + latitudeDelta: 10.0, + }, + { + latitude: 30.375321, + longitudeDelta: 10.0, + name: 'Pakistan', + longitude: 69.345116, + latitudeDelta: 10.0, + }, + { + latitude: 7.51498, + longitudeDelta: 10.0, + name: 'Palau', + longitude: 134.58252, + latitudeDelta: 10.0, + }, + { + latitude: 31.952162, + longitudeDelta: 10.0, + name: 'Palestinian Territories', + longitude: 35.233154, + latitudeDelta: 10.0, + }, + { + latitude: 8.537981, + longitudeDelta: 10.0, + name: 'Panama', + longitude: -80.782127, + latitudeDelta: 10.0, + }, + { + latitude: -6.314993, + longitudeDelta: 10.0, + name: 'Papua New Guinea', + longitude: 143.95555, + latitudeDelta: 10.0, + }, + { + latitude: -23.442503, + longitudeDelta: 10.0, + name: 'Paraguay', + longitude: -58.443832, + latitudeDelta: 10.0, + }, + { + latitude: -9.189967, + longitudeDelta: 10.0, + name: 'Peru', + longitude: -75.015152, + latitudeDelta: 10.0, + }, + { + latitude: 12.879721, + longitudeDelta: 10.0, + name: 'Philippines', + longitude: 121.774017, + latitudeDelta: 10.0, + }, + { + latitude: -24.703615, + longitudeDelta: 10.0, + name: 'Pitcairn Islands', + longitude: -127.439308, + latitudeDelta: 10.0, + }, + { + latitude: 51.919438, + longitudeDelta: 10.0, + name: 'Poland', + longitude: 19.145136, + latitudeDelta: 10.0, + }, + { + latitude: 39.399872, + longitudeDelta: 10.0, + name: 'Portugal', + longitude: -8.224454, + latitudeDelta: 10.0, + }, + { + latitude: 18.220833, + longitudeDelta: 10.0, + name: 'Puerto Rico', + longitude: -66.590149, + latitudeDelta: 10.0, + }, + { + latitude: 25.354826, + longitudeDelta: 10.0, + name: 'Qatar', + longitude: 51.183884, + latitudeDelta: 10.0, + }, + { + latitude: 45.943161, + longitudeDelta: 10.0, + name: 'Romania', + longitude: 24.96676, + latitudeDelta: 10.0, + }, + { + latitude: 61.52401, + longitudeDelta: 10.0, + name: 'Russia', + longitude: 105.318756, + latitudeDelta: 10.0, + }, + { + latitude: -1.940278, + longitudeDelta: 10.0, + name: 'Rwanda', + longitude: 29.873888, + latitudeDelta: 10.0, + }, + { + latitude: -21.115141, + longitudeDelta: 10.0, + name: 'R\u00e9union', + longitude: 55.536384, + latitudeDelta: 10.0, + }, + { + latitude: -24.143474, + longitudeDelta: 10.0, + name: 'Saint Helena', + longitude: -10.030696, + latitudeDelta: 10.0, + }, + { + latitude: 17.357822, + longitudeDelta: 10.0, + name: 'Saint Kitts and Nevis', + longitude: -62.782998, + latitudeDelta: 10.0, + }, + { + latitude: 13.909444, + longitudeDelta: 10.0, + name: 'Saint Lucia', + longitude: -60.978893, + latitudeDelta: 10.0, + }, + { + latitude: 46.941936, + longitudeDelta: 10.0, + name: 'Saint Pierre and Miquelon', + longitude: -56.27111, + latitudeDelta: 10.0, + }, + { + latitude: 12.984305, + longitudeDelta: 10.0, + name: 'Saint Vincent and the Grenadines', + longitude: -61.287228, + latitudeDelta: 10.0, + }, + { + latitude: -13.759029, + longitudeDelta: 10.0, + name: 'Samoa', + longitude: -172.104629, + latitudeDelta: 10.0, + }, + { + latitude: 43.94236, + longitudeDelta: 10.0, + name: 'San Marino', + longitude: 12.457777, + latitudeDelta: 10.0, + }, + { + latitude: 23.885942, + longitudeDelta: 10.0, + name: 'Saudi Arabia', + longitude: 45.079162, + latitudeDelta: 10.0, + }, + { + latitude: 14.497401, + longitudeDelta: 10.0, + name: 'Senegal', + longitude: -14.452362, + latitudeDelta: 10.0, + }, + { + latitude: 44.016521, + longitudeDelta: 10.0, + name: 'Serbia', + longitude: 21.005859, + latitudeDelta: 10.0, + }, + { + latitude: -4.679574, + longitudeDelta: 10.0, + name: 'Seychelles', + longitude: 55.491977, + latitudeDelta: 10.0, + }, + { + latitude: 8.460555, + longitudeDelta: 10.0, + name: 'Sierra Leone', + longitude: -11.779889, + latitudeDelta: 10.0, + }, + { + latitude: 1.352083, + longitudeDelta: 10.0, + name: 'Singapore', + longitude: 103.819836, + latitudeDelta: 10.0, + }, + { + latitude: 48.669026, + longitudeDelta: 10.0, + name: 'Slovakia', + longitude: 19.699024, + latitudeDelta: 10.0, + }, + { + latitude: 46.151241, + longitudeDelta: 10.0, + name: 'Slovenia', + longitude: 14.995463, + latitudeDelta: 10.0, + }, + { + latitude: -9.64571, + longitudeDelta: 10.0, + name: 'Solomon Islands', + longitude: 160.156194, + latitudeDelta: 10.0, + }, + { + latitude: 5.152149, + longitudeDelta: 10.0, + name: 'Somalia', + longitude: 46.199616, + latitudeDelta: 10.0, + }, + { + latitude: -30.559482, + longitudeDelta: 10.0, + name: 'South Africa', + longitude: 22.937506, + latitudeDelta: 10.0, + }, + { + latitude: -54.429579, + longitudeDelta: 10.0, + name: 'South Georgia and the South Sandwich Islands', + longitude: -36.587909, + latitudeDelta: 10.0, + }, + { + latitude: 35.907757, + longitudeDelta: 10.0, + name: 'South Korea', + longitude: 127.766922, + latitudeDelta: 10.0, + }, + { + latitude: 40.463667, + longitudeDelta: 10.0, + name: 'Spain', + longitude: -3.74922, + latitudeDelta: 10.0, + }, + { + latitude: 7.873054, + longitudeDelta: 10.0, + name: 'Sri Lanka', + longitude: 80.771797, + latitudeDelta: 10.0, + }, + { + latitude: 12.862807, + longitudeDelta: 10.0, + name: 'Sudan', + longitude: 30.217636, + latitudeDelta: 10.0, + }, + { + latitude: 3.919305, + longitudeDelta: 10.0, + name: 'Suriname', + longitude: -56.027783, + latitudeDelta: 10.0, + }, + { + latitude: 77.553604, + longitudeDelta: 10.0, + name: 'Svalbard and Jan Mayen', + longitude: 23.670272, + latitudeDelta: 10.0, + }, + { + latitude: -26.522503, + longitudeDelta: 10.0, + name: 'Swaziland', + longitude: 31.465866, + latitudeDelta: 10.0, + }, + { + latitude: 60.128161, + longitudeDelta: 10.0, + name: 'Sweden', + longitude: 18.643501, + latitudeDelta: 10.0, + }, + { + latitude: 46.818188, + longitudeDelta: 10.0, + name: 'Switzerland', + longitude: 8.227512, + latitudeDelta: 10.0, + }, + { + latitude: 34.802075, + longitudeDelta: 10.0, + name: 'Syria', + longitude: 38.996815, + latitudeDelta: 10.0, + }, + { + latitude: 0.18636, + longitudeDelta: 10.0, + name: 'S\u00e3o Tom\u00e9 and Pr\u00edncipe', + longitude: 6.613081, + latitudeDelta: 10.0, + }, + { + latitude: 23.69781, + longitudeDelta: 10.0, + name: 'Taiwan', + longitude: 120.960515, + latitudeDelta: 10.0, + }, + { + latitude: 38.861034, + longitudeDelta: 10.0, + name: 'Tajikistan', + longitude: 71.276093, + latitudeDelta: 10.0, + }, + { + latitude: -6.369028, + longitudeDelta: 10.0, + name: 'Tanzania', + longitude: 34.888822, + latitudeDelta: 10.0, + }, + { + latitude: 15.870032, + longitudeDelta: 10.0, + name: 'Thailand', + longitude: 100.992541, + latitudeDelta: 10.0, + }, + { + latitude: -8.874217, + longitudeDelta: 10.0, + name: 'Timor-Leste', + longitude: 125.727539, + latitudeDelta: 10.0, + }, + { + latitude: 8.619543, + longitudeDelta: 10.0, + name: 'Togo', + longitude: 0.824782, + latitudeDelta: 10.0, + }, + { + latitude: -8.967363, + longitudeDelta: 10.0, + name: 'Tokelau', + longitude: -171.855881, + latitudeDelta: 10.0, + }, + { + latitude: -21.178986, + longitudeDelta: 10.0, + name: 'Tonga', + longitude: -175.198242, + latitudeDelta: 10.0, + }, + { + latitude: 10.691803, + longitudeDelta: 10.0, + name: 'Trinidad and Tobago', + longitude: -61.222503, + latitudeDelta: 10.0, + }, + { + latitude: 33.886917, + longitudeDelta: 10.0, + name: 'Tunisia', + longitude: 9.537499, + latitudeDelta: 10.0, + }, + { + latitude: 38.963745, + longitudeDelta: 10.0, + name: 'Turkey', + longitude: 35.243322, + latitudeDelta: 10.0, + }, + { + latitude: 38.969719, + longitudeDelta: 10.0, + name: 'Turkmenistan', + longitude: 59.556278, + latitudeDelta: 10.0, + }, + { + latitude: 21.694025, + longitudeDelta: 10.0, + name: 'Turks and Caicos Islands', + longitude: -71.797928, + latitudeDelta: 10.0, + }, + { + latitude: -7.109535, + longitudeDelta: 10.0, + name: 'Tuvalu', + longitude: 177.64933, + latitudeDelta: 10.0, + }, + { + latitude: 18.335765, + longitudeDelta: 10.0, + name: 'U.S. Virgin Islands', + longitude: -64.896335, + latitudeDelta: 10.0, + }, + { + latitude: 1.373333, + longitudeDelta: 10.0, + name: 'Uganda', + longitude: 32.290275, + latitudeDelta: 10.0, + }, + { + latitude: 48.379433, + longitudeDelta: 10.0, + name: 'Ukraine', + longitude: 31.16558, + latitudeDelta: 10.0, + }, + { + latitude: 23.424076, + longitudeDelta: 10.0, + name: 'United Arab Emirates', + longitude: 53.847818, + latitudeDelta: 10.0, + }, + { + latitude: 55.378051, + longitudeDelta: 10.0, + name: 'United Kingdom', + longitude: -3.435973, + latitudeDelta: 10.0, + }, + { + latitude: 37.09024, + longitudeDelta: 10.0, + name: 'United States', + longitude: -95.712891, + latitudeDelta: 10.0, + }, + { + latitude: -32.522779, + longitudeDelta: 10.0, + name: 'Uruguay', + longitude: -55.765835, + latitudeDelta: 10.0, + }, + { + latitude: 41.377491, + longitudeDelta: 10.0, + name: 'Uzbekistan', + longitude: 64.585262, + latitudeDelta: 10.0, + }, + { + latitude: -15.376706, + longitudeDelta: 10.0, + name: 'Vanuatu', + longitude: 166.959158, + latitudeDelta: 10.0, + }, + { + latitude: 41.902916, + longitudeDelta: 10.0, + name: 'Vatican City', + longitude: 12.453389, + latitudeDelta: 10.0, + }, + { + latitude: 6.42375, + longitudeDelta: 10.0, + name: 'Venezuela', + longitude: -66.58973, + latitudeDelta: 10.0, + }, + { + latitude: 14.058324, + longitudeDelta: 10.0, + name: 'Vietnam', + longitude: 108.277199, + latitudeDelta: 10.0, + }, + { + latitude: -13.768752, + longitudeDelta: 10.0, + name: 'Wallis and Futuna', + longitude: -177.156097, + latitudeDelta: 10.0, + }, + { + latitude: 24.215527, + longitudeDelta: 10.0, + name: 'Western Sahara', + longitude: -12.885834, + latitudeDelta: 10.0, + }, + { + latitude: 15.552727, + longitudeDelta: 10.0, + name: 'Yemen', + longitude: 48.516388, + latitudeDelta: 10.0, + }, + { + latitude: -13.133897, + longitudeDelta: 10.0, + name: 'Zambia', + longitude: 27.849332, + latitudeDelta: 10.0, + }, + { + latitude: -19.015438, + longitudeDelta: 10.0, + name: 'Zimbabwe', + longitude: 29.154857, + latitudeDelta: 10.0, + }, +]; + +export default CachedMap; diff --git a/example/src/examples/Callouts.tsx b/example/src/examples/Callouts.tsx new file mode 100644 index 000000000..6a212911b --- /dev/null +++ b/example/src/examples/Callouts.tsx @@ -0,0 +1,206 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, + TouchableOpacity, + Alert, +} from 'react-native'; +import MapView, {Marker, Callout, CalloutSubview} from 'react-native-maps'; +import CustomCallout from './CustomCallout'; + +const {width, height} = Dimensions.get('window'); +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +const SPACE = 0.01; + +class Callouts extends React.Component { + marker1: any; + marker2: any; + marker4: any; + constructor(props: any) { + super(props); + + this.state = { + cnt: 0, + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + markers: [ + { + coordinate: { + latitude: LATITUDE + SPACE, + longitude: LONGITUDE + SPACE, + }, + }, + { + coordinate: { + latitude: LATITUDE + SPACE, + longitude: LONGITUDE - SPACE, + }, + }, + { + coordinate: { + latitude: LATITUDE, + longitude: LONGITUDE, + }, + }, + { + coordinate: { + latitude: LATITUDE, + longitude: LONGITUDE - SPACE / 2, + }, + }, + ], + }; + } + + show() { + this.marker1.showCallout(); + } + + hide() { + this.marker1.hideCallout(); + } + + render() { + const {region, markers} = this.state; + return ( + + + { + this.marker1 = ref; + }} + coordinate={markers[0].coordinate} + title="This is a native view" + description="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation" + /> + + + + This is a plain view + + + + { + this.marker2 = ref; + }}> + { + Alert.alert('callout pressed'); + }} + style={styles.customView}> + + {`This is a custom callout bubble view ${this.state.cnt}`} + { + this.setState({cnt: this.state.cnt + 1}, () => { + this.marker2.redrawCallout(); + }); + }} + style={[styles.calloutButton]}> + Click me + + + + + { + this.marker4 = ref; + }} + coordinate={markers[3].coordinate} + title="You can also open this callout" + description="by pressing on transparent area of custom callout" + /> + + + + Tap on markers to see different callouts + + + + this.show()} + style={[styles.bubble, styles.button]}> + Show + + this.hide()} + style={[styles.bubble, styles.button]}> + Hide + + + + ); + } +} + +const styles = StyleSheet.create({ + customView: { + width: 140, + height: 140, + }, + plainView: { + width: 60, + }, + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, + calloutButton: { + width: 'auto', + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 6, + paddingVertical: 6, + borderRadius: 12, + alignItems: 'center', + marginHorizontal: 10, + marginVertical: 10, + }, +}); + +export default Callouts; diff --git a/example/src/examples/CameraControl.tsx b/example/src/examples/CameraControl.tsx new file mode 100644 index 000000000..ae0b357d2 --- /dev/null +++ b/example/src/examples/CameraControl.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import {StyleSheet, View, TouchableOpacity, Text, Alert} from 'react-native'; + +import MapView from 'react-native-maps'; + +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; + +class CameraControl extends React.Component { + map: any; + async getCamera() { + const camera = await this.map.getCamera(); + Alert.alert('Current camera', JSON.stringify(camera), [{text: 'OK'}], { + cancelable: true, + }); + } + + async setCamera() { + const camera = await this.map.getCamera(); + // Note that we do not have to pass a full camera object to setCamera(). + // Similar to setState(), we can pass only the properties you like to change. + this.map.setCamera({ + heading: camera.heading + 10, + }); + } + + async animateCamera() { + const camera = await this.map.getCamera(); + camera.heading += 40; + camera.pitch += 10; + camera.altitude += 1000; + camera.zoom -= 1; + camera.center.latitude += 0.5; + this.map.animateCamera(camera, {duration: 2000}); + } + + render() { + return ( + + { + this.map = ref; + }} + style={styles.map} + initialCamera={{ + center: { + latitude: LATITUDE, + longitude: LONGITUDE, + }, + pitch: 45, + heading: 90, + altitude: 1000, + zoom: 10, + }} + /> + + this.getCamera()} + style={[styles.bubble, styles.button]}> + Get current camera + + this.setCamera()} + style={[styles.bubble, styles.button]}> + Set Camera + + this.animateCamera()} + style={[styles.bubble, styles.button]}> + Animate Camera + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + button: { + marginTop: 12, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'column', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default CameraControl; diff --git a/example/examples/CustomCallout.js b/example/src/examples/CustomCallout.tsx similarity index 71% rename from example/examples/CustomCallout.js rename to example/src/examples/CustomCallout.tsx index 080d715b6..85a1a5746 100644 --- a/example/examples/CustomCallout.js +++ b/example/src/examples/CustomCallout.tsx @@ -1,28 +1,22 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { - StyleSheet, - View, - Text, -} = ReactNative; +import React from 'react'; -var CustomCallout = React.createClass({ +import {StyleSheet, View} from 'react-native'; + +class CustomCallout extends React.Component { render() { return ( - - {this.props.children} - + {this.props.children} ); - }, -}); + } +} -var styles = StyleSheet.create({ +const styles = StyleSheet.create({ container: { flexDirection: 'column', alignSelf: 'flex-start', @@ -38,11 +32,6 @@ var styles = StyleSheet.create({ borderColor: '#007a87', borderWidth: 0.5, }, - dollar: { - - //color: '#FFFFFF', - //fontSize: 10, - }, amount: { flex: 1, }, @@ -64,4 +53,4 @@ var styles = StyleSheet.create({ }, }); -module.exports = CustomCallout; +export default CustomCallout; diff --git a/example/src/examples/CustomMarkers.tsx b/example/src/examples/CustomMarkers.tsx new file mode 100644 index 000000000..8ae5769bb --- /dev/null +++ b/example/src/examples/CustomMarkers.tsx @@ -0,0 +1,112 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, + TouchableOpacity, +} from 'react-native'; + +import MapView, {Marker} from 'react-native-maps'; +import flagPinkImg from './assets/flag-pink.png'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +let id = 0; + +class CustomMarkers extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + markers: [], + }; + + this.onMapPress = this.onMapPress.bind(this); + } + + onMapPress(e: any) { + this.setState({ + markers: [ + ...this.state.markers, + { + coordinate: e.nativeEvent.coordinate, + key: `foo${id++}`, + }, + ], + }); + } + + render() { + return ( + + + {this.state.markers.map((marker: any) => ( + + ))} + + + this.setState({markers: []})} + style={styles.bubble}> + Tap map to create a marker of random color + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default CustomMarkers; diff --git a/example/src/examples/CustomOverlay.tsx b/example/src/examples/CustomOverlay.tsx new file mode 100644 index 000000000..0d042703c --- /dev/null +++ b/example/src/examples/CustomOverlay.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import {StyleSheet, View, Dimensions} from 'react-native'; + +import MapView from 'react-native-maps'; +import XMarksTheSpot from './CustomOverlayXMarksTheSpot'; + +const {width, height} = Dimensions.get('window'); +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class CustomOverlay extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + coordinates: [ + { + longitude: -122.442753, + latitude: 37.79879, + }, + { + longitude: -122.424728, + latitude: 37.801232, + }, + { + longitude: -122.422497, + latitude: 37.790651, + }, + { + longitude: -122.440693, + latitude: 37.788209, + }, + ], + center: { + longitude: -122.4326648935676, + latitude: 37.79418561114521, + }, + }; + } + + render() { + const {coordinates, center, region} = this.state; + return ( + + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, +}); + +export default CustomOverlay; diff --git a/example/src/examples/CustomOverlayXMarksTheSpot.tsx b/example/src/examples/CustomOverlayXMarksTheSpot.tsx new file mode 100644 index 000000000..e53681d02 --- /dev/null +++ b/example/src/examples/CustomOverlayXMarksTheSpot.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import {View} from 'react-native'; +import {Polygon, Polyline, Marker} from 'react-native-maps'; + +class XMarksTheSpot extends React.Component { + render() { + return ( + + + + + + + ); + } +} + +export default XMarksTheSpot; diff --git a/example/src/examples/CustomTiles.tsx b/example/src/examples/CustomTiles.tsx new file mode 100644 index 000000000..46a52f3c0 --- /dev/null +++ b/example/src/examples/CustomTiles.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import {StyleSheet, View, Text, Dimensions} from 'react-native'; +import MapView, {MAP_TYPES, PROVIDER_DEFAULT, UrlTile} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class CustomTiles extends React.Component { + constructor(props: any, context: any) { + super(props, context); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + }; + } + + get mapType() { + // MapKit does not support 'none' as a base map + return this.props.provider === PROVIDER_DEFAULT + ? MAP_TYPES.STANDARD + : MAP_TYPES.NONE; + } + + render() { + const {region} = this.state; + return ( + + + + + + + Custom Tiles + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default CustomTiles; diff --git a/example/src/examples/CustomTilesLocal.tsx b/example/src/examples/CustomTilesLocal.tsx new file mode 100644 index 000000000..dd3d1da8a --- /dev/null +++ b/example/src/examples/CustomTilesLocal.tsx @@ -0,0 +1,104 @@ +import React from 'react'; +import {StyleSheet, View, Text, Dimensions} from 'react-native'; +import MapView, { + MAP_TYPES, + PROVIDER_DEFAULT, + LocalTile, +} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class CustomTiles extends React.Component { + constructor(props: any, context: any) { + super(props, context); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + }; + } + + get mapType() { + // MapKit does not support 'none' as a base map + return this.props.provider === PROVIDER_DEFAULT + ? MAP_TYPES.STANDARD + : MAP_TYPES.NONE; + } + + render() { + const {region} = this.state; + return ( + + + + + + + Custom Tiles Local + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + }, + bubble: { + flex: 1, + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default CustomTiles; diff --git a/example/examples/DefaultMarkers.js b/example/src/examples/DefaultMarkers.tsx similarity index 62% rename from example/examples/DefaultMarkers.js rename to example/src/examples/DefaultMarkers.tsx index b4c978d5a..880e9d258 100644 --- a/example/examples/DefaultMarkers.js +++ b/example/src/examples/DefaultMarkers.tsx @@ -1,17 +1,14 @@ -var React = require('react'); -var ReactNative = require('react-native'); -var { +import React from 'react'; +import { StyleSheet, - PropTypes, View, Text, Dimensions, TouchableOpacity, -} = ReactNative; +} from 'react-native'; +import MapView, {Marker} from 'react-native-maps'; -var MapView = require('react-native-maps'); - -var { width, height } = Dimensions.get('window'); +const {width, height} = Dimensions.get('window'); const ASPECT_RATIO = width / height; const LATITUDE = 37.78825; @@ -21,12 +18,16 @@ const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; let id = 0; function randomColor() { - return '#'+Math.floor(Math.random()*16777215).toString(16); + return `#${Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6)}`; } -var DefaultMarkers = React.createClass({ - getInitialState() { - return { +class DefaultMarkers extends React.Component { + constructor(props: any) { + super(props); + + this.state = { region: { latitude: LATITUDE, longitude: LONGITUDE, @@ -35,9 +36,9 @@ var DefaultMarkers = React.createClass({ }, markers: [], }; - }, + } - onMapPress(e) { + onMapPress(e: any) { this.setState({ markers: [ ...this.state.markers, @@ -48,18 +49,19 @@ var DefaultMarkers = React.createClass({ }, ], }); - }, + } render() { return ( - {this.state.markers.map(marker => ( - this.onMapPress(e)}> + {this.state.markers.map((marker: any) => ( + - - Tap to create a marker of random color - + this.setState({markers: []})} + style={styles.bubble}> + Tap map to create a marker of random color + ); - }, -}); + } +} -var styles = StyleSheet.create({ +const styles = StyleSheet.create({ container: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, + ...StyleSheet.absoluteFillObject, justifyContent: 'flex-end', alignItems: 'center', }, map: { - position: 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, + ...StyleSheet.absoluteFillObject, }, bubble: { backgroundColor: 'rgba(255,255,255,0.7)', @@ -116,4 +112,4 @@ var styles = StyleSheet.create({ }, }); -module.exports = DefaultMarkers; +export default DefaultMarkers; diff --git a/example/src/examples/DisplayLatLng.tsx b/example/src/examples/DisplayLatLng.tsx new file mode 100644 index 000000000..2e509d67e --- /dev/null +++ b/example/src/examples/DisplayLatLng.tsx @@ -0,0 +1,168 @@ +import React from 'react'; +import { + StyleSheet, + View, + Text, + Dimensions, + TouchableOpacity, +} from 'react-native'; + +import MapView, {MAP_TYPES} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +class DisplayLatLng extends React.Component { + map: any; + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + }; + } + + onRegionChange(region: any) { + this.setState({region}); + } + + jumpRandom() { + this.setState({region: this.randomRegion()}); + } + + animateRandom() { + this.map.animateToRegion(this.randomRegion()); + } + + animateRandomCoordinate() { + this.map.animateCamera({center: this.randomCoordinate()}); + } + + animateToRandomBearing() { + this.map.animateCamera({heading: this.getRandomFloat(-360, 360)}); + } + + animateToRandomViewingAngle() { + this.map.animateCamera({pitch: this.getRandomFloat(0, 90)}); + } + + getRandomFloat(min: any, max: any) { + return Math.random() * (max - min) + min; + } + + randomCoordinate() { + const region = this.state.region; + return { + 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 ( + + { + this.map = ref; + }} + mapType={MAP_TYPES.TERRAIN} + style={styles.map} + initialRegion={this.state.region} + onRegionChange={region => this.onRegionChange(region)} + /> + + + {this.state.region.latitude.toPrecision(7)}, + {this.state.region.longitude.toPrecision(7)} + + + + this.jumpRandom()} + style={[styles.bubble, styles.button]}> + Jump + + this.animateRandom()} + style={[styles.bubble, styles.button]}> + 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) + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 100, + paddingHorizontal: 8, + alignItems: 'center', + justifyContent: 'center', + marginHorizontal: 5, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, + buttonText: { + textAlign: 'center', + }, + centeredText: {textAlign: 'center'}, +}); + +export default DisplayLatLng; diff --git a/example/src/examples/DraggableMarkers.tsx b/example/src/examples/DraggableMarkers.tsx new file mode 100644 index 000000000..9464afb53 --- /dev/null +++ b/example/src/examples/DraggableMarkers.tsx @@ -0,0 +1,84 @@ +import React from 'react'; +import {StyleSheet, View, Dimensions} from 'react-native'; + +import MapView, {Marker} from 'react-native-maps'; +import PriceMarker from './PriceMarker'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +const SPACE = 0.01; + +function log(eventName: any, e: any) { + console.log(eventName, e.nativeEvent); +} + +class MarkerTypes extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + a: { + latitude: LATITUDE + SPACE, + longitude: LONGITUDE + SPACE, + }, + b: { + latitude: LATITUDE - SPACE, + longitude: LONGITUDE - SPACE, + }, + }; + } + + render() { + return ( + + + log('onSelect', e)} + onDrag={e => log('onDrag', e)} + onDragStart={e => log('onDragStart', e)} + onDragEnd={e => log('onDragEnd', e)} + onPress={e => log('onPress', e)} + draggable> + + + log('onSelect', e)} + onDrag={e => log('onDrag', e)} + onDragStart={e => log('onDragStart', e)} + onDragEnd={e => log('onDragEnd', e)} + onPress={e => log('onPress', e)} + draggable + /> + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, +}); + +export default MarkerTypes; diff --git a/example/src/examples/EventListener.tsx b/example/src/examples/EventListener.tsx new file mode 100644 index 000000000..b5cacfead --- /dev/null +++ b/example/src/examples/EventListener.tsx @@ -0,0 +1,230 @@ +import React from 'react'; +import {StyleSheet, View, Text, Dimensions, ScrollView} from 'react-native'; +import MapView, {Marker, Polygon, Polyline, Callout} from 'react-native-maps'; +import PriceMarker from './PriceMarker'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +let id = 0; + +class Event extends React.Component { + shouldComponentUpdate(nextProps: any) { + return this.props.event.id !== nextProps.event.id; + } + + render() { + const {event} = this.props; + return ( + + {event.name} + + {JSON.stringify(event.data, null, 2)} + + + ); + } +} + +class EventListener extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + events: [], + }; + } + + makeEvent(e: any, name: any) { + return { + id: id++, + name, + data: e.nativeEvent ? e.nativeEvent : e, + }; + } + + recordEvent(name: any) { + return (e: any) => { + if (e.persist) { + e.persist(); // Avoids warnings relating to https://fb.me/react-event-pooling + } + this.setState((prevState: any) => ({ + events: [this.makeEvent(e, name), ...prevState.events.slice(0, 10)], + })); + }; + } + + render() { + return ( + + + + + + + + + Well hello there... + + + + + + + + + {this.state.events.map((event: any) => ( + + ))} + + + + ); + } +} + +const styles = StyleSheet.create({ + callout: { + width: 60, + }, + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + event: { + borderBottomWidth: 1, + borderBottomColor: '#ccc', + padding: 8, + }, + eventData: { + fontSize: 10, + fontFamily: 'courier', + color: '#555', + }, + eventName: { + fontSize: 13, + fontWeight: 'bold', + color: '#222', + }, + eventList: { + position: 'absolute', + top: height / 2, + left: 0, + right: 0, + bottom: 0, + }, + map: { + position: 'absolute', + top: 0, + left: 0, + right: 0, + bottom: height / 2, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default EventListener; diff --git a/example/src/examples/FitToCoordinates.tsx b/example/src/examples/FitToCoordinates.tsx new file mode 100644 index 000000000..bcd65a945 --- /dev/null +++ b/example/src/examples/FitToCoordinates.tsx @@ -0,0 +1,140 @@ +import React from 'react'; +import { + StyleSheet, + View, + Dimensions, + TouchableOpacity, + Text, +} from 'react-native'; + +import MapView, {Marker} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +const SPACE = 0.01; + +function createMarker(modifier = 1) { + return { + latitude: LATITUDE - SPACE * modifier, + longitude: LONGITUDE - SPACE * modifier, + }; +} + +const MARKERS = [ + createMarker(), + createMarker(2), + createMarker(3), + createMarker(4), +]; + +const DEFAULT_PADDING = {top: 40, right: 40, bottom: 40, left: 40}; + +class FitToCoordinates extends React.Component { + map: any; + async logFrames() { + const visMarkersFrames = await this.map.getMarkersFrames(true); + console.log('Visible markers frames:', visMarkersFrames); + const allMarkersFrames = await this.map.getMarkersFrames(); + console.log('All markers frames:', allMarkersFrames); + } + + fitPadding() { + this.map.fitToCoordinates([MARKERS[2], MARKERS[3]], { + edgePadding: {top: 100, right: 100, bottom: 100, left: 100}, + animated: true, + }); + } + + fitBottomTwoMarkers() { + this.map.fitToCoordinates([MARKERS[2], MARKERS[3]], { + edgePadding: DEFAULT_PADDING, + animated: true, + }); + } + + fitAllMarkers() { + this.map.fitToCoordinates(MARKERS, { + edgePadding: DEFAULT_PADDING, + animated: true, + }); + } + + render() { + return ( + + { + this.map = ref; + }} + style={styles.map} + initialRegion={{ + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }}> + {MARKERS.map((marker, i) => ( + + ))} + + + this.fitPadding()} + style={[styles.bubble, styles.button]}> + Fit Bottom Two Markers with Padding + + this.fitBottomTwoMarkers()} + style={[styles.bubble, styles.button]}> + Fit Bottom Two Markers + + this.fitAllMarkers()} + style={[styles.bubble, styles.button]}> + Fit All Markers + + this.logFrames()} + style={[styles.bubble, styles.button]}> + Log markers frames + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + button: { + marginTop: 12, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'column', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); + +export default FitToCoordinates; diff --git a/example/src/examples/FitToSuppliedMarkers.tsx b/example/src/examples/FitToSuppliedMarkers.tsx new file mode 100644 index 000000000..859f76b6b --- /dev/null +++ b/example/src/examples/FitToSuppliedMarkers.tsx @@ -0,0 +1,133 @@ +import React from 'react'; +import {StyleSheet, View, Dimensions} from 'react-native'; +import MapView, {Marker} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +const SPACE = 0.01; + +const markerIDs = ['Marker1', 'Marker2', 'Marker3', 'Marker4', 'Marker5']; +const timeout = 4000; +let animationTimeout: any; + +class FocusOnMarkers extends React.Component { + map: any; + constructor(props: any) { + super(props); + + this.state = { + a: { + latitude: LATITUDE + SPACE, + longitude: LONGITUDE + SPACE, + }, + b: { + latitude: LATITUDE - SPACE, + longitude: LONGITUDE - SPACE, + }, + c: { + latitude: LATITUDE - SPACE * 2, + longitude: LONGITUDE - SPACE * 2, + }, + d: { + latitude: LATITUDE - SPACE * 3, + longitude: LONGITUDE - SPACE * 3, + }, + e: { + latitude: LATITUDE - SPACE * 4, + longitude: LONGITUDE - SPACE * 4, + }, + }; + } + + componentDidMount() { + animationTimeout = setTimeout(() => { + this.focus1(); + }, timeout); + } + + componentWillUnmount() { + if (animationTimeout) { + clearTimeout(animationTimeout); + } + } + + focusMap(markers: any, animated: any) { + console.log(`Markers received to populate map: ${markers}`); + this.map.fitToSuppliedMarkers(markers, animated); + } + + focus1() { + animationTimeout = setTimeout(() => { + this.focusMap([markerIDs[1], markerIDs[4]], true); + + this.focus2(); + }, timeout); + } + + focus2() { + animationTimeout = setTimeout(() => { + this.focusMap([markerIDs[2], markerIDs[3]], false); + + this.focus3(); + }, timeout); + } + + focus3() { + animationTimeout = setTimeout(() => { + this.focusMap([markerIDs[1], markerIDs[2]], false); + + this.focus4(); + }, timeout); + } + + focus4() { + animationTimeout = setTimeout(() => { + this.focusMap([markerIDs[0], markerIDs[3]], true); + + this.focus1(); + }, timeout); + } + + render() { + return ( + + { + this.map = ref; + }} + style={styles.map} + initialRegion={{ + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }}> + + + + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, +}); + +export default FocusOnMarkers; diff --git a/example/src/examples/Geojson.tsx b/example/src/examples/Geojson.tsx new file mode 100644 index 000000000..7048d4b88 --- /dev/null +++ b/example/src/examples/Geojson.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import MapView, {Geojson} from 'react-native-maps'; +import {StyleSheet} from 'react-native'; +const myPlace: any = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [64.165329, 48.844287], + }, + }, + ], +}; + +const GeojsonMap = () => ( + + + +); + +export default GeojsonMap; diff --git a/example/src/examples/GradientPolylines.tsx b/example/src/examples/GradientPolylines.tsx new file mode 100644 index 000000000..04e4f482a --- /dev/null +++ b/example/src/examples/GradientPolylines.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import {StyleSheet, Dimensions} from 'react-native'; + +import MapView, {Polyline} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +const COORDINATES = [ + {latitude: 37.8025259, longitude: -122.4351431}, + {latitude: 37.7896386, longitude: -122.421646}, + {latitude: 37.7665248, longitude: -122.4161628}, + {latitude: 37.7734153, longitude: -122.4577787}, + {latitude: 37.7948605, longitude: -122.4596065}, + {latitude: 37.8025259, longitude: -122.4351431}, +]; + +const COLORS = [ + '#7F0000', + '#00000000', // no color, creates a "long" gradient between the previous and next coordinate + '#B24112', + '#E5845C', + '#238C23', + '#7F0000', +]; + +class GradientPolylines extends React.Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + coordinates: [], + }; + this.setState({coordinates: COORDINATES}); + } + + render() { + return ( + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + }, +}); + +export default GradientPolylines; diff --git a/example/src/examples/GradientPolylinesFunctional.tsx b/example/src/examples/GradientPolylinesFunctional.tsx new file mode 100644 index 000000000..54662f260 --- /dev/null +++ b/example/src/examples/GradientPolylinesFunctional.tsx @@ -0,0 +1,70 @@ +import React, {useEffect, useState} from 'react'; +import {StyleSheet, Dimensions} from 'react-native'; + +import MapView, {Polyline, LatLng, Provider} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 37.78825; +const LONGITUDE = -122.4324; +const LATITUDE_DELTA = 0.0922; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +const COORDINATES = [ + {latitude: 37.8025259, longitude: -122.4351431}, + {latitude: 37.7896386, longitude: -122.421646}, + {latitude: 37.7665248, longitude: -122.4161628}, + {latitude: 37.7734153, longitude: -122.4577787}, + {latitude: 37.7948605, longitude: -122.4596065}, + {latitude: 37.8025259, longitude: -122.4351431}, +]; + +const COLORS = [ + '#7F0000', + '#00000000', // no color, creates a "long" gradient between the previous and next coordinate + '#B24112', + '#E5845C', + '#238C23', + '#7F0000', +]; + +export type GradientPolylinesFunctionalProps = { + provider: Provider; +}; + +const GradientPolylines = (props: GradientPolylinesFunctionalProps) => { + const [region] = useState({ + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }); + + const [polylineSteps, setPolylineSteps] = useState([]); + + useEffect(() => setPolylineSteps(COORDINATES), []); + + return ( + + + + ); +}; + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + }, +}); + +export default GradientPolylines; diff --git a/example/src/examples/ImageOverlayWithAssets.tsx b/example/src/examples/ImageOverlayWithAssets.tsx new file mode 100644 index 000000000..ebacd6543 --- /dev/null +++ b/example/src/examples/ImageOverlayWithAssets.tsx @@ -0,0 +1,84 @@ +import React, {Component} from 'react'; +import {StyleSheet, View, Dimensions} from 'react-native'; + +import MapView, {Overlay} from 'react-native-maps'; +import flagPinkImg from './assets/flag-pink.png'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 35.679976; +const LONGITUDE = 139.768458; +const LATITUDE_DELTA = 0.01; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +// 116423, 51613, 17 +const OVERLAY_TOP_LEFT_COORDINATE = [35.68184060244454, 139.76531982421875]; +const OVERLAY_BOTTOM_RIGHT_COORDINATE = [35.679609609368576, 139.76806640625]; +const IMAGE = flagPinkImg; + +export default class ImageOverlayWithURL extends Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + overlay: { + bounds: [OVERLAY_TOP_LEFT_COORDINATE, OVERLAY_BOTTOM_RIGHT_COORDINATE], + image: IMAGE, + }, + }; + } + + render() { + return ( + + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); diff --git a/example/src/examples/ImageOverlayWithBearing.tsx b/example/src/examples/ImageOverlayWithBearing.tsx new file mode 100644 index 000000000..eba30da3f --- /dev/null +++ b/example/src/examples/ImageOverlayWithBearing.tsx @@ -0,0 +1,103 @@ +import React, {Component} from 'react'; +import {StyleSheet, View, Dimensions} from 'react-native'; + +import MapView, {Overlay} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 35.679976; +const LONGITUDE = 139.768458; +const LATITUDE_DELTA = 0.01; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +// 116423, 51613, 17 +const OVERLAY_TOP_LEFT_COORDINATE1 = [35.679609609368576, 139.76531982421875]; +const OVERLAY_BOTTOM_RIGHT_COORDINATE1 = [35.68184060244454, 139.76806640625]; +const IMAGE_URL1 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51613.png'; +// 116423, 51615, 17 +const OVERLAY_TOP_LEFT_COORDINATE2 = [35.67514743608467, 139.76531982421875]; +const OVERLAY_BOTTOM_RIGHT_COORDINATE2 = [35.67737855391474, 139.76806640625]; +const IMAGE_URL2 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51615.png'; + +export default class ImageOverlayWithBearing extends Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + overlay1: { + bounds: [ + OVERLAY_TOP_LEFT_COORDINATE1, + OVERLAY_BOTTOM_RIGHT_COORDINATE1, + ], + image: IMAGE_URL1, + }, + overlay2: { + bounds: [ + OVERLAY_TOP_LEFT_COORDINATE2, + OVERLAY_BOTTOM_RIGHT_COORDINATE2, + ], + image: IMAGE_URL2, + }, + }; + } + + render() { + return ( + + + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); diff --git a/example/src/examples/ImageOverlayWithURL.tsx b/example/src/examples/ImageOverlayWithURL.tsx new file mode 100644 index 000000000..62c778a7c --- /dev/null +++ b/example/src/examples/ImageOverlayWithURL.tsx @@ -0,0 +1,101 @@ +import React, {Component} from 'react'; +import {StyleSheet, View, Dimensions} from 'react-native'; + +import MapView, {Overlay} from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); + +const ASPECT_RATIO = width / height; +const LATITUDE = 35.679976; +const LONGITUDE = 139.768458; +const LATITUDE_DELTA = 0.01; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; +// 116423, 51613, 17 +const OVERLAY_TOP_LEFT_COORDINATE1 = [35.68184060244454, 139.76531982421875]; +const OVERLAY_BOTTOM_RIGHT_COORDINATE1 = [35.679609609368576, 139.76806640625]; +const IMAGE_URL1 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51613.png'; +// 116423, 51615, 17 +const OVERLAY_TOP_LEFT_COORDINATE2 = [35.67737855391474, 139.76531982421875]; +const OVERLAY_BOTTOM_RIGHT_COORDINATE2 = [35.67514743608467, 139.76806640625]; +const IMAGE_URL2 = 'https://maps.gsi.go.jp/xyz/std/17/116423/51615.png'; + +export default class ImageOverlayWithURL extends Component { + constructor(props: any) { + super(props); + + this.state = { + region: { + latitude: LATITUDE, + longitude: LONGITUDE, + latitudeDelta: LATITUDE_DELTA, + longitudeDelta: LONGITUDE_DELTA, + }, + overlay1: { + bounds: [ + OVERLAY_TOP_LEFT_COORDINATE1, + OVERLAY_BOTTOM_RIGHT_COORDINATE1, + ], + image: IMAGE_URL1, + }, + overlay2: { + bounds: [ + OVERLAY_TOP_LEFT_COORDINATE2, + OVERLAY_BOTTOM_RIGHT_COORDINATE2, + ], + image: IMAGE_URL2, + }, + }; + } + + render() { + return ( + + + + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'flex-end', + alignItems: 'center', + }, + map: { + ...StyleSheet.absoluteFillObject, + }, + bubble: { + backgroundColor: 'rgba(255,255,255,0.7)', + paddingHorizontal: 18, + paddingVertical: 12, + borderRadius: 20, + }, + latlng: { + width: 200, + alignItems: 'stretch', + }, + button: { + width: 80, + paddingHorizontal: 12, + alignItems: 'center', + marginHorizontal: 10, + }, + buttonContainer: { + flexDirection: 'row', + marginVertical: 20, + backgroundColor: 'transparent', + }, +}); diff --git a/example/src/examples/IndoorMap.tsx b/example/src/examples/IndoorMap.tsx new file mode 100644 index 000000000..7d6d564b5 --- /dev/null +++ b/example/src/examples/IndoorMap.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import {StyleSheet, View, Dimensions, Button, Alert} from 'react-native'; +import MapView from 'react-native-maps'; + +const {width, height} = Dimensions.get('window'); +const ASPECT_RATIO = width / height; +const LATITUDE = 1.3039991; +const LONGITUDE = 103.8316911; +const LATITUDE_DELTA = 0.003; +const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO; + +export default class IndoorMap extends React.Component { + map: any; + constructor(props: any) { + super(props); + this.setIndoorLevel = this.setIndoorLevel.bind(this); + } + + handleIndoorFocus(event: any) { + const {indoorBuilding} = event.nativeEvent; + const {defaultLevelIndex, levels} = indoorBuilding; + const levelNames = levels.map((lv: any) => lv.name || ''); + const msg = `Default Level: ${defaultLevelIndex}\nLevels: ${levelNames.toString()}`; + Alert.alert('Indoor building focused', msg); + } + + setIndoorLevel(level: any) { + this.map.setIndoorActiveLevelIndex(level); + } + + render() { + return ( + + { + this.map = map; + }} + /> +