依赖文件
@@ -0,0 +1,2 @@
|
||||
www/Promise.js
|
||||
*.spec.js
|
||||
@@ -0,0 +1,42 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5
|
||||
},
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-console": 0
|
||||
},
|
||||
"globals": {
|
||||
"require": false,
|
||||
"Symbol": false,
|
||||
"window": false,
|
||||
"cordova": false,
|
||||
"Promise": false,
|
||||
"module": false,
|
||||
"angular": false,
|
||||
"firebase": false,
|
||||
"exports": false,
|
||||
"google": false,
|
||||
"plugin": false
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
<!--
|
||||
Thank you for supporting the plugin activity ($5 usd).
|
||||
https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=SQPLZJ672HJ9N&lc=US&item_name=Cordova%2dGoogleMaps%2dPlugin&amount=5%2e00¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted
|
||||
|
||||
#--------------------
|
||||
# public support
|
||||
#--------------------
|
||||
Since this plugin project is open source, I give you free public support.
|
||||
I almost all cases ask you to share your project files on Github or other git repository services.
|
||||
I don't matter if it is public or private repo.
|
||||
However screen captures or screen records are posted in public issue thread.
|
||||
(Code are not discovered as much as possible)
|
||||
|
||||
#--------------------
|
||||
# private support
|
||||
#--------------------
|
||||
Even if you don't want to discover anything including screen captures or screen records,
|
||||
please ask me to private support.
|
||||
I ask you to donate $100 USD to the maps plugin project,
|
||||
then I make a private space, then support it.
|
||||
|
||||
--->
|
||||
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
- [ ] question
|
||||
- [ ] any problem or bug report
|
||||
- [ ] feature request
|
||||
|
||||
**If you choose 'problem or bug report', please select OS:** (check one with "x")
|
||||
- [ ] **Android**
|
||||
- [ ] **iOS**
|
||||
|
||||
**cordova information:** (run `$> cordova plugin list`)
|
||||
|
||||
```
|
||||
insert the output from the command here
|
||||
```
|
||||
|
||||
**If you use `@ionic-native/google-maps`, please tell the package.json (only `@ionic-native/core` and `@ionic-native/google-maps` are fine mostly)**
|
||||
|
||||
**Current behavior:**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior:**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Screen capture or video record:**
|
||||
<!--
|
||||
Please take a screen capture or a screen record. You can upload your files with drag & drop up to 10M bytes.
|
||||
[Android]
|
||||
$> adb shell screenrecord /sdcard/capture.mp4
|
||||
$> adb pull /sdcard/capture.mp4 ./
|
||||
[iOS]
|
||||
Use QuickTimer player on Mac
|
||||
--->
|
||||
|
||||
**Related code, data or error log (please format your code or data):**
|
||||
|
||||
```
|
||||
The person who share your project files on Github (or other git repository) is in faster lane than other people.
|
||||
Please share your project files on Github or others(Bitbucket, Gitlabs...etc).
|
||||
If you don't want to share your project files, please create a demo project, then share it.
|
||||
|
||||
Screen captures, and/or native logs(such as Logcat, xcode logs) are appreciate.
|
||||
|
||||
Giving much information, you are waiting time is less.
|
||||
Thank you for your cooperation.
|
||||
```
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this plugin
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for suggesting new feature.
|
||||
If you have a good idea that helps other people a lot, please submit your idea.
|
||||
|
||||
Although this plugin is open source, please consider to donation some amount, because "time is money".
|
||||
If you ask a small feature, such as adding some options for an existing method, this project doesn't ask any donation.
|
||||
But if you ask a big feature, such as adding "directions", "streetview",...etc, this project probably ask some amount donation.
|
||||
|
||||
So far, HTMLInfoWindow and MarkerCluster are the donated project.
|
||||
--->
|
||||
|
||||
**Please tell us your new feature idea**
|
||||
|
||||
**Why do you need it?**
|
||||
|
||||
**How helps other people?**
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Private support
|
||||
about: How to request private support?
|
||||
|
||||
---
|
||||
|
||||
This plugin is open source project, thus you can use this plugin as free!
|
||||
|
||||
In order to share the issue knowledges with other people, we would like to ask you to make converstations in open issue threads as much as possible.
|
||||
|
||||
However some people, some companies do not allow to do that, because of their NDAs or privacy policies.
|
||||
|
||||
Yes, we understand thier situations.
|
||||
|
||||
In that case, choose **"private support"**.
|
||||
If you choose "**private support**", we will make a private chat room for you, then we can discuss your issues.
|
||||
We don't discover your code or your application screen captures into public spaces.
|
||||
|
||||
### Fee
|
||||
|
||||
You can choose one of following options:
|
||||
|
||||
- only one time, ask only one issue.
|
||||
Please donate $15 USD.
|
||||
|
||||
- 1 month private support, up to 10 issues.
|
||||
Please donate $100 USD.
|
||||
|
||||
- one year private support, no cap.
|
||||
Please donate $1,000 USD.
|
||||
|
||||
|
||||
|
||||
### How to apply it?
|
||||
|
||||
Please make an issue with this template, then donate the fee amount.
|
||||
|
||||
https://github.com/mapsplugin/cordova-plugin-googlemaps#please-support-this-plugin-activity
|
||||
|
||||
You don't need to discover your information at this issue thread.
|
||||
After donation, we will contact the email address which is used for paypal payment.
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: Question or bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
*********************************************************************
|
||||
|
||||
**public support**
|
||||
|
||||
If you have a question about this plugin, please ask here.
|
||||
However this is not a good place for Q&A, please ask only things related with this plugin (such as how to use).
|
||||
|
||||
If you want to report your problem, please share your project files on Github in advance.
|
||||
Without your project files, I can only guess your issue (except your issue is really easy case).
|
||||
Sharing your project files in advance is the fastest way to resolve your issue.
|
||||
|
||||
**private support**
|
||||
|
||||
If you don't want to discover anything including code, screen captures or screen records,
|
||||
please [create an private issue ticket](https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/.github/ISSUE_TEMPLATE/private-support.md).
|
||||
I provide a private space for chatting.
|
||||
|
||||
*********************************************************************
|
||||
|
||||
--->
|
||||
|
||||
**I'm submitting a ...** (check one with "x")
|
||||
- [ ] question
|
||||
- [ ] any problem or bug report
|
||||
|
||||
**OS:** (check one with "x")
|
||||
- [ ] **Android**
|
||||
- [ ] **iOS**
|
||||
- [ ] **Browser**
|
||||
|
||||
**cordova information:** (run `$> cordova plugin list`)
|
||||
|
||||
```
|
||||
insert the output from the command here
|
||||
```
|
||||
|
||||
**If you use `@ionic-native/google-maps`, please tell the package.json (only `@ionic-native/core` and `@ionic-native/google-maps` are fine mostly)**
|
||||
```
|
||||
@ionic-native/core :
|
||||
@ionic-native/google-maps :
|
||||
```
|
||||
|
||||
**Current behavior:**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior:**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Screen capture or video record:**
|
||||
<!--
|
||||
Please take a screen capture or a screen record. You can upload your files with drag & drop up to 10M bytes. If your video file is bigger than 10MB, please upload to your youtube channel, or your Google drive.
|
||||
|
||||
[Android]
|
||||
$> adb shell screenrecord /sdcard/capture.mp4
|
||||
$> adb pull /sdcard/capture.mp4 ./
|
||||
[iOS]
|
||||
Use QuickTimer player on Mac
|
||||
--->
|
||||
|
||||
**Related code, data or error log (please format your code or data):**
|
||||
|
||||
```
|
||||
Please share your project files on Github or others(Bitbucket, Gitlabs...etc).
|
||||
If you don't want to share your project files, please create a demo project, then share it.
|
||||
|
||||
Screen captures, and/or native logs(such as Logcat, xcode logs) are appreciate.
|
||||
|
||||
Giving much information, you are waiting time is less.
|
||||
Thank you for your cooperation.
|
||||
```
|
||||
|
||||
**Support this plugin activity**
|
||||
|
||||
I appreicate if you give me a beer :beer: from [here](
|
||||
https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=SQPLZJ672HJ9N&lc=US&item_name=Cordova%2dGoogleMaps%2dPlugin&amount=5%2e00¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted)
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"js": {
|
||||
"indent_size": 2,
|
||||
"indent_char": " ",
|
||||
"indent_level": 0,
|
||||
"indent_with_tabs": false,
|
||||
"preserve_newlines": true,
|
||||
"max_preserve_newlines": 2,
|
||||
"jslint_happy": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"esversion": 5
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- NODE_VERSION="10.12.0"
|
||||
- ANDROID_API="28"
|
||||
- ANDROID_BUILD_TOOLS="28.0.3"
|
||||
- CORDOVA_VERSION="9.0.0"
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
before_cache:
|
||||
# Do not cache a few Gradle files/directories (see https://docs.travis-ci.com/user/languages/java/#Caching)
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
||||
# Gradle dependencies
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
# Android directory
|
||||
- $HOME/.android
|
||||
|
||||
# NVM
|
||||
- $HOME/.nvm
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: CORDOVA_PLATFORM="browser"
|
||||
language: node_js
|
||||
node_js: '${NODE_VERSION}'
|
||||
|
||||
- if: branch = master
|
||||
os: osx
|
||||
env: CORDOVA_PLATFORM="ios"
|
||||
language: objective-c
|
||||
cache:
|
||||
- bundler
|
||||
- cocoapods
|
||||
node_js: '${NODE_VERSION}'
|
||||
osx_image: xcode9.4
|
||||
before_script:
|
||||
- exist=$(which ios-deploy); if [[ -z "${exist}" ]]; then
|
||||
npm -g install ios-deploy > /dev/null;
|
||||
fi
|
||||
|
||||
- if: branch = master
|
||||
env: CORDOVA_PLATFORM="android"
|
||||
os: linux
|
||||
node_js: '${NODE_VERSION}'
|
||||
language: android
|
||||
jdk: oraclejdk8
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- android-${ANDROID_API}
|
||||
- platform-tools
|
||||
# The BuildTools version used by your project
|
||||
- build-tools-${ANDROID_BUILD_TOOLS}
|
||||
# The SDK version used to compile your project
|
||||
- android-${ANDROID_API}
|
||||
# Additional components
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
- sys-img-x86-android-${ANDROID_API}
|
||||
licenses:
|
||||
- 'android-sdk-license-.+'
|
||||
- '.+'
|
||||
|
||||
install:
|
||||
|
||||
- exist=$(which nvm); if [[ -z "${exist}" ]]; then
|
||||
rm -rf ~/.nvm > /dev/null;
|
||||
git clone https://github.com/creationix/nvm.git ~/.nvm > /dev/null;
|
||||
(cd ~/.nvm && git checkout `git describe --abbrev=0 --tags` > /dev/null);
|
||||
source ~/.nvm/nvm.sh && nvm > /dev/null;
|
||||
fi
|
||||
- exist=$(nvm list | grep ${NODE_VERSION}); if [[ -z "${exist}" ]]; then
|
||||
nvm install ${NODE_VERSION} > /dev/null;
|
||||
fi
|
||||
- nvm use ${NODE_VERSION} > /dev/null;
|
||||
- node --version
|
||||
- exist=$(which cordova); if [[ -z "${exist}" ]]; then
|
||||
npm -g install cordova@${CORDOVA_VERSION} > /dev/null;
|
||||
fi
|
||||
- cordova --version
|
||||
- exist=$(which cordova-paramedic); if [[ -z "${exist}" ]]; then
|
||||
npm -g install https://github.com/apache/cordova-paramedic;
|
||||
fi
|
||||
- npm i > /dev/null
|
||||
|
||||
script:
|
||||
- /bin/bash build_test.sh
|
||||
@@ -0,0 +1,206 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2014 Masashi Katsumata
|
||||
|
||||
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.
|
||||
|
||||
Additional conditions:
|
||||
As of the cordova-plugin-googlemaps version 2.2.5, following user is prohibited to use this plugin (additional fork, copy the code from this repo, anything).
|
||||
- the member of `Bemyeye Holdings limited (https://github.com/BeMyEye)`
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
# Cordova GoogleMaps plugin for Android, iOS and Browser v2.7.1
|
||||
|
||||
| Download | Build test (multiple_maps branch)|
|
||||
|----------|---------------------------|
|
||||
| [](https://npm-stat.com/charts.html?package=cordova-plugin-googlemaps) |[](https://travis-ci.org/mapsplugin/cordova-plugin-googlemaps/branches) |
|
||||
|
||||
This plugin displays Google Maps in your application.
|
||||
This plugin uses these libraries for each platforms:
|
||||
|
||||
- Android : [Google Maps Android API](https://developers.google.com/maps/documentation/android/)
|
||||
- iOS : [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios/)
|
||||
- Browser : [Google Maps JavaScript API v3](https://developers.google.com/maps/documentation/javascript/)
|
||||
|
||||
Both [PhoneGap](http://phonegap.com/) and [Apache Cordova](http://cordova.apache.org/) are supported.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<h3>Android, iOS</h3>
|
||||
<img src="https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps-doc/master/v2.6.0/hello-world/image1.gif" height="300">
|
||||
</td>
|
||||
<td>
|
||||
<h3>Browser</h3>
|
||||
<img src="https://raw.githubusercontent.com/mapsplugin/cordova-plugin-googlemaps/master/images/browser_demo.gif" height="300">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
-----
|
||||
|
||||
## Guides
|
||||
|
||||
- [How to generate API keys?](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/api_key/README.md)
|
||||
- [Hello, World](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/hello-world/README.md)
|
||||
- [Hello, World (with PhoneGap Build)](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/hello-world-phonegap-build/README.md)
|
||||
- [Trouble shootings](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/tree/master/troubleshootings/README.md)
|
||||
|
||||
## Quick install
|
||||
|
||||
```
|
||||
$> cordova plugin add cordova-plugin-googlemaps
|
||||
```
|
||||
|
||||
Then set your Google Maps API keys into your `config.xml` (Android / iOS).
|
||||
|
||||
```xml
|
||||
<widget ...>
|
||||
<preference name="GOOGLE_MAPS_ANDROID_API_KEY" value="(api key)" />
|
||||
<preference name="GOOGLE_MAPS_IOS_API_KEY" value="(api key)" />
|
||||
</widget>
|
||||
```
|
||||
|
||||
For browser platform,
|
||||
|
||||
```js
|
||||
// If your app runs this program on browser,
|
||||
// you need to set `API_KEY_FOR_BROWSER_RELEASE` and `API_KEY_FOR_BROWSER_DEBUG`
|
||||
// before `plugin.google.maps.Map.getMap()`
|
||||
//
|
||||
// API_KEY_FOR_BROWSER_RELEASE for `https:` protocol
|
||||
// API_KEY_FOR_BROWSER_DEBUG for `http:` protocol
|
||||
//
|
||||
plugin.google.maps.environment.setEnv({
|
||||
'API_KEY_FOR_BROWSER_RELEASE': '(YOUR_API_KEY_IS_HERE)',
|
||||
'API_KEY_FOR_BROWSER_DEBUG': '' // optional
|
||||
});
|
||||
|
||||
// Create a Google Maps native view under the map_canvas div.
|
||||
var map = plugin.google.maps.Map.getMap(div);
|
||||
|
||||
```
|
||||
|
||||
## PhoneGap Build settings
|
||||
|
||||
```xml
|
||||
<widget ...>
|
||||
|
||||
<!--
|
||||
You need to specify cli-7.1.0 or greater version.
|
||||
https://build.phonegap.com/current-support
|
||||
-->
|
||||
<preference name="phonegap-version" value="cli-8.1.1" />
|
||||
</widget>
|
||||
```
|
||||
|
||||
## Install optional variables (config.xml)
|
||||
|
||||
-  **PLAY_SERVICES_VERSION = (15.0.1)**<br>
|
||||
The Google Play Services SDK version.
|
||||
_You need to specify the same version number with all other plugins._
|
||||
Check out the latest version [here](https://developers.google.com/android/guides/releases).
|
||||
|
||||
-  **ANDROID_SUPPORT_V4_VERSION = (27.1.1)**<br>
|
||||
This plugin requires the Android support library v4.
|
||||
_The minimum version is 24.1.0._
|
||||
Check out the latest version [here](https://developer.android.com/topic/libraries/support-library/revisions.html).
|
||||
|
||||
-  **LOCATION_WHEN_IN_USE_DESCRIPTION**<br>
|
||||
This message is displayed when your application requests **LOCATION PERMISSION for only necessary times**.
|
||||
|
||||
-  **LOCATION_ALWAYS_USAGE_DESCRIPTION**<br>
|
||||
This message is displayed when your application requests **LOCATION PERMISSION for always**.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Please support this plugin activity.
|
||||
|
||||
In order to keep this plugin as free, please consider to donate little amount for this project.
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=SQPLZJ672HJ9N&lc=US&item_name=cordova%2dgooglemaps%2dplugin¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted)
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Release Notes
|
||||
|
||||
- **v2.7.1**
|
||||
- Fix: (iOS) UiWebView references present in v2.7.0
|
||||
|
||||
- **v2.7.0**
|
||||
- Re-adoption: <a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-sdk" target="_blank">cordova-plugin-googlemaps-sdk dependency</a>
|
||||
- Important update: No longer support `UIWebView` on iOS. `WKWebView` only.
|
||||
- Fix: (iOS) Can't load image files from local host on ionic 4 / 5
|
||||
- Update: (Android) prevent null pointer error in AsyncLoadImage.java
|
||||
- Fix: Css animation interference when call setDiv and there is a push/pop page
|
||||
- Fix: (Android/iOS/Browser) KML parser crash
|
||||
- Fix: flickering and wrong rendering of some DOM elements
|
||||
- Add: `map.stopAnimation()`
|
||||
- Fix: can't remove map while map.animateCamera() is running
|
||||
- Update: (Android) Increase cache memory size
|
||||
- Update: (Android/iOS) Danish localization
|
||||
- Fix: (Android) Prevent resize event after `map.setDiv(null)`
|
||||
- Fix: (Android/iOS) Can not interactive with the map inside <form>
|
||||
- Fix: jslint errors
|
||||
- Fix: marker.setIcon crashes
|
||||
- Update: Set default value range to heading and tilt
|
||||
- Fix: (Android/iOS) touch detection is wrong after clicking on back button very soon.
|
||||
- Fix: An error occurs when you click a marker of marker cluster #2660
|
||||
- Remove promise-7.0.4-min.js.map
|
||||
- Fix: (iOS) bug fix: App crashes if "bearing" property is "<null>"
|
||||
- Fix: HTMLColor2RGBA() converts to incorrect value
|
||||
- Fix: (Android) Can't load marker image from the Internet
|
||||
- many bug fixes...
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Demos
|
||||
|
||||
|
||||
[Demo (Browser)](https://mapsplugin.github.io/HelloGoogleMap/)
|
||||
|
||||

|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
## Documentation
|
||||
|
||||

|
||||
|
||||
[All documentations are here!!](https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md)
|
||||
|
||||
https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md
|
||||
|
||||
**Quick examples**
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/Map/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/raw/master/images/map.png?raw=true"><br>Map</a></td>
|
||||
<td><pre>
|
||||
var options = {
|
||||
camera: {
|
||||
target: {lat: ..., lng: ...},
|
||||
zoom: 19
|
||||
}
|
||||
};
|
||||
var map = plugin.google.maps.Map.getMap(mapDiv, options)</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/Marker/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/marker.png?raw=true"><br>Marker</a></td>
|
||||
<td><pre>
|
||||
var marker = map.addMarker({
|
||||
position: {lat: ..., lng: ...},
|
||||
title: "Hello Cordova Google Maps for iOS and Android",
|
||||
snippet: "This plugin is awesome!"
|
||||
})</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/MarkerCluster/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/markercluster.png?raw=true"><br>MarkerCluster</a></td>
|
||||
<td><pre>
|
||||
var markerCluster = map.addMarkerCluster({
|
||||
//maxZoomLevel: 5,
|
||||
boundsDraw: true,
|
||||
markers: dummyData(),
|
||||
icons: [
|
||||
{min: 2, max: 100, url: "./img/blue.png", anchor: {x: 16, y: 16}},
|
||||
{min: 100, max: 1000, url: "./img/yellow.png", anchor: {x: 16, y: 16}},
|
||||
{min: 1000, max: 2000, url: "./img/purple.png", anchor: {x: 24, y: 24}},
|
||||
{min: 2000, url: "./img/red.png",anchor: {x: 32,y: 32}}
|
||||
]
|
||||
});</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/HtmlInfoWindow/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/htmlInfoWindow.png?raw=true"><br>HtmlInfoWindow</a></td>
|
||||
<td><pre>
|
||||
var html = "<img src='./House-icon.png' width='64' height='64' >" +
|
||||
"<br>" +
|
||||
"This is an example";
|
||||
htmlInfoWindow.setContent(html);
|
||||
htmlInfoWindow.open(marker);
|
||||
</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/Circle/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/circle.png?raw=true"><br>Circle</a></td>
|
||||
<td><pre>
|
||||
var circle = map.addCircle({
|
||||
'center': {lat: ..., lng: ...},
|
||||
'radius': 300,
|
||||
'strokeColor' : '#AA00FF',
|
||||
'strokeWidth': 5,
|
||||
'fillColor' : '#880000'
|
||||
});</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/Polyline/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/polyline.png?raw=true"><br>Polyline</a></td>
|
||||
<td><pre>
|
||||
var polyline = map.addPolyline({
|
||||
points: AIR_PORTS,
|
||||
'color' : '#AA00FF',
|
||||
'width': 10,
|
||||
'geodesic': true
|
||||
});</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/Polygon/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/polygon.png?raw=true"><br>Polygon</a></td>
|
||||
<td><pre>
|
||||
var polygon = map.addPolygon({
|
||||
'points': GORYOKAKU_POINTS,
|
||||
'strokeColor' : '#AA00FF',
|
||||
'strokeWidth': 5,
|
||||
'fillColor' : '#880000'
|
||||
});</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/GroundOverlay/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/groundoverlay.png?raw=true"><br>GroundOverlay</a></td>
|
||||
<td><pre>
|
||||
var groundOverlay = map.addGroundOverlay({
|
||||
'url': "./newark_nj_1922.jpg",
|
||||
'bounds': [
|
||||
{"lat": 40.712216, "lng": -74.22655},
|
||||
{"lat": 40.773941, "lng": -74.12544}
|
||||
],
|
||||
'opacity': 0.5
|
||||
});
|
||||
</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/TileOverlay/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/tileoverlay.png?raw=true"><br>TileOverlay</a></td>
|
||||
<td><pre>
|
||||
var tileOverlay = map.addTileOverlay({
|
||||
debug: true,
|
||||
opacity: 0.75,
|
||||
getTile: function(x, y, zoom) {
|
||||
return "../images/map-for-free/" + zoom + "_" + x + "-" + y + ".gif"
|
||||
}
|
||||
});</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/KmlOverlay/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/multiple_maps/images/kmloverlay.png?raw=true"><br>KmlOverlay</a></td>
|
||||
<td><pre>
|
||||
map.addKmlOverlay({
|
||||
'url': 'polygon.kml'
|
||||
}, function(kmlOverlay) { ... });</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/Geocoder/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/geocoder.png?raw=true"><br>Geocoder</a></td>
|
||||
<td><pre>
|
||||
plugin.google.maps.Geocoder.geocode({
|
||||
// US Capital cities
|
||||
"address": [
|
||||
"Montgomery, AL, USA", ... "Cheyenne, Wyoming, USA"
|
||||
]
|
||||
}, function(mvcArray) { ... });</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/utilities/geometry/poly/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/poly.png?raw=true"><br>poly utility</a></td>
|
||||
<td><pre>
|
||||
var GORYOKAKU_POINTS = [
|
||||
{lat: 41.79883, lng: 140.75675},
|
||||
...
|
||||
{lat: 41.79883, lng: 140.75673}
|
||||
]
|
||||
var contain = plugin.google.maps.geometry.poly.containsLocation(
|
||||
position, GORYOKAKU_POINTS);
|
||||
marker.setIcon(contain ? "blue" : "red");
|
||||
</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/tree/master/v2.6.0/class/utilities/geometry/encoding/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/encode.png?raw=true"><br>encode utility</a></td>
|
||||
<td><pre>
|
||||
var GORYOKAKU_POINTS = [
|
||||
{lat: 41.79883, lng: 140.75675},
|
||||
...
|
||||
{lat: 41.79883, lng: 140.75673}
|
||||
]
|
||||
var encodedPath = plugin.google.maps.geometry.
|
||||
encoding.encodePath(GORYOKAKU_POINTS);
|
||||
</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/utilities/geometry/spherical/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/spherical.png?raw=true"><br>spherical utility</a></td>
|
||||
<td><pre>
|
||||
var heading = plugin.google.maps.geometry.spherical.computeHeading(
|
||||
markerA.getPosition(), markerB.getPosition());
|
||||
label.innerText = "heading : " + heading.toFixed(0) + "°";
|
||||
</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/locationservice/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/blob/master/images/locationService.png?raw=true"><br>Location service</a></td>
|
||||
<td><pre>
|
||||
plugin.google.maps.LocationService.getMyLocation(function(result) {
|
||||
alert(["Your current location:\n",
|
||||
"latitude:" + location.latLng.lat.toFixed(3),
|
||||
"longitude:" + location.latLng.lng.toFixed(3),
|
||||
"speed:" + location.speed,
|
||||
"time:" + location.time,
|
||||
"bearing:" + location.bearing].join("\n"));
|
||||
});
|
||||
</pre></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><a href="https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/class/StreetView/README.md"><img src="https://github.com/mapsplugin/cordova-plugin-googlemaps/raw/master/images/streetview.png?raw=true"><br>StreetView</a></td>
|
||||
<td><pre>
|
||||
var div = document.getElementById("pano_canvas1");
|
||||
var panorama = plugin.google.maps.StreetView.getPanorama(div, {
|
||||
camera: {
|
||||
target: {lat: 42.345573, lng: -71.098326}
|
||||
}
|
||||
});</pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
### What is the difference between this plugin and Google Maps JavaScript API v3?
|
||||
|
||||
Google Maps JavaScript API v3 works on any platforms,
|
||||
but it does not work if device is **offline**.
|
||||
|
||||
This plugin uses three different APIs:
|
||||
- Android : [Google Maps Android API](https://developers.google.com/maps/documentation/android/)
|
||||
- iOS : [Google Maps SDK for iOS](https://developers.google.com/maps/documentation/ios/)
|
||||
- Browser : [Google Maps JavaScript API v3](https://developers.google.com/maps/documentation/javascript/)
|
||||
|
||||
In Android and iOS applications, this plugin displays native Google Maps views, which is **faster** than Google Maps JavaScript API v3.
|
||||
And it even works if the device is **offline**.
|
||||
|
||||
In Browser platform, this plugin displays JS map views (Google Maps JavaScript API v3).
|
||||
It should work as PWA (progressive web application), but the device has to be **online**.
|
||||
|
||||
In order to work for all platforms, this plugin provides **own API** instead of each original APIs.
|
||||
You can write your code `similar to` the Google Maps JavaScript API v3.
|
||||
|
||||
**Feature comparison table**
|
||||
|
||||
| | Google Maps JavaScript API v3 | Cordova-Plugin-GoogleMaps(Android,iOS)| Cordova-Plugin-GoogleMaps(Browser) |
|
||||
|----------------|-----------------------------------|---------------------------------------|---------------------------------------|
|
||||
|Rendering system| JavaScript + HTML | JavaScript + Native API's | JavaScript |
|
||||
|Offline map | Not possible | Possible (only your displayed area) | Not possible |
|
||||
|3D View | Not possible | Possible | Not possible |
|
||||
|Platform | All browsers | Android and iOS applications only | All browsers |
|
||||
|Tile image | Bitmap | Vector | Bitmap |
|
||||
|
||||
**Class comparison table**
|
||||
|
||||
| Google Maps JavaScript API v3 | Cordova-Plugin-GoogleMaps |
|
||||
|-----------------------------------|---------------------------------------|
|
||||
| google.maps.Map | Map |
|
||||
| google.maps.Marker | Marker |
|
||||
| google.maps.InfoWindow | Default InfoWindow, and HtmlInfoWindow|
|
||||
| google.maps.Circle | Circle |
|
||||
| google.maps.Rectangle | Polygon |
|
||||
| google.maps.Polyline | Polyline |
|
||||
| google.maps.Polygon | Polygon |
|
||||
| google.maps.GroundOverlay | GroundOverlay |
|
||||
| google.maps.ImageMapType | TileOverlay |
|
||||
| google.maps.MVCObject | BaseClass |
|
||||
| google.maps.MVCArray | BaseArrayClass |
|
||||
| google.maps.Geocoder | plugin.google.maps.geocoder |
|
||||
| google.maps.geometry.spherical | plugin.google.maps.geometry.spherical |
|
||||
| google.maps.geometry.encoding | plugin.google.maps.geometry.encoding |
|
||||
| google.maps.geometry.poly | plugin.google.maps.geometry.poly |
|
||||
| (not available) | MarkerCluster |
|
||||
| google.maps.KmlLayer | KmlOverlay |
|
||||
| (not available) | LocationService |
|
||||
| google.maps.StreetView | StreetView :sparkles: |
|
||||
| google.maps.Data | (not available) |
|
||||
| google.maps.DirectionsService | (not available) |
|
||||
| google.maps.DistanceMatrixService | (not available) |
|
||||
| google.maps.TransitLayer | (not available) |
|
||||
| google.maps.places.* | (not available) |
|
||||
| google.maps.visualization.* | (not available) |
|
||||
|
||||
### How does this plugin work (Android, iOS)?
|
||||
|
||||
This plugin generates native map views, and puts them **under the browser**.
|
||||
|
||||
The map views are not HTML elements. This means that they are not a `<div>` or anything HTML related.
|
||||
But you can specify the size and position of the map view using its containing `<div>`.
|
||||
|
||||
This plugin changes the background to `transparent` in your application.
|
||||
Then the plugin detects your touch position, which is either meant for the `native map` or an `html element`
|
||||
(which can be on top of your map, or anywhere else on the screen).
|
||||
|
||||

|
||||
|
||||
The benefit of this plugin is the ability to automatically detect which HTML elements are over the map or not.
|
||||
|
||||
For instance, in the image below, say you tap on the header div (which is over the map view).
|
||||
The plugin will detect whether your tap is for the header div or for the map view and then pass the touch event appropriately.
|
||||
|
||||
This means **you can use the native Google Maps views similar to HTML elements**.
|
||||
|
||||

|
||||
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
## Official Communities
|
||||
|
||||
- Gitter : (managed by @Hirbod)
|
||||
|
||||
https://gitter.im/nightstomp/cordova-plugin-googlemaps
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
DO_BUILD=0
|
||||
|
||||
# If pull request, do it.
|
||||
echo "TRAVIS_EVENT_TYPE = ${TRAVIS_EVENT_TYPE}"
|
||||
if [[ ${TRAVIS_EVENT_TYPE} == "pull_request" ]]; then
|
||||
DO_BUILD=1
|
||||
fi
|
||||
|
||||
|
||||
# If commit branch is master, do the build test
|
||||
echo "TRAVIS_BRANCH = ${TRAVIS_BRANCH}"
|
||||
if [[ ${TRAVIS_BRANCH} == "master" ]]; then
|
||||
DO_BUILD=1
|
||||
fi
|
||||
|
||||
if [ ${DO_BUILD} -eq 1 ]; then
|
||||
cordova-paramedic --verbose --platform ${CORDOVA_PLATFORM} --justBuild --plugin ./tests;
|
||||
fi
|
||||
|
||||
if [[ ${CORDOVA_PLATFORM} == "browser" ]]; then
|
||||
npm run test
|
||||
fi
|
||||
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 3.4 MiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 262 KiB |
|
After Width: | Height: | Size: 205 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 122 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,22 @@
|
||||
var Resolver = require('jest-resolve');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
var pluginModuleRegex = /^cordova-plugin-googlemaps\.(.*)/;
|
||||
var pathBrowser = './src/browser';
|
||||
var pathWww = './www';
|
||||
|
||||
module.exports = function(moduleId, options) {
|
||||
if (pluginModuleRegex.test(moduleId)) {
|
||||
var fileName = moduleId.match(pluginModuleRegex)[1] + '.js';
|
||||
if (fs.existsSync(path.resolve(pathBrowser, fileName))) {
|
||||
return path.resolve(pathBrowser, fileName);
|
||||
}
|
||||
if (fs.existsSync(path.resolve(pathWww, fileName))) {
|
||||
return path.resolve(pathWww, fileName);
|
||||
}
|
||||
|
||||
throw new Error(moduleId + 'could not be located');
|
||||
}
|
||||
return Resolver.findNodeModule(moduleId, options);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
moduleNameMapper: {
|
||||
'^cordova/(.*)$': '<rootDir>/node_modules/cordova-js/src/common/$1',
|
||||
},
|
||||
resolver: './jest-cordova-resolver.js',
|
||||
globals: {
|
||||
cordova: {},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"tags": {
|
||||
"allowUnknownTags": false
|
||||
},
|
||||
"source": {
|
||||
"include": "www",
|
||||
"includePattern": ".js$",
|
||||
"excludePattern": "(node_modules/|docs|__test__)"
|
||||
},
|
||||
"opts": {
|
||||
"template": "node_modules/docdash/",
|
||||
"encoding": "utf8",
|
||||
"destination": "docs/",
|
||||
"recurse": true,
|
||||
"verbose": true
|
||||
},
|
||||
"templates": {
|
||||
"cleverLinks": false,
|
||||
"monospaceLinks": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"name": "cordova-plugin-googlemaps",
|
||||
"version": "2.7.1",
|
||||
"description": "Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.",
|
||||
"cordova": {
|
||||
"id": "cordova-plugin-googlemaps",
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios",
|
||||
"browser"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mapsplugin/cordova-plugin-googlemaps.git"
|
||||
},
|
||||
"keywords": [
|
||||
"google",
|
||||
"maps",
|
||||
"geo",
|
||||
"google maps",
|
||||
"ecosystem:cordova",
|
||||
"cordova-android",
|
||||
"cordova-ios",
|
||||
"cordova-browser"
|
||||
],
|
||||
"engines": [
|
||||
{
|
||||
"name": "cordova-android",
|
||||
"version": ">=5.1.0"
|
||||
},
|
||||
{
|
||||
"name": "cordova-ios",
|
||||
"version": ">=5.1.1"
|
||||
},
|
||||
{
|
||||
"name": "apple-xcode",
|
||||
"version": ">=10.0.0"
|
||||
},
|
||||
{
|
||||
"name": "apple-ios",
|
||||
"version": ">=10.0.0"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"test": "jest && npm run eslint",
|
||||
"jest": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"eslint": "node node_modules/eslint/bin/eslint www src"
|
||||
},
|
||||
"author": "Masashi Katsumata, Hirbod Mirjavadi",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mapsplugin/cordova-plugin-googlemaps/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mapsplugin/cordova-plugin-googlemaps",
|
||||
"devDependencies": {
|
||||
"cordova-js": "^4.2.4",
|
||||
"eslint": "^5.7.0",
|
||||
"jest": "^23.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"cordova-common": "^3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,527 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<plugin id="cordova-plugin-googlemaps" version="2.7.1" xmlns="http://apache.org/cordova/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<name>cordova-plugin-googlemaps</name>
|
||||
<js-module name="Promise" src="www/Promise.js" />
|
||||
<js-module name="BaseClass" src="www/BaseClass.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="BaseArrayClass" src="www/BaseArrayClass.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="LatLng" src="www/LatLng.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="LatLngBounds" src="www/LatLngBounds.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="VisibleRegion" src="www/VisibleRegion.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Location" src="www/Location.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="CameraPosition" src="www/CameraPosition.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Polyline" src="www/Polyline.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Polygon" src="www/Polygon.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Marker" src="www/Marker.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="HtmlInfoWindow" src="www/HtmlInfoWindow.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Circle" src="www/Circle.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="TileOverlay" src="www/TileOverlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="GroundOverlay" src="www/GroundOverlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Common" src="www/Common.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="encoding" src="www/encoding.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="spherical" src="www/spherical.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="poly" src="www/poly.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Geocoder" src="www/Geocoder.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="LocationService" src="www/LocationService.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Map" src="www/Map.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="event" src="www/event.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="MapTypeId" src="www/MapTypeId.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="KmlOverlay" src="www/KmlOverlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="KmlLoader" src="www/KmlLoader.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Environment" src="www/Environment.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="MarkerCluster" src="www/MarkerCluster.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Cluster" src="www/Cluster.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="geomodel" src="www/geomodel.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="commandQueueExecutor" src="www/commandQueueExecutor.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="pluginInit" src="www/pluginInit.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="StreetViewPanorama" src="www/StreetViewPanorama.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Overlay" src="www/Overlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="Thread" src="www/Thread.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="InlineWorker" src="www/InlineWorker.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
|
||||
<description>You can display native Google Maps views in cordova application. Native Google Maps view is faster, powerful, and more friendly for mobile applications. You can control the native Google Maps view through this plugin API.
|
||||
|
||||
This plugin works in browser application as well. One code for Android, iOS, and browsers!
|
||||
</description>
|
||||
<repo>https://github.com/mapsplugin/cordova-plugin-googlemaps.git</repo>
|
||||
<issue>https://github.com/mapsplugin/cordova-plugin-googlemaps/issues</issue>
|
||||
<author>Masashi Katsumata, Hirbod</author>
|
||||
|
||||
<license>Apache-2.0</license>
|
||||
|
||||
<keywords>google,maps,geo</keywords>
|
||||
|
||||
<engines>
|
||||
<engine name="cordova" version=">=7.0.0" />
|
||||
<engine name="cordova-ios" version=">=4.5.0" />
|
||||
<engine name="apple-xcode" version=">=9.0.0" />
|
||||
<engine name="apple-ios" version=">=10.0.0" />
|
||||
</engines>
|
||||
|
||||
<!-- android -->
|
||||
<platform name="android">
|
||||
<info>
|
||||
Official document https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md Please consider to buy a beer for us 🍺 https://github.com/mapsplugin/cordova-plugin-googlemaps#buy-us-a-beer
|
||||
</info>
|
||||
|
||||
<js-module name="googlemaps-cdv-plugin" src="www/plugin-loader-for-android_ios.js">
|
||||
<clobbers target="plugin.google.maps"/>
|
||||
</js-module>
|
||||
<js-module name="js_CordovaGoogleMaps" src="www/js_CordovaGoogleMaps-for-android_ios.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<!-- for Crosswalk -->
|
||||
<preference name="xwalkZOrderOnTop" value="true"/>
|
||||
<preference name="BackgroundColor" value="0"/>
|
||||
|
||||
<!-- for this plugin -->
|
||||
<preference name="android-minSdkVersion" value="19"/>
|
||||
</config-file>
|
||||
|
||||
<config-file target="res/xml/config.xml" parent="/*">
|
||||
<feature name="CordovaGoogleMaps">
|
||||
<param name="android-package" value="plugin.google.maps.CordovaGoogleMaps"/>
|
||||
<param name="onload" value="true"/>
|
||||
<param name="onreset" value="true"/>
|
||||
</feature>
|
||||
<feature name="PluginMap">
|
||||
<param name="android-package" value="plugin.google.maps.PluginMap"/>
|
||||
</feature>
|
||||
<feature name="PluginPolyline">
|
||||
<param name="android-package" value="plugin.google.maps.PluginPolyline"/>
|
||||
</feature>
|
||||
<feature name="PluginPolygon">
|
||||
<param name="android-package" value="plugin.google.maps.PluginPolygon"/>
|
||||
</feature>
|
||||
<feature name="PluginMarker">
|
||||
<param name="android-package" value="plugin.google.maps.PluginMarker"/>
|
||||
</feature>
|
||||
<feature name="PluginCircle">
|
||||
<param name="android-package" value="plugin.google.maps.PluginCircle"/>
|
||||
</feature>
|
||||
<feature name="PluginTileOverlay">
|
||||
<param name="android-package" value="plugin.google.maps.PluginTileOverlay"/>
|
||||
</feature>
|
||||
<feature name="PluginGroundOverlay">
|
||||
<param name="android-package" value="plugin.google.maps.PluginGroundOverlay"/>
|
||||
</feature>
|
||||
<feature name="PluginKmlOverlay">
|
||||
<param name="android-package" value="plugin.google.maps.PluginKmlOverlay"/>
|
||||
</feature>
|
||||
<feature name="PluginGeocoder">
|
||||
<param name="android-package" value="plugin.google.maps.PluginGeocoder"/>
|
||||
</feature>
|
||||
<feature name="PluginLocationService">
|
||||
<param name="android-package" value="plugin.google.maps.PluginLocationService"/>
|
||||
</feature>
|
||||
<feature name="PluginEnvironment">
|
||||
<param name="android-package" value="plugin.google.maps.PluginEnvironment"/>
|
||||
</feature>
|
||||
<feature name="PluginMarkerCluster">
|
||||
<param name="android-package" value="plugin.google.maps.PluginMarkerCluster"/>
|
||||
</feature>
|
||||
<feature name="PluginStreetViewPanorama">
|
||||
<param name="android-package" value="plugin.google.maps.PluginStreetViewPanorama"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/plugin/google/maps/AsyncLoadImage.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/AsyncLoadImageInterface.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginEnvironment.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/CordovaGoogleMaps.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/ObjectCache.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/MyPlugin.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/MyPluginLayout.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/MyPluginInterface.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginCircle.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginGeocoder.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginLocationService.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginGroundOverlay.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginKmlOverlay.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginMap.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginMarker.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginPolygon.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginPolyline.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginTileOverlay.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginTileProvider.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginUtil.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginAsyncInterface.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/BitmapCache.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginMarkerCluster.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/PluginStreetViewPanorama.java" target-dir="src/plugin/google/maps"/>
|
||||
<source-file src="src/android/plugin/google/maps/IPluginView.java" target-dir="src/plugin/google/maps"/>
|
||||
|
||||
<framework src="src/android/frameworks/tbxml-android.gradle" custom="true" type="gradleReference"/>
|
||||
<resource-file src="src/android/frameworks/tbxml-android.aar" target="libs/tbxml-android.aar"/>
|
||||
<resource-file src="src/android/frameworks/tbxml-android.aar" target="app/src/libs/tbxml-android.aar"/>
|
||||
|
||||
|
||||
<framework src="src/android/frameworks/pgm-custom.gradle" custom="true" type="gradleReference"/>
|
||||
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest/application">
|
||||
<meta-data android:name="com.google.android.geo.API_KEY" android:value="${GOOGLE_MAPS_ANDROID_API_KEY}"/>
|
||||
<!-- Google Play Services SDK -->
|
||||
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
|
||||
<!-- for Android 9 or above -->
|
||||
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||
</config-file>
|
||||
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<!-- The following two permissions are not required to use Google Maps Android API v2, but are recommended. -->
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<!-- OpenGL ES version 2 -->
|
||||
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
|
||||
<!-- GPS -->
|
||||
<uses-feature android:name="android.hardware.location"/>
|
||||
<uses-feature android:name="android.hardware.location.gps"/>
|
||||
</config-file>
|
||||
|
||||
<resource-file src="src/android/res/layout/dummy_infowindow.xml" target="res/layout/dummy_infowindow.xml"/>
|
||||
<resource-file src="src/android/res/drawable/dummy_my_location_button.png" target="res/drawable/dummy_my_location_button.png"/>
|
||||
<resource-file src="src/android/res/drawable/dummy_mylocation_button_shadow.xml" target="res/drawable/dummy_mylocation_button_shadow.xml"/>
|
||||
|
||||
<!-- Base messages -->
|
||||
<resource-file src="src/android/res/values/pgm_strings.xml" target="res/values/pgm_strings.xml"/>
|
||||
<!-- localized messages for Spanish -->
|
||||
<resource-file src="src/android/res/values-es/pgm_strings.xml" target="res/values-es/pgm_strings.xml"/>
|
||||
<!-- localized messages for Japanese -->
|
||||
<resource-file src="src/android/res/values-ja/pgm_strings.xml" target="res/values-ja/pgm_strings.xml"/>
|
||||
<!-- localized messages for Russian -->
|
||||
<resource-file src="src/android/res/values-ru/pgm_strings.xml" target="res/values-ru/pgm_strings.xml"/>
|
||||
<!-- localized messages for Ukrainian -->
|
||||
<resource-file src="src/android/res/values-uk/pgm_strings.xml" target="res/values-uk/pgm_strings.xml"/>
|
||||
<!-- localized messages for Portuguese -->
|
||||
<resource-file src="src/android/res/values-pt-rPT/pgm_strings.xml" target="res/values-pt-rPT/pgm_strings.xml"/>
|
||||
<!-- localized messages for German -->
|
||||
<resource-file src="src/android/res/values-de/pgm_strings.xml" target="res/values-de/pgm_strings.xml"/>
|
||||
<!-- localized messages for French -->
|
||||
<resource-file src="src/android/res/values-fr/pgm_strings.xml" target="res/values-fr/pgm_strings.xml"/>
|
||||
<!-- localized messages for Hindi -->
|
||||
<resource-file src="src/android/res/values-hi/pgm_strings.xml" target="res/values-hi/pgm_strings.xml"/>
|
||||
<!-- localized messages for Arabic -->
|
||||
<resource-file src="src/android/res/values-ar/pgm_strings.xml" target="res/values-ar/pgm_strings.xml"/>
|
||||
<!-- localized messages for Dutch -->
|
||||
<resource-file src="src/android/res/values-nl/pgm_strings.xml" target="res/values-nl/pgm_strings.xml"/>
|
||||
<!-- localized messages for Polish -->
|
||||
<resource-file src="src/android/res/values-pl/pgm_strings.xml" target="res/values-pl/pgm_strings.xml"/>
|
||||
<!-- localized messages for Denmark -->
|
||||
<resource-file src="src/android/res/values-da/pgm_strings.xml" target="res/values-da/pgm_strings.xml"/>
|
||||
<!-- localized messages for Vietnamese -->
|
||||
<resource-file src="src/android/res/values-vi/pgm_strings.xml" target="res/values-vi/pgm_strings.xml"/>
|
||||
</platform>
|
||||
|
||||
<!-- ios -->
|
||||
<platform name="ios">
|
||||
|
||||
<preference name="LOCATION_WHEN_IN_USE_DESCRIPTION" default="This app wants to get your location while this app runs only."/>
|
||||
<preference name="LOCATION_ALWAYS_USAGE_DESCRIPTION" default="This app wants to get your location always, even this app runs in background."/>
|
||||
|
||||
<js-module name="googlemaps-cdv-plugin" src="www/plugin-loader-for-android_ios.js">
|
||||
<clobbers target="plugin.google.maps"/>
|
||||
</js-module>
|
||||
<js-module name="js_CordovaGoogleMaps" src="www/js_CordovaGoogleMaps-for-android_ios.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
|
||||
<info>
|
||||
Official document https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md Please consider to buy a beer for us 🍺 https://github.com/mapsplugin/cordova-plugin-googlemaps#buy-us-a-beer
|
||||
</info>
|
||||
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="CordovaGoogleMaps">
|
||||
<param name="ios-package" value="CordovaGoogleMaps"/>
|
||||
<param name="onload" value="true"/>
|
||||
<param name="onreset" value="true"/>
|
||||
</feature>
|
||||
<feature name="PluginEnvironment">
|
||||
<param name="ios-package" value="PluginEnvironment"/>
|
||||
</feature>
|
||||
<feature name="PluginGeocoder">
|
||||
<param name="ios-package" value="PluginGeocoder"/>
|
||||
</feature>
|
||||
<feature name="PluginLocationService">
|
||||
<param name="ios-package" value="PluginLocationService"/>
|
||||
</feature>
|
||||
<feature name="PluginMap">
|
||||
<param name="ios-package" value="PluginMap"/>
|
||||
</feature>
|
||||
<feature name="PluginCircle">
|
||||
<param name="ios-package" value="PluginCircle"/>
|
||||
</feature>
|
||||
<feature name="PluginGroundOverlay">
|
||||
<param name="ios-package" value="PluginGroundOverlay"/>
|
||||
</feature>
|
||||
<feature name="PluginKmlOverlay">
|
||||
<param name="ios-package" value="PluginKmlOverlay"/>
|
||||
</feature>
|
||||
<feature name="PluginMarker">
|
||||
<param name="ios-package" value="PluginMarker"/>
|
||||
</feature>
|
||||
<feature name="PluginPolygon">
|
||||
<param name="ios-package" value="PluginPolygon"/>
|
||||
</feature>
|
||||
<feature name="PluginPolyline">
|
||||
<param name="ios-package" value="PluginPolyline"/>
|
||||
</feature>
|
||||
<feature name="PluginTileOverlay">
|
||||
<param name="ios-package" value="PluginTileOverlay"/>
|
||||
</feature>
|
||||
<feature name="PluginMarkerCluster">
|
||||
<param name="ios-package" value="PluginMarkerCluster"/>
|
||||
</feature>
|
||||
<feature name="PluginStreetViewPanorama">
|
||||
<param name="ios-package" value="PluginStreetViewPanorama"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<header-file src="src/ios/GoogleMaps/PluginObjects.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginObjects.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginCircle.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginCircle.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginGeocoder.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginGeocoder.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginLocationService.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginLocationService.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginEnvironment.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginEnvironment.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/CordovaGoogleMaps.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/CordovaGoogleMaps.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginMapViewController.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginMapViewController.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginGroundOverlay.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginGroundOverlay.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginKmlOverlay.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginKmlOverlay.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginMap.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginMap.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginMarker.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginMarker.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginUtil.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginUtil.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginPolygon.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginPolygon.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginPolyline.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginPolyline.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginTileProvider.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginTileProvider.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/TBXML.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/TBXML.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginTileOverlay.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginTileOverlay.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/MFGoogleMapAdditions/GMSCoordinateBounds+Geometry.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/MyPluginLayer.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/MyPluginLayer.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/MyPluginScrollView.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/MyPluginScrollView.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginMarkerCluster.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginMarkerCluster.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/UIImageCache.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/UIImageCache.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginStreetViewPanorama.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginStreetViewPanorama.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginStreetViewPanoramaController.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginStreetViewPanoramaController.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/PluginViewController.h"/>
|
||||
<source-file src="src/ios/GoogleMaps/PluginViewController.m"/>
|
||||
<header-file src="src/ios/GoogleMaps/IPluginView.h"/>
|
||||
<header-file src="src/ios/GoogleMaps/IPluginProtocol.h"/>
|
||||
|
||||
<!-- Base messages -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_en.json" target="pgm_Localizable_en.json"/>
|
||||
<!-- localized messages for Spanish -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_es.json" target="pgm_Localizable_es.json"/>
|
||||
<!-- localized messages for Japanese -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_ja.json" target="pgm_Localizable_ja.json"/>
|
||||
<!-- localized messages for Russian -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_ru.json" target="pgm_Localizable_ru.json"/>
|
||||
<!-- localized messages for Ukrainian -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_uk.json" target="pgm_Localizable_uk.json"/>
|
||||
<!-- localized messages for Brazilian Portuguese -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_pt-BR.json" target="pgm_Localizable_pt-BR.json"/>
|
||||
<!-- localized messages for German -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_de.json" target="pgm_Localizable_de.json"/>
|
||||
<!-- localized messages for French -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_fr.json" target="pgm_Localizable_fr.json"/>
|
||||
<!-- localized messages for Hindi -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_hi.json" target="pgm_Localizable_hi.json"/>
|
||||
<!-- localized messages for Arabic -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_ar.json" target="pgm_Localizable_ar.json"/>
|
||||
<!-- localized messages for Dutch -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_nl.json" target="pgm_Localizable_nl.json"/>
|
||||
<!-- localized messages for Polish -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_pl.json" target="pgm_Localizable_pl.json"/>
|
||||
<!-- localized messages for Denmark -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_da.json" target="pgm_Localizable_da.json"/>
|
||||
<!-- localized messages for Vietnamese -->
|
||||
<resource-file src="src/ios/strings/pgm_Localizable_vi.json" target="pgm_Localizable_vi.json"/>
|
||||
|
||||
<config-file target="*-Info.plist" parent="NSLocationWhenInUseUsageDescription">
|
||||
<string>$LOCATION_WHEN_IN_USE_DESCRIPTION</string>
|
||||
</config-file>
|
||||
|
||||
<config-file target="*-Info.plist" parent="NSLocationAlwaysUsageDescription">
|
||||
<string>$LOCATION_ALWAYS_USAGE_DESCRIPTION</string>
|
||||
</config-file>
|
||||
|
||||
<config-file target="*-Info.plist" parent="LSApplicationQueriesSchemes">
|
||||
<array>
|
||||
<string>googlechromes</string>
|
||||
<string>comgooglemaps</string>
|
||||
</array>
|
||||
</config-file>
|
||||
|
||||
<config-file target="*-Info.plist" parent="CFBundleURLTypes">
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>$PACKAGE_NAME</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>$PACKAGE_NAME</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</config-file>
|
||||
|
||||
<dependency id="com.googlemaps.ios" url="https://github.com/mapsplugin/cordova-plugin-googlemaps-sdk" />
|
||||
|
||||
<!--
|
||||
<hook type="before_platform_add" src="src/ios/check_sdk_version.js"/>
|
||||
<hook type="before_plugin_install" src="src/ios/check_sdk_version.js"/>
|
||||
-->
|
||||
</platform>
|
||||
|
||||
<!-- browser -->
|
||||
<platform name="browser">
|
||||
<info>
|
||||
Official document https://github.com/mapsplugin/cordova-plugin-googlemaps-doc/blob/master/v2.6.0/README.md Please consider to buy a beer for us 🍺 https://github.com/mapsplugin/cordova-plugin-googlemaps#buy-us-a-beer
|
||||
</info>
|
||||
|
||||
<js-module name="googlemaps-cdv-plugin" src="www/plugin-loader-for-browser.js">
|
||||
<clobbers target="plugin.google.maps"/>
|
||||
</js-module>
|
||||
<js-module name="js_CordovaGoogleMaps" src="www/js_CordovaGoogleMaps-for-browser.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="CordovaGoogleMaps" src="src/browser/CordovaGoogleMaps.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginMap" src="src/browser/PluginMap.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginMarker" src="src/browser/PluginMarker.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginCircle" src="src/browser/PluginCircle.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginGroundOverlay" src="src/browser/PluginGroundOverlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginKmlOverlay" src="src/browser/PluginKmlOverlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginMarkerCluster" src="src/browser/PluginMarkerCluster.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginPolygon" src="src/browser/PluginPolygon.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginPolyline" src="src/browser/PluginPolyline.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginStreetViewPanorama" src="src/browser/PluginStreetViewPanorama.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginTileOverlay" src="src/browser/PluginTileOverlay.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginLocationService" src="src/browser/PluginLocationService.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginGeocoder" src="src/browser/PluginGeocoder.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
<js-module name="PluginEnvironment" src="src/browser/PluginEnvironment.js">
|
||||
<runs/>
|
||||
</js-module>
|
||||
</platform>
|
||||
|
||||
</plugin>
|
||||
@@ -0,0 +1,9 @@
|
||||
# Pull request guide
|
||||
|
||||
Thank you for considering to improve this cordova-plugin-googlemaps.
|
||||
|
||||
When you create a pull request, please make it to **multiple_maps** branch instead of master branch.
|
||||
|
||||
Because **the multiple_maps branch is edge version**.
|
||||
|
||||
Thank you for your understanding.
|
||||
@@ -0,0 +1,106 @@
|
||||
def searchFileInParents(target) {
|
||||
def searchPath = target
|
||||
for (int i = 0; i < 8; i++) {
|
||||
println(searchPath)
|
||||
def targetFile = file(searchPath)
|
||||
if(!targetFile.canRead()) {
|
||||
searchPath = '../' + searchPath;
|
||||
} else {
|
||||
return targetFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
android {
|
||||
// #1. Find (project)/config.xml file,
|
||||
// because ionic capacitor does not copy <preference> nodes
|
||||
def configXmlFile = searchFileInParents("config.xml")
|
||||
|
||||
if (configXmlFile == null) {
|
||||
// #2. Find (project)/app/src/main/res/xml/config.xml file,
|
||||
// cordova should copy <preference> to this file
|
||||
configXmlFile = searchFileInParents("app/src/main/res/xml/config.xml")
|
||||
}
|
||||
|
||||
if (configXmlFile == null) {
|
||||
throw new GradleException("Can not find config.xml file.")
|
||||
}
|
||||
|
||||
// parse xml file
|
||||
def widget = new XmlParser().parse(configXmlFile)
|
||||
if (widget.preference == null) {
|
||||
throw new GradleException("widget is null.")
|
||||
}
|
||||
if (widget.preference.size() == 0) {
|
||||
throw new GradleException("Please add '<preference name=\"GOOGLE_MAPS_ANDROID_API_KEY\" />' to the config.xml file.")
|
||||
}
|
||||
println("preference = " + widget.preference.size())
|
||||
Properties props = new Properties()
|
||||
def isKeyFound = 0
|
||||
def useBetaSdk = 0
|
||||
props.setProperty("GOOGLE_MAPS_PLAY_SERVICES_VERSION", "16.0.0");
|
||||
props.setProperty("ANDROID_SUPPORT_V4_VERSION", "27.1.1");
|
||||
props.setProperty("GOOGLE_MAPS_ANDROID_SDK", "");
|
||||
|
||||
widget.preference.each { pref ->
|
||||
def name = pref.attributes().name
|
||||
def value = pref.attributes().value
|
||||
println("name = " + name);
|
||||
if (name == "GOOGLE_MAPS_ANDROID_API_KEY") {
|
||||
isKeyFound = 1
|
||||
props.setProperty(name, value)
|
||||
}
|
||||
if (name == "GOOGLE_MAPS_PLAY_SERVICES_VERSION" ||
|
||||
name == "ANDROID_SUPPORT_V4_VERSION") {
|
||||
props.setProperty(name, value)
|
||||
}
|
||||
if (name == "GOOGLE_MAPS_ANDROID_SDK") {
|
||||
useBetaSdk = 1
|
||||
props.setProperty("GOOGLE_MAPS_ANDROID_SDK", value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isKeyFound == 0) {
|
||||
throw new GradleException("Please add '<preference name=\"GOOGLE_MAPS_ANDROID_API_KEY\" />' to the config.xml file.")
|
||||
}
|
||||
|
||||
if (useBetaSdk == 1) {
|
||||
def libsDirPath = System.getProperty("user.dir")
|
||||
def libsDir = new File("${libsDirPath}/libs/")
|
||||
if (!libsDir.exists()) {
|
||||
libsDir.mkdirs()
|
||||
}
|
||||
|
||||
def mapsSDK = props.getProperty("GOOGLE_MAPS_ANDROID_SDK")
|
||||
def f = new File("${libsDir.absolutePath}/${mapsSDK}.aar")
|
||||
println("---->lib = " + f.absolutePath);
|
||||
if (!f.exists()) {
|
||||
new URL("https://dl.google.com/dl/geosdk/${mapsSDK}.aar").withInputStream{ i -> f.withOutputStream{ it << i }}
|
||||
}
|
||||
dependencies {
|
||||
implementation name: mapsSDK, ext:'aar'
|
||||
}
|
||||
}
|
||||
|
||||
def PLAY_SERVICES_VERSION = props.get("GOOGLE_MAPS_PLAY_SERVICES_VERSION")
|
||||
def ANDROID_SUPPORT_V4_VERSION = props.get("ANDROID_SUPPORT_V4_VERSION")
|
||||
println("---->PLAY_SERVICES_VERSION = ${PLAY_SERVICES_VERSION}")
|
||||
println("---->ANDROID_SUPPORT_V4_VERSION = ${ANDROID_SUPPORT_V4_VERSION}")
|
||||
dependencies {
|
||||
implementation "com.google.android.gms:play-services-maps:${PLAY_SERVICES_VERSION}"
|
||||
implementation "com.google.android.gms:play-services-location:${PLAY_SERVICES_VERSION}"
|
||||
implementation "com.android.support:support-core-utils:${ANDROID_SUPPORT_V4_VERSION}"
|
||||
}
|
||||
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
manifestPlaceholders.GOOGLE_MAPS_ANDROID_API_KEY = props.getProperty("GOOGLE_MAPS_ANDROID_API_KEY")
|
||||
}
|
||||
release {
|
||||
manifestPlaceholders.GOOGLE_MAPS_ANDROID_API_KEY = props.getProperty("GOOGLE_MAPS_ANDROID_API_KEY")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
repositories {
|
||||
maven { url 'https://maven.google.com' }
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
def libsDirPath = System.getProperty("user.dir")
|
||||
flatDir{
|
||||
dirs 'src/main/libs', 'libs', "${libsDirPath}/libs"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(name:'tbxml-android', ext:'aar')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
|
||||
packagingOptions {
|
||||
exclude 'README'
|
||||
exclude 'LICENSE'
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class AsyncLoadImage extends AsyncTask<Void, Void, AsyncLoadImage.AsyncLoadImageResult> {
|
||||
private AsyncLoadImageInterface callback;
|
||||
private float density = Resources.getSystem().getDisplayMetrics().density;
|
||||
private AsyncLoadImageOptions mOptions;
|
||||
private String userAgent;
|
||||
private String currentPageUrl;
|
||||
|
||||
// Get max available VM memory, exceeding this amount will throw an
|
||||
// OutOfMemory exception.
|
||||
static int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
// Use 1/8th of the available memory for this memory cache.
|
||||
public static BitmapCache mIconCache = new BitmapCache(maxMemory / 8);
|
||||
|
||||
private final String TAG = "AsyncLoadImage";
|
||||
private CordovaWebView webView;
|
||||
private CordovaInterface cordova;
|
||||
|
||||
public static class AsyncLoadImageOptions {
|
||||
String url;
|
||||
int width;
|
||||
int height;
|
||||
boolean noCaching;
|
||||
}
|
||||
|
||||
public static class AsyncLoadImageResult {
|
||||
Bitmap image;
|
||||
boolean cacheHit;
|
||||
String cacheKey;
|
||||
}
|
||||
|
||||
public AsyncLoadImage(CordovaInterface cordova, CordovaWebView webView, AsyncLoadImageOptions options, AsyncLoadImageInterface callback) {
|
||||
this.callback = callback;
|
||||
this.mOptions = options;
|
||||
this.webView = webView;
|
||||
this.cordova = cordova;
|
||||
}
|
||||
|
||||
public static String getCacheKey(String url, int width, int height) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getCacheKey(new URL(url), width, height);
|
||||
} catch (MalformedURLException e) {
|
||||
return url.hashCode() + "/" + width + "x" + height;
|
||||
}
|
||||
}
|
||||
public static String getCacheKey(URL url, int width, int height) {
|
||||
return url.hashCode() + "/" + width + "x" + height;
|
||||
}
|
||||
|
||||
public static void addBitmapToMemoryCache(String key, Bitmap image) {
|
||||
if (getBitmapFromMemCache(key) == null) {
|
||||
mIconCache.put(key, image.copy(image.getConfig(), true));
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeBitmapFromMemCahce(String key) {
|
||||
Bitmap image = mIconCache.remove(key);
|
||||
if (image == null || image.isRecycled()) {
|
||||
return;
|
||||
}
|
||||
image.recycle();
|
||||
}
|
||||
|
||||
public static Bitmap getBitmapFromMemCache(String key) {
|
||||
Bitmap image = mIconCache.get(key);
|
||||
if (image == null || image.isRecycled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return image.copy(image.getConfig(), true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCancelled(AsyncLoadImageResult result) {
|
||||
super.onCancelled(result);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.image.isRecycled()) {
|
||||
result.image.recycle();
|
||||
}
|
||||
result.image = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// If icon url contains "cdvfile://", convert to physical path.
|
||||
//----------------------------------------------------------------
|
||||
if (mOptions.url.indexOf("cdvfile://") == 0) {
|
||||
CordovaResourceApi resourceApi = webView.getResourceApi();
|
||||
mOptions.url = PluginUtil.getAbsolutePathFromCDVFilePath(resourceApi, mOptions.url);
|
||||
}
|
||||
|
||||
String currentPage = webView.getUrl();
|
||||
if (currentPage == null) {
|
||||
// Maybe someone close the map page.
|
||||
this.cancel(true);
|
||||
return;
|
||||
}
|
||||
currentPage = currentPage.replaceAll("#.*$", "");
|
||||
currentPage = currentPage.replaceAll("\\?.*$", "");
|
||||
currentPage = currentPage.replaceAll("[^\\/]*$", "");
|
||||
this.currentPageUrl = currentPage;
|
||||
|
||||
//Log.d(TAG, "-->currentPageUrl = " + this.currentPageUrl);
|
||||
|
||||
//View browserView = webView.getView();
|
||||
//String browserViewName = browserView.getClass().getName();
|
||||
this.userAgent = "Mozilla";
|
||||
|
||||
/*
|
||||
if("org.xwalk.core.XWalkView".equals(browserViewName) ||
|
||||
"org.crosswalk.engine.XWalkCordovaView".equals(browserViewName)) {
|
||||
|
||||
CordovaPreferences preferences = webView.getPreferences();
|
||||
// Set xwalk webview settings by Cordova preferences.
|
||||
String xwalkUserAgent = preferences == null ? "" : preferences.getString("xwalkUserAgent", "");
|
||||
if (!xwalkUserAgent.isEmpty()) {
|
||||
this.userAgent = xwalkUserAgent;
|
||||
}
|
||||
|
||||
String appendUserAgent = preferences.getString("AppendUserAgent", "");
|
||||
if (!appendUserAgent.isEmpty()) {
|
||||
this.userAgent = this.userAgent + " " + appendUserAgent;
|
||||
}
|
||||
if ("".equals(this.userAgent)) {
|
||||
this.userAgent = "Mozilla";
|
||||
}
|
||||
} else {
|
||||
this.userAgent = ((WebView) webView.getEngine().getView()).getSettings().getUserAgentString();
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
protected AsyncLoadImageResult doInBackground(Void... params) {
|
||||
|
||||
int mWidth = mOptions.width;
|
||||
int mHeight = mOptions.height;
|
||||
String iconUrl = mOptions.url;
|
||||
String orgIconUrl = iconUrl;
|
||||
Bitmap image = null;
|
||||
|
||||
if (iconUrl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cacheKey = null;
|
||||
cacheKey = getCacheKey(orgIconUrl, mWidth, mHeight);
|
||||
|
||||
image = getBitmapFromMemCache(cacheKey);
|
||||
if (image != null) {
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = image;
|
||||
result.cacheHit = true;
|
||||
result.cacheKey = cacheKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Log.d(TAG, String.format("---->iconURL = %s", iconUrl));
|
||||
//--------------------------------
|
||||
// Load image from local path
|
||||
//--------------------------------
|
||||
if (!iconUrl.startsWith("data:image")) {
|
||||
|
||||
if (iconUrl.startsWith("http://localhost") ||
|
||||
iconUrl.startsWith("http://127.0.0.1")) {
|
||||
// Log.d(TAG, String.format("---->(201)iconURL = %s", iconUrl));
|
||||
if (iconUrl.contains("://")) {
|
||||
iconUrl = iconUrl.replaceAll("http://.+?/", "file:///android_asset/www/");
|
||||
} else {
|
||||
// Avoid WebViewLocalServer (because can not make a connection for some reason)
|
||||
iconUrl = "file:///android_asset/www/".concat(iconUrl);
|
||||
}
|
||||
}
|
||||
|
||||
if (!iconUrl.contains("://") &&
|
||||
!iconUrl.startsWith("/") &&
|
||||
!iconUrl.startsWith("www/") &&
|
||||
!iconUrl.startsWith("data:image") &&
|
||||
!iconUrl.startsWith("./") &&
|
||||
!iconUrl.startsWith("../")) {
|
||||
iconUrl = "./" + iconUrl;
|
||||
//Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
}
|
||||
|
||||
if (iconUrl.startsWith("./") || iconUrl.startsWith("../")) {
|
||||
iconUrl = iconUrl.replace("(\\.\\/)+", "./");
|
||||
String currentPage = this.currentPageUrl;
|
||||
currentPage = currentPage.replaceAll("[^\\/]*$", "");
|
||||
currentPage = currentPage.replaceAll("#.*$", "");
|
||||
currentPage = currentPage.replaceAll("\\/[^\\/]+\\.[^\\/]+$", "");
|
||||
if (!currentPage.endsWith("/")) {
|
||||
currentPage = currentPage + "/";
|
||||
}
|
||||
iconUrl = currentPage + iconUrl;
|
||||
iconUrl = iconUrl.replaceAll("(\\/\\.\\/+)+", "/");
|
||||
//Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
}
|
||||
|
||||
if (iconUrl.indexOf("file://") == 0 &&
|
||||
!iconUrl.contains("file:///android_asset/")) {
|
||||
iconUrl = iconUrl.replace("file://", "");
|
||||
} else {
|
||||
//Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
|
||||
// if (iconUrl.indexOf("file:///android_asset/") == 0) {
|
||||
// iconUrl = iconUrl.replace("file:///android_asset/", "");
|
||||
// }
|
||||
|
||||
//Log.d(TAG, "iconUrl(222) = " + iconUrl);
|
||||
if (iconUrl.contains("./")) {
|
||||
try {
|
||||
boolean isAbsolutePath = iconUrl.startsWith("/");
|
||||
File relativePath = new File(iconUrl);
|
||||
iconUrl = relativePath.getCanonicalPath();
|
||||
//Log.d(TAG, "iconUrl = " + iconUrl);
|
||||
if (!isAbsolutePath) {
|
||||
iconUrl = iconUrl.substring(1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cacheKey = getCacheKey(iconUrl, mWidth, mHeight);
|
||||
|
||||
image = getBitmapFromMemCache(cacheKey);
|
||||
if (image != null) {
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = image;
|
||||
result.cacheHit = true;
|
||||
result.cacheKey = cacheKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
cacheKey = getCacheKey(orgIconUrl, mWidth, mHeight);
|
||||
|
||||
if (iconUrl.indexOf("http") == 0) {
|
||||
//--------------------------------
|
||||
// Load image from the Internet
|
||||
//--------------------------------
|
||||
try {
|
||||
URL url = new URL(iconUrl);
|
||||
|
||||
boolean redirect = true;
|
||||
HttpURLConnection http = null;
|
||||
String cookies = null;
|
||||
int redirectCnt = 0;
|
||||
while(redirect && redirectCnt < 10) {
|
||||
redirect = false;
|
||||
http = (HttpURLConnection)url.openConnection();
|
||||
http.setRequestMethod("GET");
|
||||
if (cookies != null) {
|
||||
http.setRequestProperty("Cookie", cookies);
|
||||
}
|
||||
http.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
|
||||
http.addRequestProperty("User-Agent", this.userAgent);
|
||||
http.setInstanceFollowRedirects(true);
|
||||
HttpURLConnection.setFollowRedirects(true);
|
||||
|
||||
// normally, 3xx is redirect
|
||||
try {
|
||||
int status = http.getResponseCode();
|
||||
if (status != HttpURLConnection.HTTP_OK) {
|
||||
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|
||||
|| status == HttpURLConnection.HTTP_MOVED_PERM
|
||||
|| status == HttpURLConnection.HTTP_SEE_OTHER)
|
||||
redirect = true;
|
||||
}
|
||||
if (redirect) {
|
||||
// get redirect url from "location" header field
|
||||
url = new URL(http.getHeaderField("Location"));
|
||||
|
||||
// get the cookie if need, for login
|
||||
cookies = http.getHeaderField("Set-Cookie");
|
||||
|
||||
// Disconnect the current connection
|
||||
http.disconnect();
|
||||
redirectCnt++;
|
||||
continue;
|
||||
}
|
||||
if (status == HttpURLConnection.HTTP_OK) {
|
||||
break;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "can not connect to " + iconUrl, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Bitmap myBitmap = null;
|
||||
InputStream inputStream = http.getInputStream();
|
||||
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int nRead;
|
||||
byte[] data = new byte[16384];
|
||||
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, nRead);
|
||||
}
|
||||
buffer.flush();
|
||||
inputStream.close();
|
||||
byte[] imageBytes = buffer.toByteArray();
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
options.inPreferredConfig = Config.ARGB_8888;
|
||||
|
||||
// The below line just checking the bitmap size (width,height).
|
||||
// Returned value is always null.
|
||||
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options);
|
||||
|
||||
if (mWidth < 1 && mHeight < 1) {
|
||||
mWidth = options.outWidth;
|
||||
mHeight = options.outHeight;
|
||||
}
|
||||
|
||||
// Resize
|
||||
int newWidth = (int)(mWidth * density);
|
||||
int newHeight = (int)(mHeight * density);
|
||||
if (newWidth > 2000 || newHeight > 2000) {
|
||||
float rationResize;
|
||||
if (newWidth >= newHeight) {
|
||||
rationResize = 2000.0f / ((float) newWidth);
|
||||
} else {
|
||||
rationResize = 2000.0f / ((float) newHeight);
|
||||
}
|
||||
newWidth = (int)(((float)newWidth) * rationResize);
|
||||
newHeight = (int)(((float)newHeight) * rationResize);
|
||||
Log.w(TAG, "Since the image size is too large, the image size resizes down mandatory");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636
|
||||
*/
|
||||
Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
|
||||
|
||||
float ratioX = newWidth / (float) options.outWidth;
|
||||
float ratioY = newHeight / (float) options.outHeight;
|
||||
float middleX = newWidth / 2.0f;
|
||||
float middleY = newHeight / 2.0f;
|
||||
|
||||
options.inJustDecodeBounds = false;
|
||||
//options.inSampleSize = (int) Math.max(ratioX, ratioY);
|
||||
options.outWidth = newWidth;
|
||||
options.outHeight = newHeight;
|
||||
|
||||
Matrix scaleMatrix = new Matrix();
|
||||
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
|
||||
|
||||
Canvas canvas = new Canvas(scaledBitmap);
|
||||
canvas.setMatrix(scaleMatrix);
|
||||
|
||||
myBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, options);
|
||||
canvas.drawBitmap(myBitmap, middleX - options.outWidth / 2, middleY - options.outHeight / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
myBitmap.recycle();
|
||||
myBitmap = null;
|
||||
canvas = null;
|
||||
imageBytes = null;
|
||||
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = scaledBitmap;
|
||||
result.cacheHit = false;
|
||||
if (!mOptions.noCaching) {
|
||||
result.cacheKey = cacheKey;
|
||||
addBitmapToMemoryCache(cacheKey, scaledBitmap);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
//Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
if (iconUrl.indexOf("data:image/") == 0 && iconUrl.contains(";base64,")) {
|
||||
String[] tmp = iconUrl.split(",");
|
||||
image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]);
|
||||
} else {
|
||||
try {
|
||||
InputStream inputStream = null;
|
||||
if (iconUrl.startsWith("file:/android_asset/")) {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
iconUrl = iconUrl.replace("file:/android_asset/", "");
|
||||
inputStream = assetManager.open(iconUrl);
|
||||
//Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
} else if (iconUrl.startsWith("file:///android_asset/")) {
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
iconUrl = iconUrl.replace("file:///android_asset/", "");
|
||||
inputStream = assetManager.open(iconUrl);
|
||||
//Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
} else if (iconUrl.startsWith("/")) {
|
||||
File file = new File(iconUrl);
|
||||
inputStream = new FileInputStream(file);
|
||||
}
|
||||
if (inputStream != null) {
|
||||
image = BitmapFactory.decodeStream(inputStream);
|
||||
inputStream.close();
|
||||
} else {
|
||||
Log.e(TAG, "Can not load the file from '" + iconUrl + "'");
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/*
|
||||
//--------------------------------
|
||||
// Load image from local path
|
||||
//--------------------------------
|
||||
if (!iconUrl.contains("://") &&
|
||||
!iconUrl.startsWith("/") &&
|
||||
!iconUrl.startsWith("www/") &&
|
||||
!iconUrl.startsWith("data:image") &&
|
||||
!iconUrl.startsWith("./") &&
|
||||
!iconUrl.startsWith("../")) {
|
||||
iconUrl = "./" + iconUrl;
|
||||
Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
}
|
||||
|
||||
if (iconUrl.startsWith("./") || iconUrl.startsWith("../")) {
|
||||
iconUrl = iconUrl.replace("(\\.\\/)+", "./");
|
||||
String currentPage = this.currentPageUrl;
|
||||
currentPage = currentPage.replaceAll("[^\\/]*$", "");
|
||||
currentPage = currentPage.replaceAll("#.*$", "");
|
||||
currentPage = currentPage.replaceAll("\\/[^\\/]+\\.[^\\/]+$", "");
|
||||
if (!currentPage.endsWith("/")) {
|
||||
currentPage = currentPage + "/";
|
||||
}
|
||||
iconUrl = currentPage + iconUrl;
|
||||
iconUrl = iconUrl.replaceAll("(\\/\\.\\/+)+", "/");
|
||||
Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
}
|
||||
|
||||
if (iconUrl.indexOf("data:image/") == 0 && iconUrl.contains(";base64,")) {
|
||||
cacheKey = getCacheKey(iconUrl, mWidth, mHeight);
|
||||
|
||||
image = getBitmapFromMemCache(cacheKey);
|
||||
if (image != null) {
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = image;
|
||||
result.cacheHit = true;
|
||||
result.cacheKey = cacheKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
String[] tmp = iconUrl.split(",");
|
||||
image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]);
|
||||
} else if (iconUrl.indexOf("file://") == 0 &&
|
||||
!iconUrl.contains("file:///android_asset/")) {
|
||||
iconUrl = iconUrl.replace("file://", "");
|
||||
File tmp = new File(iconUrl);
|
||||
if (tmp.exists()) {
|
||||
cacheKey = getCacheKey(iconUrl, mWidth, mHeight);
|
||||
|
||||
image = getBitmapFromMemCache(cacheKey);
|
||||
if (image != null) {
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = image;
|
||||
result.cacheHit = true;
|
||||
result.cacheKey = cacheKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
image = BitmapFactory.decodeFile(iconUrl);
|
||||
} else {
|
||||
//if (PluginMarker.this.mapCtrl.mPluginLayout.isDebug) {
|
||||
Log.w(TAG, "icon is not found (" + iconUrl + ")");
|
||||
//}
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "--> iconUrl = " + iconUrl);
|
||||
cacheKey = getCacheKey(iconUrl, mWidth, mHeight);
|
||||
image = getBitmapFromMemCache(cacheKey);
|
||||
|
||||
if (image != null) {
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = image;
|
||||
result.cacheHit = true;
|
||||
result.cacheKey = cacheKey;
|
||||
return result;
|
||||
}
|
||||
|
||||
Log.d(TAG, "iconUrl = " + iconUrl);
|
||||
if (iconUrl.indexOf("file:///android_asset/") == 0) {
|
||||
iconUrl = iconUrl.replace("file:///android_asset/", "");
|
||||
}
|
||||
|
||||
Log.d(TAG, "iconUrl = " + iconUrl);
|
||||
if (iconUrl.contains("./")) {
|
||||
try {
|
||||
boolean isAbsolutePath = iconUrl.startsWith("/");
|
||||
File relativePath = new File(iconUrl);
|
||||
iconUrl = relativePath.getCanonicalPath();
|
||||
Log.d(TAG, "iconUrl = " + iconUrl);
|
||||
if (!isAbsolutePath) {
|
||||
iconUrl = iconUrl.substring(1);
|
||||
}
|
||||
Log.d(TAG, "iconUrl = " + iconUrl);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//}
|
||||
|
||||
if (mWidth > 0 && mHeight > 0) {
|
||||
mWidth = Math.round(mWidth * density);
|
||||
mHeight = Math.round(mHeight * density);
|
||||
image = PluginUtil.resizeBitmap(image, mWidth, mHeight);
|
||||
} else {
|
||||
image = PluginUtil.scaleBitmapForDevice(image);
|
||||
}
|
||||
|
||||
AsyncLoadImageResult result = new AsyncLoadImageResult();
|
||||
result.image = image;
|
||||
result.cacheHit = false;
|
||||
if (!mOptions.noCaching) {
|
||||
result.cacheKey = cacheKey;
|
||||
addBitmapToMemoryCache(cacheKey, image);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(AsyncLoadImageResult result) {
|
||||
this.callback.onPostExecute(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
public interface AsyncLoadImageInterface {
|
||||
public void onPostExecute(AsyncLoadImage.AsyncLoadImageResult result) ;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.LruCache;
|
||||
|
||||
public class BitmapCache extends LruCache<String, Bitmap> {
|
||||
|
||||
public BitmapCache(int maxSize) {
|
||||
super(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int sizeOf(String key, Bitmap bitmap) {
|
||||
// The cache size will be measured in kilobytes rather than
|
||||
// number of items.
|
||||
return bitmap.getByteCount() / 1024;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void entryRemoved(boolean evicted, String key, Bitmap oldBitmap, Bitmap newBitmap) {
|
||||
if (!oldBitmap.isRecycled()) {
|
||||
oldBitmap.recycle();
|
||||
oldBitmap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,584 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.RectF;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GooglePlayServicesUtil;
|
||||
import com.google.android.gms.maps.MapsInitializer;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.LOG;
|
||||
import org.apache.cordova.PluginEntry;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CordovaGoogleMaps extends CordovaPlugin implements ViewTreeObserver.OnScrollChangedListener{
|
||||
private final String TAG = "GoogleMapsPlugin";
|
||||
private Activity activity;
|
||||
public ViewGroup root;
|
||||
public MyPluginLayout mPluginLayout = null;
|
||||
public boolean initialized = false;
|
||||
public PluginManager pluginManager;
|
||||
private static final Object timerLock = new Object();
|
||||
|
||||
@SuppressLint("NewApi") @Override
|
||||
public void initialize(final CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
if (root != null) {
|
||||
return;
|
||||
}
|
||||
LOG.setLogLevel(LOG.ERROR);
|
||||
|
||||
activity = cordova.getActivity();
|
||||
final View view = webView.getView();
|
||||
view.getViewTreeObserver().addOnScrollChangedListener(CordovaGoogleMaps.this);
|
||||
root = (ViewGroup) view.getParent();
|
||||
|
||||
pluginManager = webView.getPluginManager();
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@SuppressLint("NewApi")
|
||||
public void run() {
|
||||
|
||||
// Enable this, webView makes draw cache on the Android action bar issue.
|
||||
//View view = webView.getView();
|
||||
//if (Build.VERSION.SDK_INT >= 21 || "org.xwalk.core.XWalkView".equals(view.getClass().getName())){
|
||||
// view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
// Log.d("Layout", "--> view =" + view.isHardwareAccelerated()); //always false
|
||||
//}
|
||||
|
||||
|
||||
// ------------------------------
|
||||
// Check of Google Play Services
|
||||
// ------------------------------
|
||||
int checkGooglePlayServices = GooglePlayServicesUtil.isGooglePlayServicesAvailable(activity);
|
||||
|
||||
Log.d(TAG, "----> checkGooglePlayServices = " + (ConnectionResult.SUCCESS == checkGooglePlayServices));
|
||||
|
||||
if (checkGooglePlayServices != ConnectionResult.SUCCESS) {
|
||||
// google play services is missing!!!!
|
||||
/*
|
||||
* Returns status code indicating whether there was an error. Can be one
|
||||
* of following in ConnectionResult: SUCCESS, SERVICE_MISSING,
|
||||
* SERVICE_VERSION_UPDATE_REQUIRED, SERVICE_DISABLED, SERVICE_INVALID.
|
||||
*/
|
||||
Log.e(TAG, "---Google Play Services is not available: " + GooglePlayServicesUtil.getErrorString(checkGooglePlayServices));
|
||||
|
||||
boolean isNeedToUpdate = false;
|
||||
|
||||
String errorMsg = PluginUtil.getPgmStrings(activity, "pgm_google_play_error");
|
||||
switch (checkGooglePlayServices) {
|
||||
case ConnectionResult.DEVELOPER_ERROR:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_developer_error");
|
||||
break;
|
||||
case ConnectionResult.INTERNAL_ERROR:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_internal_error");
|
||||
break;
|
||||
case ConnectionResult.INVALID_ACCOUNT:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_invalid_account");
|
||||
break;
|
||||
case ConnectionResult.LICENSE_CHECK_FAILED:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_license_check_failed");
|
||||
break;
|
||||
case ConnectionResult.NETWORK_ERROR:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_network_error");
|
||||
break;
|
||||
case ConnectionResult.SERVICE_DISABLED:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_disabled");
|
||||
break;
|
||||
case ConnectionResult.SERVICE_INVALID:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_invalid");
|
||||
isNeedToUpdate = true;
|
||||
break;
|
||||
case ConnectionResult.SERVICE_MISSING:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_missing");
|
||||
isNeedToUpdate = true;
|
||||
break;
|
||||
case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_service_update_required");
|
||||
isNeedToUpdate = true;
|
||||
break;
|
||||
case ConnectionResult.SIGN_IN_REQUIRED:
|
||||
errorMsg = PluginUtil.getPgmStrings(activity,"pgm_google_play_sign_in_required");
|
||||
break;
|
||||
default:
|
||||
isNeedToUpdate = true;
|
||||
break;
|
||||
}
|
||||
|
||||
final boolean finalIsNeedToUpdate = isNeedToUpdate;
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
|
||||
alertDialogBuilder
|
||||
.setMessage(errorMsg)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog,int id) {
|
||||
dialog.dismiss();
|
||||
if (finalIsNeedToUpdate) {
|
||||
try {
|
||||
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.gms")));
|
||||
} catch (android.content.ActivityNotFoundException anfe) {
|
||||
activity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.google.android.gms")));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||
|
||||
// show it
|
||||
alertDialog.show();
|
||||
|
||||
Log.e(TAG, "Google Play Services is not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
webView.getView().setBackgroundColor(Color.TRANSPARENT);
|
||||
webView.getView().setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
mPluginLayout = new MyPluginLayout(webView, activity);
|
||||
mPluginLayout.stopTimer();
|
||||
|
||||
|
||||
// Check the API key
|
||||
ApplicationInfo appliInfo = null;
|
||||
try {
|
||||
appliInfo = activity.getPackageManager().getApplicationInfo(activity.getPackageName(), PackageManager.GET_META_DATA);
|
||||
} catch (NameNotFoundException e) {}
|
||||
|
||||
String API_KEY = appliInfo.metaData.getString("com.google.android.maps.v2.API_KEY");
|
||||
if ("API_KEY_FOR_ANDROID".equals(API_KEY)) {
|
||||
|
||||
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity);
|
||||
|
||||
alertDialogBuilder
|
||||
.setMessage(PluginUtil.getPgmStrings(activity,"pgm_api_key_error"))
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(PluginUtil.getPgmStrings(activity,"pgm_google_close_button"), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog,int id) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||
|
||||
// show it
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
|
||||
//------------------------------
|
||||
// Initialize Google Maps SDK
|
||||
//------------------------------
|
||||
if (!initialized) {
|
||||
try {
|
||||
MapsInitializer.initialize(cordova.getActivity());
|
||||
initialized = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOverrideUrlLoading(String url) {
|
||||
mPluginLayout.stopTimer();
|
||||
/*
|
||||
this.activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.loadUrl("javascript:if(window.cordova){cordova.fireDocumentEvent('plugin_url_changed', {});}");
|
||||
}
|
||||
});
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onScrollChanged() {
|
||||
if (mPluginLayout == null) {
|
||||
return;
|
||||
}
|
||||
View view = webView.getView();
|
||||
int scrollX = view.getScrollX();
|
||||
int scrollY = view.getScrollY();
|
||||
mPluginLayout.scrollTo(scrollX, scrollY);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
cordova.getThreadPool().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (action.equals("putHtmlElements")) {
|
||||
CordovaGoogleMaps.this.putHtmlElements(args, callbackContext);
|
||||
} else if ("clearHtmlElements".equals(action)) {
|
||||
CordovaGoogleMaps.this.clearHtmlElements(args, callbackContext);
|
||||
} else if ("pause".equals(action)) {
|
||||
CordovaGoogleMaps.this.pause(args, callbackContext);
|
||||
} else if ("resume".equals(action)) {
|
||||
CordovaGoogleMaps.this.resume(args, callbackContext);
|
||||
} else if ("getMap".equals(action)) {
|
||||
CordovaGoogleMaps.this.getMap(args, callbackContext);
|
||||
} else if ("getPanorama".equals(action)) {
|
||||
CordovaGoogleMaps.this.getPanorama(args, callbackContext);
|
||||
} else if ("removeMap".equals(action)) {
|
||||
CordovaGoogleMaps.this.removeMap(args, callbackContext);
|
||||
} else if ("backHistory".equals(action)) {
|
||||
CordovaGoogleMaps.this.backHistory(args, callbackContext);
|
||||
} else if ("updateMapPositionOnly".equals(action)) {
|
||||
CordovaGoogleMaps.this.updateMapPositionOnly(args, callbackContext);
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public void updateMapPositionOnly(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final JSONObject elements = args.getJSONObject(0);
|
||||
|
||||
Bundle elementsBundle = PluginUtil.Json2Bundle(elements);
|
||||
float zoomScale = Resources.getSystem().getDisplayMetrics().density;
|
||||
|
||||
Iterator<String> domIDs = elementsBundle.keySet().iterator();
|
||||
String domId;
|
||||
Bundle domInfo, size, currentDomInfo;
|
||||
while (domIDs.hasNext()) {
|
||||
domId = domIDs.next();
|
||||
domInfo = elementsBundle.getBundle(domId);
|
||||
|
||||
size = domInfo.getBundle("size");
|
||||
RectF rectF = new RectF();
|
||||
rectF.left = (float)(Double.parseDouble(size.get("left") + "") * zoomScale);
|
||||
rectF.top = (float)(Double.parseDouble(size.get("top") + "") * zoomScale);
|
||||
rectF.right = rectF.left + (float)(Double.parseDouble(size.get("width") + "") * zoomScale);
|
||||
rectF.bottom = rectF.top + (float)(Double.parseDouble(size.get("height") + "") * zoomScale);
|
||||
|
||||
mPluginLayout.HTMLNodeRectFs.put(domId, rectF);
|
||||
}
|
||||
|
||||
if (mPluginLayout.isSuspended) {
|
||||
mPluginLayout.updateMapPositions();
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
public void backHistory(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!webView.backHistory()) {
|
||||
// If no more history back, exit the app
|
||||
cordova.getActivity().finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public synchronized void pause(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
synchronized (timerLock) {
|
||||
if (mPluginLayout == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
mPluginLayout.stopTimer();
|
||||
callbackContext.success();
|
||||
}
|
||||
}
|
||||
public synchronized void resume(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
synchronized (timerLock) {
|
||||
if (mPluginLayout == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
if (mPluginLayout.isSuspended) {
|
||||
mPluginLayout.startTimer();
|
||||
}
|
||||
callbackContext.success();
|
||||
|
||||
//On resume reapply background because it might have been changed by some other plugin
|
||||
webView.getView().setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
public void clearHtmlElements(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
if (mPluginLayout == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
mPluginLayout.clearHtmlElements();
|
||||
callbackContext.success();
|
||||
}
|
||||
public void putHtmlElements(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
final JSONObject elements = args.getJSONObject(0);
|
||||
if (mPluginLayout == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
|
||||
//Log.d(TAG, "--->stopFlag = " + mPluginLayout.stopFlag + ", mPluginLayout.needUpdatePosition = " + mPluginLayout.needUpdatePosition);
|
||||
if (!mPluginLayout.stopFlag || mPluginLayout.needUpdatePosition) {
|
||||
mPluginLayout.putHTMLElements(elements);
|
||||
}
|
||||
|
||||
//mPluginLayout.updateMapPositions();
|
||||
//mPluginLayout.startTimer();
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
super.onReset();
|
||||
if (mPluginLayout == null || mPluginLayout.pluginOverlays == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
mPluginLayout.setBackgroundColor(Color.WHITE);
|
||||
|
||||
Set<String> mapIds = mPluginLayout.pluginOverlays.keySet();
|
||||
IPluginView pluginOverlay;
|
||||
|
||||
// prevent the ConcurrentModificationException error.
|
||||
String[] mapIdArray= mapIds.toArray(new String[mapIds.size()]);
|
||||
for (String mapId : mapIdArray) {
|
||||
if (mPluginLayout.pluginOverlays.containsKey(mapId)) {
|
||||
pluginOverlay = mPluginLayout.removePluginOverlay(mapId);
|
||||
pluginOverlay.remove(null, null);
|
||||
pluginOverlay.onDestroy();
|
||||
mPluginLayout.HTMLNodes.remove(mapId);
|
||||
}
|
||||
}
|
||||
mPluginLayout.HTMLNodes.clear();
|
||||
mPluginLayout.pluginOverlays.clear();
|
||||
|
||||
System.gc();
|
||||
Runtime.getRuntime().gc();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void removeMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String mapId = args.getString(0);
|
||||
if (mPluginLayout.pluginOverlays.containsKey(mapId)) {
|
||||
IPluginView pluginOverlay = mPluginLayout.removePluginOverlay(mapId);
|
||||
if (pluginOverlay != null) {
|
||||
pluginOverlay.remove(null, null);
|
||||
pluginOverlay.onDestroy();
|
||||
mPluginLayout.HTMLNodes.remove(mapId);
|
||||
pluginOverlay = null;
|
||||
}
|
||||
|
||||
try {
|
||||
Field pluginMapField = pluginManager.getClass().getDeclaredField("pluginMap");
|
||||
pluginMapField.setAccessible(true);
|
||||
LinkedHashMap<String, CordovaPlugin> pluginMapInstance = (LinkedHashMap<String, CordovaPlugin>) pluginMapField.get(pluginManager);
|
||||
pluginMapInstance.remove(mapId);
|
||||
Field entryMapField = pluginManager.getClass().getDeclaredField("entryMap");
|
||||
entryMapField.setAccessible(true);
|
||||
LinkedHashMap<String, PluginEntry> entryMapInstance = (LinkedHashMap<String, PluginEntry>) entryMapField.get(pluginManager);
|
||||
entryMapInstance.remove(mapId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
System.gc();
|
||||
Runtime.getRuntime().gc();
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void getMap(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
//------------------------------------------
|
||||
// Create an instance of PluginMap class.
|
||||
//------------------------------------------
|
||||
JSONObject meta = args.getJSONObject(0);
|
||||
String mapId = meta.getString("__pgmId");
|
||||
PluginMap pluginMap = new PluginMap();
|
||||
pluginMap.privateInitialize(mapId, cordova, webView, null);
|
||||
pluginMap.initialize(cordova, webView);
|
||||
pluginMap.mapCtrl = CordovaGoogleMaps.this;
|
||||
pluginMap.self = pluginMap;
|
||||
|
||||
PluginEntry pluginEntry = new PluginEntry(mapId, pluginMap);
|
||||
pluginManager.addService(pluginEntry);
|
||||
|
||||
pluginMap.getMap(args, callbackContext);
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void getPanorama(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
//------------------------------------------
|
||||
// Create an instance of PluginStreetView class.
|
||||
//------------------------------------------
|
||||
JSONObject meta = args.getJSONObject(0);
|
||||
String mapId = meta.getString("__pgmId");
|
||||
Log.d(TAG, "---> mapId = " + mapId);
|
||||
PluginStreetViewPanorama pluginStreetView = new PluginStreetViewPanorama();
|
||||
pluginStreetView.privateInitialize(mapId, cordova, webView, null);
|
||||
pluginStreetView.initialize(cordova, webView);
|
||||
pluginStreetView.mapCtrl = CordovaGoogleMaps.this;
|
||||
pluginStreetView.self = pluginStreetView;
|
||||
|
||||
PluginEntry pluginEntry = new PluginEntry(mapId, pluginStreetView);
|
||||
pluginManager.addService(pluginEntry);
|
||||
|
||||
pluginStreetView.getPanorama(args, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
Collection<PluginEntry>pluginEntries = pluginManager.getPluginEntries();
|
||||
for (PluginEntry pluginEntry: pluginEntries) {
|
||||
if (pluginEntry.service.startsWith("map_")) {
|
||||
pluginEntry.plugin.onStart();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
Collection<PluginEntry>pluginEntries = pluginManager.getPluginEntries();
|
||||
for (PluginEntry pluginEntry: pluginEntries) {
|
||||
if (pluginEntry.service.startsWith("map_")) {
|
||||
pluginEntry.plugin.onStop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onPause(boolean multitasking) {
|
||||
super.onPause(multitasking);
|
||||
if (mPluginLayout != null) {
|
||||
mPluginLayout.stopTimer();
|
||||
}
|
||||
|
||||
Collection<PluginEntry>pluginEntries = pluginManager.getPluginEntries();
|
||||
for (PluginEntry pluginEntry: pluginEntries) {
|
||||
if (pluginEntry.service.startsWith("map_")) {
|
||||
pluginEntry.plugin.onPause(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(boolean multitasking) {
|
||||
Collection<PluginEntry>pluginEntries = pluginManager.getPluginEntries();
|
||||
for (Iterator<PluginEntry> iterator = pluginEntries.iterator(); iterator.hasNext();) {
|
||||
PluginEntry pluginEntry = iterator.next();
|
||||
if (pluginEntry.service.startsWith("map_")) {
|
||||
pluginEntry.plugin.onResume(multitasking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Collection<PluginEntry>pluginEntries = pluginManager.getPluginEntries();
|
||||
for (PluginEntry pluginEntry: pluginEntries) {
|
||||
if (pluginEntry.service.startsWith("map_")) {
|
||||
pluginEntry.plugin.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the system when the device configuration changes while your activity is running.
|
||||
*
|
||||
* @param newConfig The new device configuration
|
||||
*/
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PluginMap pluginMap;
|
||||
Collection<PluginEntry> collection = pluginManager.getPluginEntries();
|
||||
for (PluginEntry entry: collection) {
|
||||
if ("plugin.google.maps.PluginMap".equals(entry.pluginClass) && entry.plugin != null) {
|
||||
pluginMap = (PluginMap)entry.plugin;
|
||||
if (pluginMap.map != null) {
|
||||
|
||||
// Trigger the CAMERA_MOVE_END mandatory
|
||||
pluginMap.onCameraIdle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
/*
|
||||
// Checks the orientation of the screen
|
||||
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
Toast.makeText(activity, "landscape", Toast.LENGTH_SHORT).show();
|
||||
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
|
||||
Toast.makeText(activity, "portrait", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public interface IPluginView {
|
||||
boolean getVisible();
|
||||
boolean getClickable();
|
||||
String getDivId();
|
||||
String getOverlayId();
|
||||
ViewGroup getView();
|
||||
int getViewDepth();
|
||||
void onDestroy();
|
||||
void onStart();
|
||||
void onStop();
|
||||
void onPause(boolean multitasking);
|
||||
void onResume(boolean multitasking);
|
||||
void remove(JSONArray args, final CallbackContext callbackContext);
|
||||
void attachToWebView(JSONArray args, final CallbackContext callbackContext);
|
||||
void detachFromWebView(JSONArray args, final CallbackContext callbackContext);
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.maps.GoogleMap;
|
||||
import com.google.android.gms.maps.model.Circle;
|
||||
import com.google.android.gms.maps.model.GroundOverlay;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
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 com.google.android.gms.maps.model.TileOverlay;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginEntry;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class MyPlugin extends CordovaPlugin implements MyPluginInterface {
|
||||
public MyPlugin self = null;
|
||||
public final Map<String, Method> methods = new ConcurrentHashMap<String, Method>();
|
||||
protected static ExecutorService executorService = null;
|
||||
|
||||
public CordovaGoogleMaps mapCtrl = null;
|
||||
public GoogleMap map = null;
|
||||
public PluginMap pluginMap = null;
|
||||
protected boolean isRemoved = false;
|
||||
protected static float density = Resources.getSystem().getDisplayMetrics().density;
|
||||
|
||||
public void setPluginMap(PluginMap pluginMap) {
|
||||
this.pluginMap = pluginMap;
|
||||
this.mapCtrl = pluginMap.mapCtrl;
|
||||
this.map = pluginMap.map;
|
||||
}
|
||||
protected String TAG = "";
|
||||
|
||||
@SuppressLint("UseSparseArrays")
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
TAG = this.getServiceName();
|
||||
if (executorService == null) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
executorService = Executors.newCachedThreadPool();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) {
|
||||
self = this;
|
||||
executorService.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (isRemoved) {
|
||||
// Ignore every execute calls.
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (methods) {
|
||||
if (methods.size() == 0) {
|
||||
TAG = MyPlugin.this.getServiceName();
|
||||
if (!TAG.contains("-")) {
|
||||
if (TAG.startsWith("map")) {
|
||||
mapCtrl.mPluginLayout.pluginOverlays.put(TAG, (PluginMap) MyPlugin.this);
|
||||
} else if (TAG.startsWith("streetview")) {
|
||||
mapCtrl.mPluginLayout.pluginOverlays.put(TAG, (PluginStreetViewPanorama) MyPlugin.this);
|
||||
}
|
||||
} else {
|
||||
PluginEntry pluginEntry = new PluginEntry(TAG, MyPlugin.this);
|
||||
pluginMap.plugins.put(TAG, pluginEntry);
|
||||
}
|
||||
|
||||
|
||||
//CordovaPlugin plugin = mapCtrl.webView.getPluginManager().getPlugin(this.getServiceName());
|
||||
// Log.d("MyPlugin", "---> this = " + this);
|
||||
// Log.d("MyPlugin", "---> plugin = " + plugin);
|
||||
|
||||
Method[] classMethods = self.getClass().getMethods();
|
||||
for (Method classMethod : classMethods) {
|
||||
methods.put(classMethod.getName(), classMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// this.create(args, callbackContext);
|
||||
// return true;
|
||||
if (methods.containsKey(action)) {
|
||||
if (self.mapCtrl.mPluginLayout.isDebug) {
|
||||
try {
|
||||
if (args != null && args.length() > 0) {
|
||||
Log.d(TAG, "(debug)action=" + action + " args[0]=" + args.getString(0));
|
||||
} else {
|
||||
Log.d(TAG, "(debug)action=" + action);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Method method = methods.get(action);
|
||||
try {
|
||||
if (isRemoved) {
|
||||
// Ignore every execute calls.
|
||||
return;
|
||||
}
|
||||
method.invoke(self, args, callbackContext);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("Cannot access to the '" + action + "' method.");
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("Cannot access to the '" + action + "' method.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
// dummy
|
||||
}
|
||||
|
||||
protected synchronized Circle getCircle(String id) {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
//Log.e(TAG, "---> can not find the circle : " + id);
|
||||
return null;
|
||||
}
|
||||
return (Circle)pluginMap.objects.get(id);
|
||||
}
|
||||
protected synchronized GroundOverlay getGroundOverlay(String id) {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
//Log.e(TAG, "---> can not find the ground overlay : " + id);
|
||||
return null;
|
||||
}
|
||||
return (GroundOverlay)pluginMap.objects.get(id);
|
||||
}
|
||||
protected synchronized Marker getMarker(String id) {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
//Log.e(TAG, "---> can not find the maker : " + id);
|
||||
return null;
|
||||
}
|
||||
return (Marker)pluginMap.objects.get(id);
|
||||
}
|
||||
protected synchronized Polyline getPolyline(String id) {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
//Log.e(TAG, "---> can not find the polyline : " + id);
|
||||
return null;
|
||||
}
|
||||
return (Polyline)pluginMap.objects.get(id);
|
||||
}
|
||||
protected synchronized Polygon getPolygon(String id) {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
//Log.e(TAG, "---> can not find the polygon : " + id);
|
||||
return null;
|
||||
}
|
||||
return (Polygon)pluginMap.objects.get(id);
|
||||
}
|
||||
protected synchronized TileOverlay getTileOverlay(String id) {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
//Log.e(TAG, "---> can not find the tileoverlay : " + id);
|
||||
return null;
|
||||
}
|
||||
return (TileOverlay)pluginMap.objects.get(id);
|
||||
}
|
||||
|
||||
protected void setInt(String methodName, String id, int value, final CallbackContext callbackContext) throws JSONException {
|
||||
this.setValue(methodName, int.class, id, value, callbackContext);
|
||||
}
|
||||
protected void setFloat(String methodName, String id, float value, final CallbackContext callbackContext) throws JSONException {
|
||||
this.setValue(methodName, float.class, id, value, callbackContext);
|
||||
}
|
||||
protected void setDouble(String methodName, String id, float value, final CallbackContext callbackContext) throws JSONException {
|
||||
this.setValue(methodName, double.class, id, value, callbackContext);
|
||||
}
|
||||
protected void setString(String methodName, String id, String value, final CallbackContext callbackContext) throws JSONException {
|
||||
this.setValue(methodName, String.class, id, value, callbackContext);
|
||||
}
|
||||
|
||||
protected void setBoolean(String methodName, String id, Boolean value, final CallbackContext callbackContext) throws JSONException {
|
||||
this.setValue(methodName, boolean.class, id, value, callbackContext);
|
||||
}
|
||||
|
||||
private void setValue(String methodName, Class<?> methodClass, String id, final Object value, final CallbackContext callbackContext) throws JSONException {
|
||||
if (!pluginMap.objects.containsKey(id)) {
|
||||
return;
|
||||
}
|
||||
final Object object = pluginMap.objects.get(id);
|
||||
try {
|
||||
final Method method = object.getClass().getDeclaredMethod(methodName, methodClass);
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
method.invoke(object, value);
|
||||
callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
protected void clear() {
|
||||
String[] keys = pluginMap.objects.keys.toArray(new String[pluginMap.objects.size()]);
|
||||
Object object;
|
||||
for (String key : keys) {
|
||||
object = pluginMap.objects.remove(key);
|
||||
object = null;
|
||||
}
|
||||
pluginMap.objects.clear();
|
||||
}
|
||||
|
||||
|
||||
protected void onOverlayEvent(String eventName, String overlayId, LatLng point) {
|
||||
webView.loadUrl("javascript:plugin.google.maps.Map." +
|
||||
"_onOverlayEvent(" +
|
||||
"'" + eventName + "','" + overlayId + "', " +
|
||||
"new window.plugin.google.maps.LatLng(" + point.latitude + "," + point.longitude + ")" +
|
||||
")");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaArgs;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public interface MyPluginInterface {
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView);
|
||||
public void setPluginMap(PluginMap pluginMap);
|
||||
public boolean execute(String action, String rawArgs, CallbackContext callbackContext) throws JSONException;
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException;
|
||||
public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException;
|
||||
}
|
||||
@@ -0,0 +1,716 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.AbsoluteLayout;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MyPluginLayout extends FrameLayout implements ViewTreeObserver.OnScrollChangedListener, ViewTreeObserver.OnGlobalLayoutListener {
|
||||
private static final String TAG = "MyPluginLayout";
|
||||
private View browserView;
|
||||
private Context context;
|
||||
private FrontLayerLayout frontLayer;
|
||||
private ScrollView scrollView;
|
||||
public FrameLayout scrollFrameLayout;
|
||||
public Map<String, IPluginView> pluginOverlays = new ConcurrentHashMap<String, IPluginView>();
|
||||
private Map<String, TouchableWrapper> touchableWrappers = new ConcurrentHashMap<String, TouchableWrapper>();
|
||||
private boolean isScrolling = false;
|
||||
public boolean isDebug = false;
|
||||
public final Object _lockHtmlNodes = new Object();
|
||||
public Map<String, String> CACHE_FIND_DOM = new ConcurrentHashMap<String, String>();
|
||||
public Map<String, Bundle> HTMLNodes = new ConcurrentHashMap<String, Bundle>();
|
||||
public Map<String, RectF> HTMLNodeRectFs = new ConcurrentHashMap<String, RectF>();
|
||||
private Activity mActivity = null;
|
||||
private Paint debugPaint = new Paint();
|
||||
public boolean stopFlag = false;
|
||||
public boolean needUpdatePosition = false;
|
||||
public boolean isSuspended = false;
|
||||
private float zoomScale;
|
||||
private static final Object timerLock = new Object();
|
||||
|
||||
private static Timer redrawTimer;
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
ViewTreeObserver observer = browserView.getViewTreeObserver();
|
||||
observer.removeGlobalOnLayoutListener(this);
|
||||
observer.addOnScrollChangedListener(this);
|
||||
}
|
||||
|
||||
private Runnable resizeWorker = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final int scrollY = browserView.getScrollY();
|
||||
final int scrollX = browserView.getScrollX();
|
||||
final int webviewWidth = browserView.getWidth();
|
||||
final int webviewHeight = browserView.getHeight();
|
||||
|
||||
Set<String> keySet = pluginOverlays.keySet();
|
||||
String[] toArrayBuf = new String[pluginOverlays.size()];
|
||||
String[] mapIds = keySet.toArray(toArrayBuf);
|
||||
String mapId;
|
||||
IPluginView pluginOverlay;
|
||||
RectF drawRect;
|
||||
for (int i = 0; i < mapIds.length; i++) {
|
||||
mapId = mapIds[i];
|
||||
if (mapId == null) {
|
||||
continue;
|
||||
}
|
||||
pluginOverlay = pluginOverlays.get(mapId);
|
||||
if (pluginOverlay == null || pluginOverlay.getDivId() == null) {
|
||||
continue;
|
||||
}
|
||||
drawRect = HTMLNodeRectFs.get(pluginOverlay.getDivId());
|
||||
if (drawRect == null || drawRect.left == 0 && drawRect.top == 0 && drawRect.width() == 0 && drawRect.height() == 0) {
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int width = (int)drawRect.width();
|
||||
int height = (int)drawRect.height();
|
||||
int x = (int) drawRect.left;
|
||||
int y = (int) drawRect.top + scrollY;
|
||||
|
||||
|
||||
|
||||
if (pluginOverlay.getVisible()) {
|
||||
if (pluginOverlay.getOverlayId().startsWith("streetview_")) {
|
||||
if (y + height >= scrollY &&
|
||||
x + width >= scrollX &&
|
||||
y < scrollY + webviewHeight &&
|
||||
x < scrollX + webviewWidth) {
|
||||
MyPluginLayout.this.addPluginOverlay(pluginOverlay);
|
||||
} else {
|
||||
|
||||
scrollFrameLayout.removeView(pluginOverlay.getView());
|
||||
pluginOverlay.getView().removeView(touchableWrappers.remove(pluginOverlay.getOverlayId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ViewGroup.LayoutParams lParams = pluginOverlay.getView().getLayoutParams();
|
||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) lParams;
|
||||
//Log.d("MyPluginLayout", "-->FrameLayout x = " + x + ", y = " + y + ", w = " + width + ", h = " + height);
|
||||
if (params.leftMargin == x && params.topMargin == y &&
|
||||
params.width == width && params.height == height) {
|
||||
continue;
|
||||
}
|
||||
params.width = width;
|
||||
params.height = height;
|
||||
params.leftMargin = x;
|
||||
params.topMargin = y;
|
||||
pluginOverlay.getView().setLayoutParams(params);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void updateMapPositions() {
|
||||
mActivity.runOnUiThread(resizeWorker);
|
||||
}
|
||||
|
||||
private class ResizeTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
mActivity.runOnUiThread(resizeWorker);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public MyPluginLayout(CordovaWebView webView, Activity activity) {
|
||||
super(webView.getView().getContext());
|
||||
this.browserView = webView.getView();
|
||||
browserView.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||
mActivity = activity;
|
||||
ViewGroup root = (ViewGroup) browserView.getParent();
|
||||
this.context = browserView.getContext();
|
||||
//if (Build.VERSION.SDK_INT >= 21 || "org.xwalk.core.XWalkView".equals(browserView.getClass().getName())) {
|
||||
// browserView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
//}
|
||||
|
||||
zoomScale = Resources.getSystem().getDisplayMetrics().density;
|
||||
Log.e(TAG, "--> zoomScale = " + zoomScale);
|
||||
frontLayer = new FrontLayerLayout(this.context);
|
||||
|
||||
scrollView = new ScrollView(this.context);
|
||||
scrollView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
|
||||
root.removeView(browserView);
|
||||
frontLayer.addView(browserView);
|
||||
|
||||
scrollFrameLayout = new FrameLayout(this.context);
|
||||
scrollFrameLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
||||
|
||||
|
||||
View dummyView = new View(this.context);
|
||||
dummyView.setLayoutParams(new LayoutParams(1, 99999));
|
||||
scrollFrameLayout.addView(dummyView);
|
||||
|
||||
scrollView.setHorizontalScrollBarEnabled(true);
|
||||
scrollView.setVerticalScrollBarEnabled(true);
|
||||
scrollView.addView(scrollFrameLayout);
|
||||
|
||||
browserView.setDrawingCacheEnabled(false);
|
||||
|
||||
|
||||
this.addView(scrollView);
|
||||
this.addView(frontLayer);
|
||||
root.addView(this);
|
||||
browserView.setBackgroundColor(Color.TRANSPARENT);
|
||||
/*
|
||||
if("org.xwalk.core.XWalkView".equals(browserView.getClass().getName())
|
||||
|| "org.crosswalk.engine.XWalkCordovaView".equals(browserView.getClass().getName())) {
|
||||
try {
|
||||
// view.setZOrderOnTop(true)
|
||||
// Called just in time as with root.setBackground(...) the color
|
||||
// come in front and take the whole screen
|
||||
browserView.getClass().getMethod("setZOrderOnTop", boolean.class)
|
||||
.invoke(browserView, true);
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
*/
|
||||
scrollView.setHorizontalScrollBarEnabled(false);
|
||||
scrollView.setVerticalScrollBarEnabled(false);
|
||||
startTimer();
|
||||
}
|
||||
|
||||
public synchronized void stopTimer() {
|
||||
synchronized (timerLock) {
|
||||
try {
|
||||
if (redrawTimer != null) {
|
||||
redrawTimer.cancel();
|
||||
redrawTimer.purge();
|
||||
ResizeTask task = new ResizeTask();
|
||||
task.run();
|
||||
isSuspended = true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
redrawTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void startTimer() {
|
||||
synchronized (timerLock) {
|
||||
if (redrawTimer != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
redrawTimer = new Timer();
|
||||
redrawTimer.scheduleAtFixedRate(new ResizeTask(), 0, 25);
|
||||
//mActivity.getWindow().getDecorView().requestFocus(); <--- This changes thread number sometimes. Do not use this!
|
||||
isSuspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void clearHtmlElements() {
|
||||
synchronized (_lockHtmlNodes) {
|
||||
String[] keys = HTMLNodes.keySet().toArray(new String[HTMLNodes.size()]);
|
||||
for (int i = 0; i < HTMLNodes.size(); i++) {
|
||||
HTMLNodes.remove(keys[i]);
|
||||
HTMLNodeRectFs.remove(keys[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void putHTMLElements(JSONObject elements) {
|
||||
|
||||
|
||||
Map<String, Bundle> newBuffer = new ConcurrentHashMap<String, Bundle>();
|
||||
Map<String, RectF> newBufferRectFs = new ConcurrentHashMap<String, RectF>();
|
||||
|
||||
Bundle elementsBundle = PluginUtil.Json2Bundle(elements);
|
||||
|
||||
Iterator<String> domIDs = elementsBundle.keySet().iterator();
|
||||
String domId;
|
||||
Bundle domInfo, size;
|
||||
while (domIDs.hasNext()) {
|
||||
domId = domIDs.next();
|
||||
domInfo = elementsBundle.getBundle(domId);
|
||||
if (domInfo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size = domInfo.getBundle("size");
|
||||
if (size == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
RectF rectF = new RectF();
|
||||
rectF.left = (float)(Double.parseDouble(size.get("left") + "") * zoomScale);
|
||||
rectF.top = (float)(Double.parseDouble(size.get("top") + "") * zoomScale);
|
||||
rectF.right = (float)(Double.parseDouble(size.get("right") + "") * zoomScale);
|
||||
rectF.bottom = (float)(Double.parseDouble(size.get("bottom") + "") * zoomScale);
|
||||
|
||||
|
||||
newBufferRectFs.put(domId, rectF);
|
||||
|
||||
domInfo.remove("size");
|
||||
newBuffer.put(domId, domInfo);
|
||||
}
|
||||
|
||||
Map<String, Bundle> oldBuffer = HTMLNodes;
|
||||
Map<String, RectF> oldBufferRectFs = HTMLNodeRectFs;
|
||||
|
||||
synchronized (_lockHtmlNodes) {
|
||||
HTMLNodes = newBuffer;
|
||||
HTMLNodeRectFs = newBufferRectFs;
|
||||
}
|
||||
|
||||
String[] keys = oldBuffer.keySet().toArray(new String[oldBuffer.size()]);
|
||||
for (int i = 0; i < oldBuffer.size(); i++) {
|
||||
oldBuffer.remove(keys[i]);
|
||||
oldBufferRectFs.remove(keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDebug(final boolean debug) {
|
||||
this.isDebug = debug;
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (debug) {
|
||||
inValidate();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public IPluginView removePluginOverlay(final String overlayId) {
|
||||
if (pluginOverlays == null || !pluginOverlays.containsKey(overlayId)) {
|
||||
return null;
|
||||
}
|
||||
final IPluginView pluginOverlay = pluginOverlays.remove(overlayId);
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
scrollFrameLayout.removeView(pluginOverlay.getView());
|
||||
pluginOverlay.getView().removeView(touchableWrappers.remove(overlayId));
|
||||
|
||||
//Log.d("MyPluginLayout", "--> removePluginMap / overlayId = " + overlayId);
|
||||
|
||||
|
||||
//mActivity.getWindow().getDecorView().requestFocus();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return pluginOverlay;
|
||||
}
|
||||
|
||||
public void addPluginOverlay(final IPluginView pluginOverlay) {
|
||||
if (pluginOverlay.getDivId() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HTMLNodes.containsKey(pluginOverlay.getDivId())) {
|
||||
Log.e(TAG, "----> 200x200");
|
||||
Bundle dummyInfo = new Bundle();
|
||||
dummyInfo.putDouble("offsetX", 0);
|
||||
dummyInfo.putDouble("offsetY", 3000);
|
||||
dummyInfo.putBoolean("isDummy", true);
|
||||
HTMLNodes.put(pluginOverlay.getDivId(), dummyInfo);
|
||||
HTMLNodeRectFs.put(pluginOverlay.getDivId(), new RectF(0, 3000, 200, 200));
|
||||
}
|
||||
pluginOverlays.put(pluginOverlay.getOverlayId(), pluginOverlay);
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (pluginOverlay.getView().getParent() == null) {
|
||||
TouchableWrapper wrapper = new TouchableWrapper(context);
|
||||
touchableWrappers.put(pluginOverlay.getOverlayId(), wrapper);
|
||||
pluginOverlay.getView().addView(wrapper);
|
||||
int depth = pluginOverlay.getViewDepth();
|
||||
int childCnt = scrollFrameLayout.getChildCount();
|
||||
View view;
|
||||
int index = childCnt;
|
||||
for (int i = childCnt - 1; i >= 0; i--) {
|
||||
view = scrollFrameLayout.getChildAt(i);
|
||||
if (view.getTag() == null) {
|
||||
continue;
|
||||
}
|
||||
if (Integer.parseInt(view.getTag() + "") < depth) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scrollFrameLayout.addView(pluginOverlay.getView(), (childCnt - index));
|
||||
}
|
||||
|
||||
//mActivity.getWindow().getDecorView().requestFocus();
|
||||
|
||||
//updateViewPosition(pluginMap.mapId);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void scrollTo(int x, int y) {
|
||||
this.scrollView.scrollTo(x, y);
|
||||
}
|
||||
|
||||
public void inValidate() {
|
||||
this.frontLayer.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollChanged() {
|
||||
//Log.d("Layout", "---> onScrollChanged");
|
||||
scrollView.scrollTo(browserView.getScrollX(), browserView.getScrollY());
|
||||
}
|
||||
|
||||
private class Overflow {
|
||||
boolean cropX;
|
||||
boolean cropY;
|
||||
RectF rect;
|
||||
}
|
||||
|
||||
private class FrontLayerLayout extends FrameLayout {
|
||||
|
||||
public FrontLayerLayout(Context context) {
|
||||
super(context);
|
||||
this.setWillNotDraw(false);
|
||||
}
|
||||
|
||||
private String findClickedDom(String domId, PointF clickPoint, boolean isMapChild, Overflow overflow) {
|
||||
if (CACHE_FIND_DOM.containsKey(domId)) {
|
||||
String cacheResult = CACHE_FIND_DOM.get(domId);
|
||||
if ("(null)".equals(cacheResult)) {
|
||||
cacheResult = null;
|
||||
}
|
||||
return cacheResult;
|
||||
}
|
||||
|
||||
Overflow overflow1 = null;
|
||||
if (overflow != null) {
|
||||
overflow1 = new Overflow();
|
||||
overflow1.cropX = overflow.cropX;
|
||||
overflow1.cropY = overflow.cropY;
|
||||
overflow1.rect = new RectF(overflow.rect.left, overflow.rect.top, overflow.rect.right, overflow.rect.bottom);
|
||||
}
|
||||
//Log.d(TAG, "-(findClickedDom)domId = " + domId + ", clickPoint = " + clickPoint.x + ", " + clickPoint.y);
|
||||
|
||||
String maxDomId = null;
|
||||
RectF rect, rect2, rect3;
|
||||
Bundle domInfo = HTMLNodes.get(domId);
|
||||
Bundle zIndexProp;
|
||||
int containMapCnt = 0;
|
||||
if (domInfo.containsKey("containMapIDs")) {
|
||||
Set<String> keys = domInfo.getBundle("containMapIDs").keySet();
|
||||
if (keys != null) {
|
||||
containMapCnt = keys.size();
|
||||
}
|
||||
}
|
||||
isMapChild = isMapChild || domInfo.getBoolean("isMap", false);
|
||||
|
||||
String pointerEvents = domInfo.getString("pointerEvents");
|
||||
String overflowX = domInfo.getString("overflowX");
|
||||
String overflowY = domInfo.getString("overflowY");
|
||||
if ("hidden".equals(overflowX) || "scroll".equals(overflowX) ||
|
||||
"hidden".equals(overflowY) || "scroll".equals(overflowY)) {
|
||||
overflow1 = new Overflow();
|
||||
overflow1.cropX = "hidden".equals(overflowX) || "scroll".equals(overflowX);
|
||||
overflow1.cropY = "hidden".equals(overflowY) || "scroll".equals(overflowY);
|
||||
rect3 = HTMLNodeRectFs.get(domId);
|
||||
overflow1.rect = new RectF(rect3.left, rect3.top, rect3.right, rect3.bottom);
|
||||
} else if ("visible".equals(overflowX) || "visible".equals(overflowY)) {
|
||||
if (overflow1 != null) {
|
||||
overflow1.cropX = !"visible".equals(overflowX);
|
||||
overflow1.cropY = !"visible".equals(overflowY);
|
||||
}
|
||||
}
|
||||
|
||||
//if (overflow1 != null) {
|
||||
// Log.d(TAG, "--domId = " + domId + ", domInfo = " + domInfo + "\n" +
|
||||
// " overflow1 cropX= " + overflow1.cropX + ", cropY = " + overflow1.cropY + ",\n" +
|
||||
// " rect = " + overflow1.rect.toShortString());
|
||||
//} else {
|
||||
// Log.d(TAG, "--domId = " + domId + ", domInfo = " + domInfo);
|
||||
//}
|
||||
|
||||
zIndexProp = domInfo.getBundle("zIndex");
|
||||
ArrayList<String> children = domInfo.getStringArrayList("children");
|
||||
if ((containMapCnt > 0 || isMapChild || "none".equals(pointerEvents) || zIndexProp.getBoolean("isInherit")) && children != null && children.size() > 0) {
|
||||
int maxZindex = (int) Double.NEGATIVE_INFINITY;
|
||||
int zIndexValue;
|
||||
String childId, grandChildId;
|
||||
ArrayList<String> grandChildren;
|
||||
for (int i = children.size() - 1; i >= 0; i--) {
|
||||
childId = children.get(i);
|
||||
domInfo = HTMLNodes.get(childId);
|
||||
if (domInfo == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
zIndexProp = domInfo.getBundle("zIndex");
|
||||
zIndexValue = zIndexProp.getInt("z");
|
||||
if (maxZindex < zIndexValue || zIndexProp.getBoolean("isInherit")) {
|
||||
grandChildren = domInfo.getStringArrayList("children");
|
||||
if (grandChildren == null || grandChildren.size() == 0) {
|
||||
rect = HTMLNodeRectFs.get(childId);
|
||||
rect2 = new RectF(rect.left, rect.top, rect.right, rect.bottom);
|
||||
if (overflow1 != null && !"root".equals(domId) ) {
|
||||
overflow1.rect = new RectF(HTMLNodeRectFs.get(domId));
|
||||
if (overflow1.cropX) {
|
||||
rect2.left = Math.max(rect2.left, overflow1.rect.left);
|
||||
rect2.right = Math.min(rect2.right, overflow1.rect.right);
|
||||
}
|
||||
if (overflow1.cropY) {
|
||||
rect2.top = Math.max(rect2.top, overflow1.rect.top);
|
||||
rect2.bottom = Math.min(rect2.bottom, overflow1.rect.bottom);
|
||||
}
|
||||
}
|
||||
if (!rect2.contains(clickPoint.x, clickPoint.y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//Log.d(TAG, "---childId = " + childId + ", domInfo = " + domInfo);
|
||||
if ("none".equals(domInfo.getString("pointerEvents"))) {
|
||||
continue;
|
||||
}
|
||||
//Log.d(TAG, "----childId = " + childId + ", maxZindex = " + maxZindex + ", zIndexValue = " + zIndexValue);
|
||||
if (maxZindex < zIndexValue) {
|
||||
maxZindex = zIndexValue;
|
||||
maxDomId = childId;
|
||||
//Log.i(TAG, "-----current = " + childId);
|
||||
}
|
||||
} else {
|
||||
if (zIndexValue < maxZindex) {
|
||||
//Log.i(TAG, "-----skip because " + childId + " is " + zIndexValue + " < " + maxZindex);
|
||||
continue;
|
||||
}
|
||||
grandChildId = findClickedDom(childId, clickPoint, isMapChild, overflow1);
|
||||
if (grandChildId == null) {
|
||||
grandChildId = childId;
|
||||
} else {
|
||||
zIndexValue = HTMLNodes.get(grandChildId).getBundle("zIndex").getInt("z");
|
||||
}
|
||||
rect = HTMLNodeRectFs.get(grandChildId);
|
||||
//rect2 = new RectF(rect);
|
||||
//Log.d(TAG, "---findClickedDom("+ childId + ") -> " + grandChildId + ",rect = " + rect2.toShortString());
|
||||
/*
|
||||
if (overflow1 != null && !"root".equals(domId) ) {
|
||||
overflow1.rect = new RectF(HTMLNodeRectFs.get(domId));
|
||||
if (overflow1.cropX) {
|
||||
rect2.left = Math.max(rect2.left, overflow1.rect.left);
|
||||
rect2.right = Math.min(rect2.right, overflow1.rect.right);
|
||||
}
|
||||
if (overflow1.cropY) {
|
||||
rect2.top = Math.max(rect2.top, overflow1.rect.top);
|
||||
rect2.bottom = Math.min(rect2.bottom, overflow1.rect.bottom);
|
||||
}
|
||||
//Log.d(TAG, "---overflow(" + domId + ") -> " + overflow1.rect.toShortString() + ", \n"+
|
||||
// " str=" + HTMLNodeRectFs.get(domId) + ",\n" +
|
||||
// " overflowX = " + overflowX + ", overflowY = " + overflowY +
|
||||
// " cropX = " + overflow1.cropX + ", cropY = " + overflow1.cropY);
|
||||
}
|
||||
*/
|
||||
if (!rect.contains(clickPoint.x, clickPoint.y)) {
|
||||
//Log.e(TAG, "----the click point is not in this element(" +grandChildId + "), rect = " + rect2.toShortString() + ", click = " + clickPoint.x + "," + clickPoint.y );
|
||||
continue;
|
||||
}
|
||||
|
||||
domInfo = HTMLNodes.get(grandChildId);
|
||||
//Log.d(TAG, "----grandChildId = " + grandChildId + ", domInfo = " + domInfo);
|
||||
if ("none".equals(domInfo.getString("pointerEvents"))) {
|
||||
//Log.e(TAG, "-----grandChildId(" + grandChildId + ") is " + domInfo.getString("pointerEvents"));
|
||||
continue;
|
||||
}
|
||||
//Log.d(TAG, "----grandChildId = " + grandChildId + ", maxZindex = " + maxZindex + ", zIndexValue = " + zIndexValue);
|
||||
if (maxZindex < zIndexValue) {
|
||||
maxZindex = zIndexValue;
|
||||
maxDomId = grandChildId;
|
||||
//Log.i(TAG, "-----current = " + grandChildId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxDomId == null && !"root".equals(domId)) {
|
||||
if ("none".equals(pointerEvents)) {
|
||||
CACHE_FIND_DOM.put(domId, "(null)");
|
||||
return null;
|
||||
}
|
||||
rect = HTMLNodeRectFs.get(domId);
|
||||
rect2 = new RectF(rect.left, rect.top, rect.right, rect.bottom);
|
||||
// if (overflow1 != null ) {
|
||||
// overflow1.rect = new RectF(HTMLNodeRectFs.get(domId));
|
||||
// if (overflow1.cropX) {
|
||||
// rect2.left = Math.max(rect2.left, overflow1.rect.left);
|
||||
// rect2.right = Math.min(rect2.right, overflow1.rect.right);
|
||||
// }
|
||||
// if (overflow1.cropY) {
|
||||
// rect2.top = Math.max(rect2.top, overflow1.rect.top);
|
||||
// rect2.bottom = Math.min(rect2.bottom, overflow1.rect.bottom);
|
||||
// }
|
||||
// }
|
||||
if (!rect2.contains(clickPoint.x, clickPoint.y)) {
|
||||
//Log.e(TAG, "--the click point is not in this element(" +domId + "), rect = " + rect2.toShortString() + ", click = " + clickPoint.x + "," + clickPoint.y );
|
||||
CACHE_FIND_DOM.put(domId, "(null)");
|
||||
return null;
|
||||
}
|
||||
maxDomId = domId;
|
||||
}
|
||||
if (maxDomId == null) {
|
||||
CACHE_FIND_DOM.put(domId, "(null)");
|
||||
} else {
|
||||
CACHE_FIND_DOM.put(domId, maxDomId);
|
||||
}
|
||||
//Log.i(TAG, "-maxDomId = " + maxDomId + ", domId = " + domId);
|
||||
|
||||
return maxDomId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
if (pluginOverlays == null || pluginOverlays.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
MyPluginLayout.this.stopFlag = true;
|
||||
|
||||
int action = event.getAction();
|
||||
//Log.d("FrontLayerLayout", "----> action = " + action + ", isScrolling = " + isScrolling);
|
||||
|
||||
// The scroll action that started in the browser region is end.
|
||||
isScrolling = action != MotionEvent.ACTION_UP && isScrolling;
|
||||
if (isScrolling) {
|
||||
MyPluginLayout.this.stopFlag = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
IPluginView pluginOverlay;
|
||||
Iterator<Map.Entry<String, IPluginView>> iterator = pluginOverlays.entrySet().iterator();
|
||||
Entry<String, IPluginView> entry;
|
||||
|
||||
PointF clickPoint = new PointF(event.getX(), event.getY());
|
||||
|
||||
RectF drawRect;
|
||||
|
||||
synchronized (_lockHtmlNodes) {
|
||||
CACHE_FIND_DOM.clear();
|
||||
|
||||
String clickedDomId = findClickedDom("root", clickPoint, false, null);
|
||||
//Log.d(TAG, "----clickedDomId = " + clickedDomId);
|
||||
while (iterator.hasNext()) {
|
||||
entry = iterator.next();
|
||||
pluginOverlay = entry.getValue();
|
||||
|
||||
//-----------------------
|
||||
// Is the map clickable?
|
||||
//-----------------------
|
||||
if (!pluginOverlay.getVisible() || !pluginOverlay.getClickable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pluginOverlay.getDivId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//------------------------------------------------
|
||||
// Is the clicked point is in the map rectangle?
|
||||
//------------------------------------------------
|
||||
drawRect = HTMLNodeRectFs.get(pluginOverlay.getDivId());
|
||||
if (drawRect == null || !drawRect.contains(clickPoint.x, clickPoint.y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pluginOverlay.getDivId().equals(clickedDomId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
isScrolling = (action == MotionEvent.ACTION_DOWN) || isScrolling;
|
||||
|
||||
//browserView.requestFocus(View.FOCUS_DOWN);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (isSuspended || HTMLNodes.isEmpty() || !isDebug) {
|
||||
return;
|
||||
}
|
||||
|
||||
IPluginView pluginOverlay;
|
||||
Iterator<Map.Entry<String, IPluginView>> iterator = pluginOverlays.entrySet().iterator();
|
||||
Entry<String, IPluginView> entry;
|
||||
RectF mapRect;
|
||||
synchronized (_lockHtmlNodes) {
|
||||
while (iterator.hasNext()) {
|
||||
entry = iterator.next();
|
||||
pluginOverlay = entry.getValue();
|
||||
if (pluginOverlay.getDivId() == null) {
|
||||
continue;
|
||||
}
|
||||
mapRect = HTMLNodeRectFs.get(pluginOverlay.getDivId());
|
||||
|
||||
debugPaint.setColor(Color.argb(100, 0, 255, 0));
|
||||
canvas.drawRect(mapRect, debugPaint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private class TouchableWrapper extends FrameLayout {
|
||||
|
||||
public TouchableWrapper(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
int action = event.getAction();
|
||||
if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP) {
|
||||
scrollView.requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.util.LruCache;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class ObjectCache {
|
||||
public final HashSet<String> keys = new HashSet<String>();
|
||||
private LruCache<String, Object> objects = new LruCache<String, Object>(1024 * 1024 * 10);
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return keys.contains(key);
|
||||
}
|
||||
|
||||
public void put(String key, Object object) {
|
||||
keys.add(key);
|
||||
objects.put(key, object);
|
||||
}
|
||||
|
||||
public Object remove(String key) {
|
||||
keys.remove(key);
|
||||
return objects.remove(key);
|
||||
}
|
||||
|
||||
public Object get(String key) {
|
||||
return objects.get(key);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return objects.size();
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
for (String key : keys) {
|
||||
objects.remove(key);
|
||||
}
|
||||
keys.clear();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
objects.evictAll();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
public interface PluginAsyncInterface {
|
||||
public void onPostExecute(Object object);
|
||||
public void onError(String errorMsg);
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
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.android.gms.maps.model.LatLngBounds;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class PluginCircle extends MyPlugin implements MyPluginInterface {
|
||||
|
||||
private String circleHashCode;
|
||||
|
||||
/**
|
||||
* Create circle
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final CircleOptions circleOptions = new CircleOptions();
|
||||
int color;
|
||||
final JSONObject properties = new JSONObject();
|
||||
|
||||
JSONObject opts = args.getJSONObject(1);
|
||||
final String hashCode = args.getString(2);
|
||||
circleHashCode = hashCode;
|
||||
|
||||
if (opts.has("center")) {
|
||||
JSONObject center = opts.getJSONObject("center");
|
||||
circleOptions.center(new LatLng(center.getDouble("lat"), center.getDouble("lng")));
|
||||
}
|
||||
if (opts.has("radius")) {
|
||||
circleOptions.radius(opts.getDouble("radius"));
|
||||
}
|
||||
if (opts.has("strokeColor")) {
|
||||
color = PluginUtil.parsePluginColor(opts.getJSONArray("strokeColor"));
|
||||
circleOptions.strokeColor(color);
|
||||
}
|
||||
if (opts.has("fillColor")) {
|
||||
color = PluginUtil.parsePluginColor(opts.getJSONArray("fillColor"));
|
||||
circleOptions.fillColor(color);
|
||||
}
|
||||
if (opts.has("strokeWidth")) {
|
||||
circleOptions.strokeWidth((int)(opts.getDouble("strokeWidth") * density));
|
||||
}
|
||||
if (opts.has("visible")) {
|
||||
circleOptions.visible(opts.getBoolean("visible"));
|
||||
}
|
||||
if (opts.has("zIndex")) {
|
||||
circleOptions.zIndex(opts.getInt("zIndex"));
|
||||
}
|
||||
if (opts.has("clickable")) {
|
||||
properties.put("isClickable", opts.getBoolean("clickable"));
|
||||
} else {
|
||||
properties.put("isClickable", true);
|
||||
}
|
||||
properties.put("isVisible", circleOptions.isVisible());
|
||||
|
||||
// Since this plugin provide own click detection,
|
||||
// disable default clickable feature.
|
||||
circleOptions.clickable(false);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Circle circle = map.addCircle(circleOptions);
|
||||
circle.setTag(hashCode);
|
||||
pluginMap.objects.put("circle_" + hashCode, circle);
|
||||
|
||||
pluginMap.objects.put("circle_property_" + hashCode, properties);
|
||||
|
||||
// Recalculate the circle bounds
|
||||
LatLngBounds bounds = PluginUtil.getBoundsFromCircle(circleOptions.getCenter(), circleOptions.getRadius());
|
||||
pluginMap.objects.put("circle_bounds_" + hashCode, bounds);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("hashCode", hashCode);
|
||||
result.put("__pgmId", "circle_" + hashCode);
|
||||
callbackContext.success(result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error(e.getMessage() + "");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set center
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setCenter(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final LatLng center = new LatLng(args.getDouble(1), args.getDouble(2));
|
||||
final Circle circle = this.getCircle(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Recalculate the circle bounds
|
||||
String propertyId = "circle_bounds_" + circleHashCode;
|
||||
LatLngBounds bounds = PluginUtil.getBoundsFromCircle(circle.getCenter(), circle.getRadius());
|
||||
pluginMap.objects.put(propertyId, bounds);
|
||||
|
||||
circle.setCenter(center);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* set fill color
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setFillColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
int color = PluginUtil.parsePluginColor(args.getJSONArray(1));
|
||||
this.setInt("setFillColor", id, color, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set stroke color
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setStrokeColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
int color = PluginUtil.parsePluginColor(args.getJSONArray(1));
|
||||
this.setInt("setStrokeColor", id, color, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set stroke width
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setStrokeWidth(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float width = (float)(args.getDouble(1) * density);
|
||||
this.setFloat("setStrokeWidth", id, width, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set radius
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setRadius(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final float radius = (float) args.getDouble(1);
|
||||
final Circle circle = this.getCircle(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
// Recalculate the circle bounds
|
||||
String propertyId = "circle_bounds_" + circleHashCode;
|
||||
LatLngBounds bounds = PluginUtil.getBoundsFromCircle(circle.getCenter(), circle.getRadius());
|
||||
pluginMap.objects.put(propertyId, bounds);
|
||||
|
||||
// Update the overlay
|
||||
circle.setRadius(radius);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* set z-index
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float zIndex = (float) args.getDouble(1);
|
||||
this.setFloat("setZIndex", id, zIndex, callbackContext);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set visibility for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean isVisible = args.getBoolean(1);
|
||||
|
||||
final Circle circle = this.getCircle(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
circle.setVisible(isVisible);
|
||||
}
|
||||
});
|
||||
String propertyId = "circle_property_" + circleHashCode;
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isVisible", isVisible);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clickable for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean clickable = args.getBoolean(1);
|
||||
String propertyId = id.replace("circle_", "circle_property_");
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isClickable", clickable);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
/**
|
||||
* Remove the circle
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final String id = args.getString(0);
|
||||
final Circle circle = this.getCircle(id);
|
||||
if (circle == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
circle.remove();
|
||||
if (pluginMap.objects != null) {
|
||||
pluginMap.objects.remove(id);
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginManager;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class PluginEnvironment extends CordovaPlugin {
|
||||
public PluginManager pluginManager;
|
||||
|
||||
@Override
|
||||
public void initialize(final CordovaInterface cordova, final CordovaWebView webView) {
|
||||
pluginManager = webView.getPluginManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) {
|
||||
cordova.getThreadPool().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Method method = PluginEnvironment.this.getClass().getDeclaredMethod(action, JSONArray.class, CallbackContext.class);
|
||||
if (!method.isAccessible()) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
method.invoke(PluginEnvironment.this, args, callbackContext);
|
||||
} catch (Exception e) {
|
||||
Log.e("CordovaLog", "An error occurred", e);
|
||||
callbackContext.error(e.toString());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void isAvailable(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
// ------------------------------
|
||||
// Check of Google Play Services
|
||||
// ------------------------------
|
||||
int checkGooglePlayServices =
|
||||
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(cordova.getActivity());
|
||||
if (checkGooglePlayServices != ConnectionResult.SUCCESS) {
|
||||
String errorMsg = GoogleApiAvailability.getInstance().getErrorString(checkGooglePlayServices);
|
||||
callbackContext.error(errorMsg);
|
||||
|
||||
try {
|
||||
cordova.getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.google.android.gms")));
|
||||
} catch (android.content.ActivityNotFoundException anfe) {
|
||||
cordova.getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=com.google.android.gms")));
|
||||
}
|
||||
|
||||
// End the app (in order to prevent lots of crashes)
|
||||
cordova.getActivity().finish();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Check of Google Maps Android API v2
|
||||
// ------------------------------
|
||||
try {
|
||||
@SuppressWarnings({ "rawtypes" })
|
||||
Class GoogleMapsClass = Class.forName("com.google.android.gms.maps.GoogleMap");
|
||||
} catch (Exception e) {
|
||||
Log.e("GoogleMaps", "Error", e);
|
||||
callbackContext.error(e.getMessage());
|
||||
return;
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set the app background
|
||||
* @param args Parameters given from JavaScript side
|
||||
* @param callbackContext Callback contect for sending back the result.
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setBackGroundColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
JSONArray rgba = args.getJSONArray(0);
|
||||
int backgroundColor = Color.WHITE;
|
||||
|
||||
if (rgba != null && rgba.length() == 4) {
|
||||
backgroundColor = PluginUtil.parsePluginColor(rgba);
|
||||
}
|
||||
final int finalBackgroundColor = backgroundColor;
|
||||
|
||||
|
||||
final CordovaGoogleMaps googleMaps = (CordovaGoogleMaps) pluginManager.getPlugin("CordovaGoogleMaps");
|
||||
|
||||
Handler handler = new Handler(cordova.getActivity().getMainLooper());
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
googleMaps.mPluginLayout.setBackgroundColor(finalBackgroundColor);
|
||||
callbackContext.success();
|
||||
}
|
||||
}, googleMaps.initialized ? 0 : 250);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Boolean getLicenseInfo(JSONArray args, final CallbackContext callbackContext) {
|
||||
callbackContext.success("Google Maps Android API v2 does not need this method anymore. But for iOS, you still need to display the lincense.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Boolean setEnv(JSONArray args, final CallbackContext callbackContext) {
|
||||
// stub
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.location.Address;
|
||||
import android.location.Geocoder;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.gms.maps.model.LatLngBounds;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class PluginGeocoder extends CordovaPlugin {
|
||||
|
||||
// In order to prevent the TOO_MANY_REQUEST_ERROR (block by Google because too many request in short period),
|
||||
// restricts the number of parallel threads.
|
||||
//
|
||||
// According from my tests, 5 threads are the best setting.
|
||||
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
|
||||
private static Geocoder geocoder = null;
|
||||
|
||||
|
||||
public void initialize(CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
if (geocoder == null) {
|
||||
geocoder = new Geocoder(cordova.getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) {
|
||||
executorService.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if ("geocode".equals(action)) {
|
||||
PluginGeocoder.this.geocode(args, callbackContext);
|
||||
} else {
|
||||
callbackContext.error("Method: Geocoder." + action + "() is not found.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error(e.getMessage() + "");
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void geocode(final JSONArray args,
|
||||
final CallbackContext callbackContext) throws JSONException, IOException {
|
||||
|
||||
JSONObject opts = args.getJSONObject(0);
|
||||
List<Address> geoResults = null;
|
||||
JSONArray results = new JSONArray();
|
||||
Iterator<Address> iterator = null;
|
||||
int retryLimit = 10;
|
||||
|
||||
// Geocoding
|
||||
if (!opts.has("position") && opts.has("address")) {
|
||||
String address = opts.getString("address");
|
||||
if (opts.has("bounds")) {
|
||||
if (opts.has("bounds")) {
|
||||
JSONArray points = opts.getJSONArray("bounds");
|
||||
LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points);
|
||||
|
||||
boolean retry = true;
|
||||
while (retry && retryLimit > 0) {
|
||||
retry = false;
|
||||
try {
|
||||
geoResults = geocoder.getFromLocationName(address, 5,
|
||||
bounds.southwest.latitude, bounds.southwest.longitude,
|
||||
bounds.northeast.latitude, bounds.northeast.longitude);
|
||||
} catch (IOException e) {
|
||||
if ("Timed out waiting for response from server".equals(e.getMessage())) {
|
||||
retry = true;
|
||||
retryLimit--;
|
||||
try {
|
||||
Thread.sleep((int)(150 + Math.random() * 100)); // wait (150 + random)ms before retry
|
||||
} catch (InterruptedException e1) {
|
||||
//e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (geoResults.size() == 0) {
|
||||
JSONObject methodResult = new JSONObject();
|
||||
methodResult.put("idx", opts.getInt("idx"));
|
||||
methodResult.put("results", new JSONArray());
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
|
||||
return;
|
||||
}
|
||||
iterator = geoResults.iterator();
|
||||
}
|
||||
} else {
|
||||
|
||||
boolean retry = true;
|
||||
while (retry) {
|
||||
retry = false;
|
||||
try {
|
||||
geoResults = geocoder.getFromLocationName(address, 5);
|
||||
} catch (IOException e) {
|
||||
if ("Timed out waiting for response from server".equals(e.getMessage())) {
|
||||
retry = true;
|
||||
try {
|
||||
Thread.sleep((int)(150 + Math.random() * 100)); // wait (150 + random)ms before retry
|
||||
} catch (InterruptedException e1) {
|
||||
//e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (geoResults == null || geoResults.size() == 0) {
|
||||
JSONObject methodResult = new JSONObject();
|
||||
methodResult.put("idx", opts.getInt("idx"));
|
||||
methodResult.put("results", new JSONArray());
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
|
||||
return;
|
||||
}
|
||||
iterator = geoResults.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse geocoding
|
||||
if (opts.has("position") && !opts.has("address")) {
|
||||
JSONObject position = opts.getJSONObject("position");
|
||||
|
||||
boolean retry = true;
|
||||
while (retry && retryLimit > 0) {
|
||||
retry = false;
|
||||
try {
|
||||
geoResults = geocoder.getFromLocation(
|
||||
position.getDouble("lat"),
|
||||
position.getDouble("lng"), 5);
|
||||
} catch (IOException e) {
|
||||
if ("Timed out waiting for response from server".equals(e.getMessage())) {
|
||||
retry = true;
|
||||
retryLimit--;
|
||||
try {
|
||||
Thread.sleep((int)(150 + Math.random() * 100)); // wait (150 + random)ms before retry
|
||||
} catch (InterruptedException e1) {
|
||||
//e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (geoResults == null || geoResults.size() == 0) {
|
||||
//-------------
|
||||
// No results
|
||||
//-------------
|
||||
JSONObject methodResult = new JSONObject();
|
||||
methodResult.put("idx", opts.getInt("idx"));
|
||||
methodResult.put("results", new JSONArray());
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
|
||||
return;
|
||||
}
|
||||
iterator = geoResults.iterator();
|
||||
}
|
||||
|
||||
if (iterator == null) {
|
||||
callbackContext.error( "Invalid request for geocoder");
|
||||
return;
|
||||
}
|
||||
|
||||
while(iterator.hasNext()) {
|
||||
JSONObject result = new JSONObject();
|
||||
Address addr = iterator.next();
|
||||
|
||||
JSONObject position = new JSONObject();
|
||||
position.put("lat", addr.getLatitude());
|
||||
position.put("lng", addr.getLongitude());
|
||||
result.put("position", position);
|
||||
|
||||
result.put("locality", addr.getLocality());
|
||||
result.put("adminArea", addr.getAdminArea());
|
||||
result.put("country", addr.getCountryName());
|
||||
result.put("countryCode", addr.getCountryCode());
|
||||
result.put("locale", addr.getLocale());
|
||||
result.put("postalCode", addr.getPostalCode());
|
||||
result.put("subAdminArea", addr.getSubAdminArea());
|
||||
result.put("subLocality", addr.getSubLocality());
|
||||
result.put("subThoroughfare", addr.getSubThoroughfare());
|
||||
result.put("thoroughfare", addr.getThoroughfare());
|
||||
|
||||
|
||||
JSONObject extra = new JSONObject();
|
||||
extra.put("featureName", addr.getFeatureName());
|
||||
extra.put("phone", addr.getPhone());
|
||||
extra.put("permises", addr.getPremises());
|
||||
extra.put("url", addr.getUrl());
|
||||
|
||||
JSONArray lines = new JSONArray();
|
||||
// Handled cases where we have only one line address.
|
||||
for (int i = 0; i <= addr.getMaxAddressLineIndex(); i++) {
|
||||
lines.put(addr.getAddressLine(i));
|
||||
}
|
||||
extra.put("lines", lines);
|
||||
|
||||
|
||||
Bundle extraInfo = addr.getExtras();
|
||||
if (extraInfo != null) {
|
||||
Set<String> keys = extraInfo.keySet();
|
||||
Iterator<String> keyIterator = keys.iterator();
|
||||
String key;
|
||||
while(keyIterator.hasNext()) {
|
||||
key = keyIterator.next();
|
||||
extra.put(key, extraInfo.get(key));
|
||||
}
|
||||
}
|
||||
result.put("extra", extra);
|
||||
results.put(result);
|
||||
}
|
||||
|
||||
JSONObject methodResult = new JSONObject();
|
||||
methodResult.put("idx", opts.getInt("idx"));
|
||||
methodResult.put("results", results);
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, methodResult);
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,661 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
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.LatLngBounds;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
public class PluginGroundOverlay extends MyPlugin implements MyPluginInterface {
|
||||
|
||||
private HashMap<Integer, AsyncTask> imageLoadingTasks = new HashMap<Integer, AsyncTask>();
|
||||
private final Object semaphore = new Object();
|
||||
private HashMap<String, Bitmap> overlayImage = new HashMap<String, Bitmap>();
|
||||
private boolean _clearDone = false;
|
||||
|
||||
@Override
|
||||
public void initialize(final CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ground overlay
|
||||
*
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void create(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
JSONObject opts = args.getJSONObject(1);
|
||||
String hashCode = args.getString(2);
|
||||
_createGroundOverlay(hashCode, opts, callbackContext);
|
||||
}
|
||||
|
||||
public void _createGroundOverlay(final String idBase, final JSONObject opts, final CallbackContext callbackContext) throws JSONException {
|
||||
final GroundOverlayOptions options = new GroundOverlayOptions();
|
||||
final JSONObject properties = new JSONObject();
|
||||
options.anchor(0.5f, 0.5f);
|
||||
|
||||
if (opts.has("anchor")) {
|
||||
JSONArray anchor = opts.getJSONArray("anchor");
|
||||
options.anchor((float)anchor.getDouble(0), (float)anchor.getDouble(1));
|
||||
}
|
||||
if (opts.has("bearing")) {
|
||||
options.bearing((float)opts.getDouble("bearing"));
|
||||
}
|
||||
if (opts.has("opacity")) {
|
||||
options.transparency(1 - (float)opts.getDouble("opacity"));
|
||||
}
|
||||
if (opts.has("zIndex")) {
|
||||
options.zIndex((float)opts.getDouble("zIndex"));
|
||||
}
|
||||
if (opts.has("visible")) {
|
||||
options.visible(opts.getBoolean("visible"));
|
||||
}
|
||||
if (opts.has("bounds")) {
|
||||
JSONArray points = opts.getJSONArray("bounds");
|
||||
LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points);
|
||||
options.positionFromBounds(bounds);
|
||||
}
|
||||
if (opts.has("clickable")) {
|
||||
properties.put("isClickable", opts.getBoolean("clickable"));
|
||||
} else {
|
||||
properties.put("isClickable", true);
|
||||
}
|
||||
properties.put("isVisible", options.isVisible());
|
||||
|
||||
// Since this plugin provide own click detection,
|
||||
// disable default clickable feature.
|
||||
options.clickable(false);
|
||||
|
||||
// Load image
|
||||
final String imageUrl = opts.getString("url");
|
||||
|
||||
setImage_(imageUrl, new PluginAsyncInterface() {
|
||||
|
||||
@Override
|
||||
public void onPostExecute(Object object) {
|
||||
if (object == null) {
|
||||
callbackContext.error("Cannot create a ground overlay");
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncLoadImage.AsyncLoadImageResult result = (AsyncLoadImage.AsyncLoadImageResult)object;
|
||||
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(result.image);
|
||||
options.image(bitmapDescriptor);
|
||||
//options.zIndex(Calendar.getInstance().getTimeInMillis());
|
||||
GroundOverlay groundOverlay = self.map.addGroundOverlay(options);
|
||||
overlayImage.put("groundoverlay_" + idBase, result.image);
|
||||
|
||||
groundOverlay.setTag(idBase);
|
||||
|
||||
pluginMap.objects.put("groundoverlay_" + idBase, groundOverlay);
|
||||
|
||||
pluginMap.objects.put("groundoverlay_bounds_" + idBase, groundOverlay.getBounds());
|
||||
|
||||
pluginMap.objects.put("groundoverlay_property_" + idBase, properties);
|
||||
|
||||
pluginMap.objects.put("groundoverlay_initOpts_" + idBase, opts);
|
||||
|
||||
|
||||
JSONObject resultJSON = new JSONObject();
|
||||
try {
|
||||
resultJSON.put("hashCode", idBase);
|
||||
resultJSON.put("__pgmId", "groundoverlay_" + idBase);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.success(resultJSON);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMsg) {
|
||||
callbackContext.error(errorMsg);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
_clearDone = false;
|
||||
synchronized (semaphore) {
|
||||
|
||||
//--------------------------------------
|
||||
// clean up properties as much as possible
|
||||
//--------------------------------------
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Set<String> keySet = pluginMap.objects.keys;
|
||||
if (keySet.size() > 0) {
|
||||
String[] objectIdArray = keySet.toArray(new String[keySet.size()]);
|
||||
|
||||
synchronized (pluginMap.objects) {
|
||||
Bitmap image;
|
||||
for (String objectId : objectIdArray) {
|
||||
if (pluginMap.objects.containsKey(objectId)) {
|
||||
if (objectId.startsWith("groundoverlay_") &&
|
||||
!objectId.startsWith("groundoverlay_property_") &&
|
||||
!objectId.startsWith("groundoverlay_initOpts_") &&
|
||||
!objectId.startsWith("groundoverlay_bounds_")) {
|
||||
GroundOverlay groundOverlay = (GroundOverlay) pluginMap.objects.remove(objectId);
|
||||
image = overlayImage.remove(objectId);
|
||||
if (image != null && !image.isRecycled()) {
|
||||
image.recycle();
|
||||
}
|
||||
groundOverlay.setTag(null);
|
||||
groundOverlay.remove();
|
||||
groundOverlay = null;
|
||||
} else {
|
||||
Object object = pluginMap.objects.remove(objectId);
|
||||
object = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (semaphore) {
|
||||
_clearDone = true;
|
||||
semaphore.notify();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
Set<String> keySet = pluginMap.objects.keys;
|
||||
if (keySet.size() > 0) {
|
||||
try {
|
||||
if (!_clearDone) {
|
||||
semaphore.wait(1000);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// ignore
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this tile layer
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void remove(JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final String id = args.getString(0);
|
||||
final GroundOverlay groundOverlay = (GroundOverlay)pluginMap.objects.get(id);
|
||||
if (groundOverlay == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (pluginMap.objects) {
|
||||
pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_property_"));
|
||||
pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_initOpts_"));
|
||||
pluginMap.objects.remove(id.replace("groundoverlay_", "groundoverlay_bounds_"));
|
||||
}
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (pluginMap.objects) {
|
||||
GroundOverlay groundOverlay = (GroundOverlay) pluginMap.objects.remove(id);
|
||||
if (groundOverlay != null) {
|
||||
Bitmap image = overlayImage.remove(id);
|
||||
if (image != null && !image.isRecycled()) {
|
||||
image.recycle();
|
||||
}
|
||||
groundOverlay.remove();
|
||||
}
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set image of the ground-overlay
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setImage(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final String id = args.getString(0);
|
||||
final String url = args.getString(1);
|
||||
final String idBase = id.replace("groundoverlay_", "");
|
||||
|
||||
String propertyId = "groundoverlay_initOpts_" + idBase;
|
||||
final JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId);
|
||||
opts.put("url", url);
|
||||
pluginMap.objects.put(propertyId, opts);
|
||||
|
||||
setImage_(url, new PluginAsyncInterface() {
|
||||
@Override
|
||||
public void onPostExecute(Object object) {
|
||||
if (object == null) {
|
||||
callbackContext.error("[error]groundoverlay.setImage(" + url + ")");
|
||||
return;
|
||||
}
|
||||
AsyncLoadImage.AsyncLoadImageResult result = (AsyncLoadImage.AsyncLoadImageResult) object;
|
||||
GroundOverlay groundOverlay = getGroundOverlay(id);
|
||||
if (groundOverlay != null) {
|
||||
Bitmap currentBmp = overlayImage.remove(id);
|
||||
if (currentBmp != null) {
|
||||
currentBmp.recycle();
|
||||
}
|
||||
}
|
||||
if (result.image != null) {
|
||||
overlayImage.put(id, result.image);
|
||||
groundOverlay.setImage(BitmapDescriptorFactory.fromBitmap(result.image));
|
||||
callbackContext.success();
|
||||
} else {
|
||||
callbackContext.error("[error]groundoverlay.setImage(" + url + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMsg) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set bounds
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setBounds(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final GroundOverlay groundOverlay = (GroundOverlay)pluginMap.objects.get(id);
|
||||
|
||||
String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_");
|
||||
JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId);
|
||||
|
||||
JSONArray points = args.getJSONArray(1);
|
||||
opts.put("bounds", points);
|
||||
pluginMap.objects.put(propertyId, opts);
|
||||
|
||||
final LatLngBounds bounds = PluginUtil.JSONArray2LatLngBounds(points);
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
groundOverlay.setPositionFromBounds(bounds);
|
||||
}
|
||||
});
|
||||
|
||||
String boundsId = id.replace("groundoverlay_", "groundoverlay_bounds_");
|
||||
pluginMap.objects.put(boundsId, bounds);
|
||||
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set opacity
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setOpacity(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
float opacity = (float)args.getDouble(1);
|
||||
String id = args.getString(0);
|
||||
|
||||
String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_");
|
||||
JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId);
|
||||
opts.put("opacity", opacity);
|
||||
pluginMap.objects.put(propertyId, opts);
|
||||
|
||||
this.setFloat("setTransparency", id, 1 - opacity, callbackContext);
|
||||
}
|
||||
/**
|
||||
* Set bearing
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setBearing(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
float bearing = (float)args.getDouble(1);
|
||||
String id = args.getString(0);
|
||||
|
||||
String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_");
|
||||
JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId);
|
||||
opts.put("bearing", bearing);
|
||||
pluginMap.objects.put(propertyId, opts);
|
||||
|
||||
this.setFloat("setBearing", id, bearing, callbackContext);
|
||||
}
|
||||
/**
|
||||
* set z-index
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float zIndex = (float) args.getDouble(1);
|
||||
|
||||
String propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_");
|
||||
JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId);
|
||||
opts.put("zIndex", zIndex);
|
||||
pluginMap.objects.put(propertyId, opts);
|
||||
|
||||
this.setFloat("setZIndex", id, zIndex, callbackContext);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set visibility for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean isVisible = args.getBoolean(1);
|
||||
|
||||
final GroundOverlay groundOverlay = this.getGroundOverlay(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
groundOverlay.setVisible(isVisible);
|
||||
}
|
||||
});
|
||||
String propertyId = id.replace("groundoverlay_", "groundoverlay_property_");
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isVisible", isVisible);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
|
||||
propertyId = id.replace("groundoverlay_", "groundoverlay_initOpts_");
|
||||
JSONObject opts = (JSONObject) pluginMap.objects.get(propertyId);
|
||||
opts.put("visible", isVisible);
|
||||
pluginMap.objects.put(propertyId, opts);
|
||||
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clickable for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean clickable = args.getBoolean(1);
|
||||
String propertyId = id.replace("groundoverlay_", "groundoverlay_property_");
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isClickable", clickable);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
|
||||
private void setImage_(final String imgUrl, final PluginAsyncInterface callback) {
|
||||
if (imgUrl == null) {
|
||||
callback.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final AsyncLoadImage.AsyncLoadImageOptions imageOptions = new AsyncLoadImage.AsyncLoadImageOptions();
|
||||
imageOptions.height = -1;
|
||||
imageOptions.width = -1;
|
||||
imageOptions.noCaching = true;
|
||||
imageOptions.url = imgUrl;
|
||||
final int taskId = imageOptions.hashCode();
|
||||
|
||||
final AsyncLoadImageInterface onComplete = new AsyncLoadImageInterface() {
|
||||
|
||||
@Override
|
||||
public void onPostExecute(AsyncLoadImage.AsyncLoadImageResult result) {
|
||||
if (result == null || result.image == null) {
|
||||
callback.onError("Can not read image from " + imgUrl);
|
||||
imageLoadingTasks.remove(taskId).cancel(true);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.onPostExecute(result);
|
||||
|
||||
imageLoadingTasks.remove(taskId).cancel(true);
|
||||
}
|
||||
};
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AsyncLoadImage task = new AsyncLoadImage(cordova, webView, imageOptions, onComplete);
|
||||
//cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// task.execute();
|
||||
// }
|
||||
//});
|
||||
task.execute();
|
||||
imageLoadingTasks.put(taskId, task);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
if (imgUrl == null) {
|
||||
callback.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!imgUrl.contains("://") &&
|
||||
!imgUrl.startsWith("/") &&
|
||||
!imgUrl.startsWith("www/") &&
|
||||
!imgUrl.startsWith("data:image") &&
|
||||
!imgUrl.startsWith("./") &&
|
||||
!imgUrl.startsWith("../")) {
|
||||
imgUrl = "./" + imgUrl;
|
||||
}
|
||||
if (imgUrl.startsWith("./") || imgUrl.startsWith("../")) {
|
||||
imgUrl = imgUrl.replace("././", "./");
|
||||
String currentPage = CURRENT_PAGE_URL;
|
||||
currentPage = currentPage.replaceAll("[^\\/]*$", "");
|
||||
imgUrl = currentPage + "/" + imgUrl;
|
||||
}
|
||||
|
||||
if (imgUrl == null) {
|
||||
callback.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final String imageUrl = imgUrl;
|
||||
if (imageUrl.indexOf("http") != 0) {
|
||||
//----------------------------------
|
||||
// Load img from local file
|
||||
//----------------------------------
|
||||
AsyncTask<Void, Void, Bitmap> task = new AsyncTask<Void, Void, Bitmap>() {
|
||||
|
||||
@Override
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
String imgUrl = imageUrl;
|
||||
if (imgUrl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap image = null;
|
||||
if (imgUrl.indexOf("cdvfile://") == 0) {
|
||||
CordovaResourceApi resourceApi = webView.getResourceApi();
|
||||
imgUrl = PluginUtil.getAbsolutePathFromCDVFilePath(resourceApi, imgUrl);
|
||||
}
|
||||
if (imgUrl == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (imgUrl.indexOf("data:image/") == 0 && imgUrl.contains(";base64,")) {
|
||||
String[] tmp = imgUrl.split(",");
|
||||
image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]);
|
||||
} else if (imgUrl.indexOf("file://") == 0 &&
|
||||
!imgUrl.contains("file:///android_asset/")) {
|
||||
imgUrl = imgUrl.replace("file://", "");
|
||||
File tmp = new File(imgUrl);
|
||||
if (tmp.exists()) {
|
||||
image = BitmapFactory.decodeFile(imgUrl);
|
||||
} else {
|
||||
Log.w(TAG, "image is not found (" + imgUrl + ")");
|
||||
}
|
||||
} else {
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (imgUrl.indexOf("file:///android_asset/") == 0) {
|
||||
imgUrl = imgUrl.replace("file:///android_asset/", "");
|
||||
}
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (imgUrl.contains("./")) {
|
||||
try {
|
||||
boolean isAbsolutePath = imgUrl.startsWith("/");
|
||||
File relativePath = new File(imgUrl);
|
||||
imgUrl = relativePath.getCanonicalPath();
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (!isAbsolutePath) {
|
||||
imgUrl = imgUrl.substring(1);
|
||||
}
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
AssetManager assetManager = PluginGroundOverlay.this.cordova.getActivity().getAssets();
|
||||
InputStream inputStream;
|
||||
try {
|
||||
inputStream = assetManager.open(imgUrl);
|
||||
image = BitmapFactory.decodeStream(inputStream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (image == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap image) {
|
||||
if (image == null) {
|
||||
callback.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
|
||||
GroundOverlay groundOverlay = null;
|
||||
try {
|
||||
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(image);
|
||||
options.image(bitmapDescriptor);
|
||||
groundOverlay = self.map.addGroundOverlay(options);
|
||||
|
||||
callback.onPostExecute(groundOverlay);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG,"PluginGroundOverlay: Warning - ground overlay method is called when ground overlay method has been disposed, wait for addGroundOverlay callback before calling more methods on the groundOverlay.");
|
||||
//e.printStackTrace();
|
||||
try {
|
||||
if (groundOverlay != null) {
|
||||
groundOverlay.remove();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
ignore = null;
|
||||
}
|
||||
callback.onError(e.getMessage() + "");
|
||||
}
|
||||
}
|
||||
};
|
||||
task.execute();
|
||||
imageLoadingTasks.add(task);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (imageUrl.indexOf("http") == 0) {
|
||||
//----------------------------------
|
||||
// Load img from on the internet
|
||||
//----------------------------------
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
AsyncLoadImage task = new AsyncLoadImage(userAgent, width, height, true, new AsyncLoadImageInterface() {
|
||||
|
||||
@Override
|
||||
public void onPostExecute(Bitmap image) {
|
||||
|
||||
if (image == null) {
|
||||
callback.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
GroundOverlay groundOverlay = null;
|
||||
try {
|
||||
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(image);
|
||||
options.image(bitmapDescriptor);
|
||||
groundOverlay = self.map.addGroundOverlay(options);
|
||||
|
||||
image.recycle();
|
||||
callback.onPostExecute(groundOverlay);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
try {
|
||||
if (groundOverlay != null) {
|
||||
groundOverlay.remove();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
ignore = null;
|
||||
}
|
||||
callback.onError(e.getMessage() + "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageUrl);
|
||||
imageLoadingTasks.add(task);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
//--------------------------------------
|
||||
// Cancel tasks
|
||||
//--------------------------------------
|
||||
cordova.getThreadPool().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AsyncTask task;
|
||||
int i, ilen = imageLoadingTasks.size();
|
||||
for (i = 0; i < ilen; i++) {
|
||||
task = imageLoadingTasks.remove(i);
|
||||
task.cancel(true);
|
||||
task = null;
|
||||
}
|
||||
imageLoadingTasks = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.clear();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import za.co.twyst.tbxml.TBXML;
|
||||
|
||||
|
||||
public class PluginKmlOverlay extends MyPlugin implements MyPluginInterface {
|
||||
private HashMap<String, Bundle> styles = new HashMap<String, Bundle>();
|
||||
|
||||
private enum KML_TAG {
|
||||
NOT_SUPPORTED,
|
||||
|
||||
kml,
|
||||
style,
|
||||
styleurl,
|
||||
stylemap,
|
||||
schema,
|
||||
coordinates
|
||||
}
|
||||
|
||||
/**
|
||||
* Create kml overlay
|
||||
*
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
final JSONObject opts = args.getJSONObject(1);
|
||||
self = this;
|
||||
if (!opts.has("url")) {
|
||||
callbackContext.error("No kml file is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
String urlStr = null;
|
||||
|
||||
try {
|
||||
urlStr = opts.getString("url");
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (urlStr == null || urlStr.length() == 0) {
|
||||
callbackContext.error("No kml file is specified");
|
||||
return;
|
||||
}
|
||||
|
||||
String currentPageUrl = webView.getUrl();
|
||||
if (!urlStr.contains("://") &&
|
||||
!urlStr.startsWith("/") &&
|
||||
!urlStr.startsWith("www/") &&
|
||||
!urlStr.startsWith("data:image") &&
|
||||
!urlStr.startsWith("./") &&
|
||||
!urlStr.startsWith("../")) {
|
||||
urlStr = "./" + urlStr;
|
||||
}
|
||||
|
||||
if (currentPageUrl.startsWith("http://localhost") ||
|
||||
currentPageUrl.startsWith("http://127.0.0.1")) {
|
||||
if (urlStr.contains("://")) {
|
||||
urlStr = urlStr.replaceAll("http://.+?/", "file:///android_asset/www/");
|
||||
} else {
|
||||
// Avoid WebViewLocalServer (because can not make a connection for some reason)
|
||||
urlStr = "file:///android_asset/www/".concat(urlStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (urlStr.startsWith("./") || urlStr.startsWith("../")) {
|
||||
urlStr = urlStr.replace("././", "./");
|
||||
currentPageUrl = currentPageUrl.replaceAll("[^\\/]*$", "");
|
||||
urlStr = currentPageUrl + "/" + urlStr;
|
||||
}
|
||||
if (urlStr.startsWith("cdvfile://")) {
|
||||
urlStr = PluginUtil.getAbsolutePathFromCDVFilePath(webView.getResourceApi(), urlStr);
|
||||
}
|
||||
|
||||
// Avoid WebViewLocalServer (because can not make a connection for some reason)
|
||||
if (urlStr.contains("http://localhost") || urlStr.contains("http://127.0.0.1")) {
|
||||
urlStr = urlStr.replaceAll("^http://[^\\/]+\\//", "file:///android_asset/www/");
|
||||
}
|
||||
|
||||
|
||||
final String finalUrl = urlStr;
|
||||
executorService.submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Bundle result = loadKml(finalUrl);
|
||||
callbackContext.success(PluginUtil.Bundle2Json(result));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Bundle loadKml(String urlStr) {
|
||||
|
||||
InputStream inputStream = getKmlContents(urlStr);
|
||||
if (inputStream == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// KMZ is not ready yet.... sorry
|
||||
|
||||
// if (urlStr.contains(".kmz")) {
|
||||
// String cacheDirPath = cordova.getActivity().getCacheDir() + "/" + Integer.toString(urlStr.hashCode(), 16);
|
||||
// File cacheDir = new File(cacheDirPath);
|
||||
// if (!cacheDir.exists()) {
|
||||
// cacheDir.mkdirs();
|
||||
// }
|
||||
// ArrayList<File> files = (PluginUtil.unpackZipFromBytes(inputStream, cacheDirPath));
|
||||
// inputStream.close();
|
||||
// for (File file : files) {
|
||||
// if (file.getName().contains(".kml")) {
|
||||
// inputStream = new FileInputStream(file);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
String line;
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
|
||||
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(line);
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
bufferedReader.close();
|
||||
|
||||
|
||||
TBXML tbxml = new TBXML();
|
||||
tbxml.parse(stringBuilder.toString());
|
||||
|
||||
KmlParserClass parser = new KmlParserClass();
|
||||
Bundle root = parser.parseXml(tbxml, tbxml.rootXMLElement());
|
||||
Bundle result = new Bundle();
|
||||
result.putBundle("schemas", parser.schemaHolder);
|
||||
result.putBundle("styles", parser.styleHolder);
|
||||
result.putBundle("root", root);
|
||||
|
||||
|
||||
tbxml.release();
|
||||
inputStreamReader.close();
|
||||
inputStream.close();
|
||||
inputStream = null;
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class KmlParserClass {
|
||||
public Bundle styleHolder = new Bundle();
|
||||
public Bundle schemaHolder = new Bundle();
|
||||
|
||||
|
||||
private Bundle parseXml(TBXML tbxml, long rootElement) {
|
||||
Bundle result = new Bundle();
|
||||
String styleId, schemaId, txt, attrName;
|
||||
int i;
|
||||
String tagName = tbxml.elementName(rootElement).toLowerCase();
|
||||
long childNode;
|
||||
Bundle styles, schema, extendedData;
|
||||
ArrayList<Bundle> children;
|
||||
ArrayList<String> styleIDs;
|
||||
|
||||
//Log.d(TAG, "--->tagName = " + tagName + "(" + rootElement + ")");
|
||||
result.putString("tagName", tagName);
|
||||
|
||||
KML_TAG kmlTag = null;
|
||||
try {
|
||||
kmlTag = KML_TAG.valueOf(tagName);
|
||||
} catch(Exception e) {
|
||||
kmlTag = KML_TAG.NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
long[] attributes = tbxml.listAttributesOfElement(rootElement);
|
||||
for (i = 0; i < attributes.length; i++) {
|
||||
attrName = tbxml.attributeName(attributes[i]);
|
||||
result.putString(attrName, tbxml.attributeValue(attributes[i]));
|
||||
}
|
||||
|
||||
|
||||
switch (kmlTag) {
|
||||
|
||||
case styleurl:
|
||||
styleId = tbxml.textForElement(rootElement);
|
||||
result.putString("styleId", styleId);
|
||||
break;
|
||||
|
||||
case stylemap:
|
||||
case style:
|
||||
|
||||
// Generate a style id for the tag
|
||||
styleId = tbxml.valueOfAttributeNamed("id", rootElement);
|
||||
if (styleId == null || styleId.isEmpty()) {
|
||||
styleId = "__" + rootElement + "__";
|
||||
}
|
||||
result.putString("styleId", styleId);
|
||||
|
||||
// Store style information into the styleHolder.
|
||||
styles = new Bundle();
|
||||
children = new ArrayList<Bundle>();
|
||||
childNode = tbxml.firstChild(rootElement);
|
||||
while (childNode > 0) {
|
||||
Bundle node = this.parseXml(tbxml, childNode);
|
||||
if (node != null) {
|
||||
if (node.containsKey("value")) {
|
||||
styles.putString(node.getString("tagName"), node.getString("value"));
|
||||
} else {
|
||||
children.add(node);
|
||||
}
|
||||
}
|
||||
childNode = tbxml.nextSibling(childNode);
|
||||
}
|
||||
if (children.size() > 0) {
|
||||
styles.putParcelableArrayList("children", children);
|
||||
}
|
||||
styleHolder.putBundle(styleId, styles);
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case schema:
|
||||
|
||||
// Generate a schema id for the tag
|
||||
schemaId = tbxml.valueOfAttributeNamed("id", rootElement);
|
||||
if (schemaId == null || schemaId.isEmpty()) {
|
||||
schemaId = "__" + rootElement + "__";
|
||||
}
|
||||
|
||||
// Store schema information into the schemaHolder.
|
||||
schema = new Bundle();
|
||||
schema.putString("name", tbxml.valueOfAttributeNamed("name", rootElement));
|
||||
children = new ArrayList<Bundle>();
|
||||
childNode = tbxml.firstChild(rootElement);
|
||||
while (childNode > 0) {
|
||||
Bundle node = this.parseXml(tbxml, childNode);
|
||||
if (node != null) {
|
||||
children.add(node);
|
||||
}
|
||||
childNode = tbxml.nextSibling(childNode);
|
||||
}
|
||||
if (children.size() > 0) {
|
||||
schema.putParcelableArrayList("children", children);
|
||||
}
|
||||
schemaHolder.putBundle(schemaId, schema);
|
||||
|
||||
|
||||
break;
|
||||
case coordinates:
|
||||
|
||||
|
||||
ArrayList<Bundle> latLngList = new ArrayList<Bundle>();
|
||||
|
||||
txt = tbxml.textForElement(rootElement);
|
||||
txt = txt.replaceAll("\\s+", "\n");
|
||||
txt = txt.replaceAll("\\n+", "\n");
|
||||
String lines[] = txt.split("\n");
|
||||
String tmpArry[];
|
||||
Bundle latLng;
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
lines[i] = lines[i].replaceAll("[^0-9,.\\-Ee]", "");
|
||||
if (!"".equals(lines[i])) {
|
||||
tmpArry = lines[i].split(",");
|
||||
latLng = new Bundle();
|
||||
latLng.putDouble("lat", Double.parseDouble(tmpArry[1]));
|
||||
latLng.putDouble("lng", Double.parseDouble(tmpArry[0]));
|
||||
latLngList.add(latLng);
|
||||
}
|
||||
}
|
||||
|
||||
result.putParcelableArrayList(tagName, latLngList);
|
||||
break;
|
||||
|
||||
|
||||
|
||||
default:
|
||||
|
||||
childNode = tbxml.firstChild(rootElement);
|
||||
if (childNode != 0) {
|
||||
children = new ArrayList<Bundle>();
|
||||
while (childNode != 0) {
|
||||
Bundle node = this.parseXml(tbxml, childNode);
|
||||
if (node != null) {
|
||||
if (node.containsKey("styleId")) {
|
||||
//--------------------------------------------
|
||||
// writtin in JavaScript
|
||||
// result.styleIDs = result.styleIDs || [];
|
||||
// result.styleIDs.push(node.styleId);
|
||||
//--------------------------------------------
|
||||
styleIDs = result.getStringArrayList("styleIDs");
|
||||
if (styleIDs == null) {
|
||||
styleIDs = new ArrayList<String>();
|
||||
}
|
||||
styleIDs.add(node.getString("styleId"));
|
||||
result.putStringArrayList("styleIDs", styleIDs);
|
||||
} else if (!("schema".equals(node.getString("tagName")))) {
|
||||
children.add(node);
|
||||
}
|
||||
}
|
||||
childNode = tbxml.nextSibling(childNode);
|
||||
}
|
||||
|
||||
result.putParcelableArrayList("children", children);
|
||||
} else {
|
||||
result.putString("value", tbxml.textForElement(rootElement));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private InputStream getKmlContents(String urlStr) {
|
||||
|
||||
InputStream inputStream;
|
||||
try {
|
||||
//Log.d("PluginKmlOverlay", "---> url = " + urlStr);
|
||||
if (urlStr.startsWith("http://") || urlStr.startsWith("https://")) {
|
||||
URL url = new URL(urlStr);
|
||||
boolean redirect = true;
|
||||
HttpURLConnection http = null;
|
||||
String cookies = null;
|
||||
int redirectCnt = 0;
|
||||
while(redirect && redirectCnt < 10) {
|
||||
redirect = false;
|
||||
http = (HttpURLConnection)url.openConnection();
|
||||
http.setRequestMethod("GET");
|
||||
if (cookies != null) {
|
||||
http.setRequestProperty("Cookie", cookies);
|
||||
}
|
||||
http.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
|
||||
http.addRequestProperty("User-Agent", "Mozilla");
|
||||
http.setInstanceFollowRedirects(true);
|
||||
HttpURLConnection.setFollowRedirects(true);
|
||||
|
||||
// normally, 3xx is redirect
|
||||
int status = http.getResponseCode();
|
||||
if (status != HttpURLConnection.HTTP_OK) {
|
||||
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|
||||
|| status == HttpURLConnection.HTTP_MOVED_PERM
|
||||
|| status == HttpURLConnection.HTTP_SEE_OTHER)
|
||||
redirect = true;
|
||||
}
|
||||
if (redirect) {
|
||||
// get redirect url from "location" header field
|
||||
url = new URL(http.getHeaderField("Location"));
|
||||
|
||||
// get the cookie if need, for login
|
||||
cookies = http.getHeaderField("Set-Cookie");
|
||||
|
||||
// Disconnect the current connection
|
||||
http.disconnect();
|
||||
redirectCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
inputStream = http.getInputStream();
|
||||
} else if (urlStr.indexOf("file://") == 0 && !urlStr.contains("file:///android_asset/") ||
|
||||
urlStr.indexOf("/") == 0) {
|
||||
urlStr = urlStr.replace("file://", "");
|
||||
try {
|
||||
boolean isAbsolutePath = urlStr.startsWith("/");
|
||||
File relativePath = new File(urlStr);
|
||||
urlStr = relativePath.getCanonicalPath();
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (!isAbsolutePath) {
|
||||
urlStr = urlStr.substring(1);
|
||||
}
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//Log.d("PluginKmlOverlay", "---> url = " + urlStr);
|
||||
inputStream = new FileInputStream(urlStr);
|
||||
} else {
|
||||
if (urlStr.indexOf("file:///android_asset/") == 0) {
|
||||
urlStr = urlStr.replace("file:///android_asset/", "");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
boolean isAbsolutePath = urlStr.startsWith("/");
|
||||
File relativePath = new File(urlStr);
|
||||
urlStr = relativePath.getCanonicalPath();
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (!isAbsolutePath) {
|
||||
urlStr = urlStr.substring(1);
|
||||
}
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//Log.d("PluginKmlOverlay", "---> url = " + urlStr);
|
||||
inputStream = cordova.getActivity().getResources().getAssets().open(urlStr);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
return inputStream;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.PermissionChecker;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
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.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class PluginLocationService extends CordovaPlugin {
|
||||
private Activity activity;
|
||||
private final String TAG = "PluginLocationService";
|
||||
private HashMap<String, Bundle> bufferForLocationDialog = new HashMap<String, Bundle>();
|
||||
|
||||
private final int ACTIVITY_LOCATION_DIALOG = 0x7f999900; // Invite the location dialog using Google Play Services
|
||||
private final int ACTIVITY_LOCATION_PAGE = 0x7f999901; // Open the location settings page
|
||||
|
||||
private GoogleApiClient googleApiClient = null;
|
||||
|
||||
public void initialize(final CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
activity = cordova.getActivity();
|
||||
}
|
||||
private static Location lastLocation = null;
|
||||
private ArrayList<CallbackContext> regularAccuracyRequestList = new ArrayList<CallbackContext>();
|
||||
private ArrayList<CallbackContext> highAccuracyRequestList = new ArrayList<CallbackContext>();
|
||||
public static final Object semaphore = new Object();
|
||||
|
||||
public static void setLastLocation(Location location) {
|
||||
// Sets the last location if the end user click on the mylocation (blue dot)
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
cordova.getThreadPool().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if ("getMyLocation".equals(action)) {
|
||||
PluginLocationService.this.getMyLocation(args, callbackContext);
|
||||
} else if ("hasPermission".equals(action)) {
|
||||
PluginLocationService.this.hasPermission(args, callbackContext);
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void hasPermission(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
synchronized (semaphore) {
|
||||
// Check geolocation permission.
|
||||
boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity().getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED;
|
||||
callbackContext.success(locationPermission ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void getMyLocation(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
synchronized (semaphore) {
|
||||
|
||||
|
||||
|
||||
LocationManager locationManager = (LocationManager) this.activity.getSystemService(Context.LOCATION_SERVICE);
|
||||
List<String> providers = locationManager.getAllProviders();
|
||||
int availableProviders = 0;
|
||||
//if (mPluginLayout != null && mPluginLayout.isDebug) {
|
||||
Log.d(TAG, "---debug at getMyLocation(available providers)--");
|
||||
//}
|
||||
Iterator<String> iterator = providers.iterator();
|
||||
String provider;
|
||||
boolean isAvailable;
|
||||
while(iterator.hasNext()) {
|
||||
provider = iterator.next();
|
||||
if ("passive".equals(provider)) {
|
||||
continue;
|
||||
}
|
||||
isAvailable = locationManager.isProviderEnabled(provider);
|
||||
if (isAvailable) {
|
||||
availableProviders++;
|
||||
}
|
||||
//if (mPluginLayout != null && mPluginLayout.isDebug) {
|
||||
Log.d(TAG, " " + provider + " = " + (isAvailable ? "" : "not ") + "available");
|
||||
//}
|
||||
}
|
||||
if (availableProviders == 0) {
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("status", false);
|
||||
result.put("error_code", "not_available");
|
||||
result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_no_location_providers"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.error(result);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
JSONObject params = args.getJSONObject(0);
|
||||
boolean requestHighAccuracy = false;
|
||||
if (params.has("enableHighAccuracy")) {
|
||||
requestHighAccuracy = params.getBoolean("enableHighAccuracy");
|
||||
}
|
||||
if (requestHighAccuracy && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("status", false);
|
||||
result.put("error_code", "not_available");
|
||||
result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_no_location_service_is_disabled"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.error(result);
|
||||
return;
|
||||
}
|
||||
|
||||
// enableHighAccuracy = true -> PRIORITY_HIGH_ACCURACY
|
||||
// enableHighAccuracy = false -> PRIORITY_BALANCED_POWER_ACCURACY
|
||||
if (requestHighAccuracy) {
|
||||
highAccuracyRequestList.add(callbackContext);
|
||||
} else {
|
||||
regularAccuracyRequestList.add(callbackContext);
|
||||
}
|
||||
|
||||
if (googleApiClient != null && googleApiClient.isConnecting()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Request geolocation permission.
|
||||
boolean locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity().getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED;
|
||||
|
||||
if (!locationPermission) {
|
||||
//_saveArgs = args;
|
||||
//_saveCallbackContext = callbackContext;
|
||||
synchronized (semaphore) {
|
||||
cordova.requestPermissions(this, callbackContext.hashCode(), new String[]{
|
||||
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
});
|
||||
try {
|
||||
semaphore.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
locationPermission = PermissionChecker.checkSelfPermission(cordova.getActivity().getApplicationContext(), Manifest.permission.ACCESS_COARSE_LOCATION) == PermissionChecker.PERMISSION_GRANTED;
|
||||
|
||||
if (!locationPermission) {
|
||||
|
||||
PluginResult errorResult = new PluginResult(PluginResult.Status.ERROR, PluginUtil.getPgmStrings(activity,"pgm_location_rejected_by_user"));
|
||||
|
||||
synchronized (semaphore) {
|
||||
for (CallbackContext callback: regularAccuracyRequestList) {
|
||||
callback.sendPluginResult(errorResult);
|
||||
}
|
||||
for (CallbackContext callback: highAccuracyRequestList) {
|
||||
callback.sendPluginResult(errorResult);
|
||||
}
|
||||
regularAccuracyRequestList.clear();
|
||||
highAccuracyRequestList.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (lastLocation != null && Calendar.getInstance().getTimeInMillis() - lastLocation.getTime() <= 2000) {
|
||||
//---------------------------------------------------------------------
|
||||
// If the user requests the location in two seconds from the last time,
|
||||
// return the last result in order to save battery usage.
|
||||
// (Don't request the device location too much! Save battery usage!)
|
||||
//---------------------------------------------------------------------
|
||||
JSONObject result;
|
||||
try {
|
||||
result = PluginUtil.location2Json(lastLocation);
|
||||
result.put("status", true);
|
||||
|
||||
PluginResult successResult = new PluginResult(PluginResult.Status.OK, result);
|
||||
synchronized (semaphore) {
|
||||
for (CallbackContext callback: regularAccuracyRequestList) {
|
||||
callback.sendPluginResult(successResult);
|
||||
}
|
||||
for (CallbackContext callback: highAccuracyRequestList) {
|
||||
callback.sendPluginResult(successResult);
|
||||
}
|
||||
regularAccuracyRequestList.clear();
|
||||
highAccuracyRequestList.clear();
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (googleApiClient == null) {
|
||||
|
||||
googleApiClient = new GoogleApiClient.Builder(activity)
|
||||
.addApi(LocationServices.API)
|
||||
.addConnectionCallbacks(new com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks() {
|
||||
|
||||
@Override
|
||||
public void onConnected(Bundle connectionHint) {
|
||||
Log.d(TAG, "===> onConnected " + callbackContext.getCallbackId());
|
||||
requestLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int cause) {
|
||||
Log.e(TAG, "===> onConnectionSuspended");
|
||||
}
|
||||
|
||||
})
|
||||
.addOnConnectionFailedListener(new com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener() {
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(@NonNull ConnectionResult result) {
|
||||
Log.e(TAG, "===> onConnectionFailed");
|
||||
|
||||
PluginResult errorResult = new PluginResult(PluginResult.Status.ERROR, result.toString());
|
||||
|
||||
synchronized (semaphore) {
|
||||
for (CallbackContext callback: regularAccuracyRequestList) {
|
||||
callback.sendPluginResult(errorResult);
|
||||
}
|
||||
for (CallbackContext callback: highAccuracyRequestList) {
|
||||
callback.sendPluginResult(errorResult);
|
||||
}
|
||||
regularAccuracyRequestList.clear();
|
||||
highAccuracyRequestList.clear();
|
||||
}
|
||||
|
||||
googleApiClient.disconnect();
|
||||
}
|
||||
|
||||
})
|
||||
.build();
|
||||
googleApiClient.connect();
|
||||
} else if (googleApiClient.isConnected()) {
|
||||
requestLocation();
|
||||
} else {
|
||||
Log.e(TAG, "===> googleApiClient.isConnected() is not connected");
|
||||
googleApiClient.connect();
|
||||
}
|
||||
}
|
||||
|
||||
private void requestLocation() {
|
||||
Log.d(TAG, "--->regularAccuracyRequestList.size = " + regularAccuracyRequestList.size());
|
||||
|
||||
if (regularAccuracyRequestList.size() > 0) {
|
||||
PluginLocationService.this._requestLocationUpdate(false, false, new CallbackContext("regular-callback", webView) {
|
||||
@Override
|
||||
public void sendPluginResult(PluginResult pluginResult) {
|
||||
|
||||
synchronized (semaphore) {
|
||||
for (CallbackContext callback: regularAccuracyRequestList) {
|
||||
callback.sendPluginResult(pluginResult);
|
||||
}
|
||||
regularAccuracyRequestList.clear();
|
||||
|
||||
if (regularAccuracyRequestList.size() == 0 && highAccuracyRequestList.size() == 0) {
|
||||
googleApiClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
Log.d(TAG, "--->highAccuracyRequestList.size = " + highAccuracyRequestList.size());
|
||||
if (highAccuracyRequestList.size() > 0) {
|
||||
PluginLocationService.this._requestLocationUpdate(false, true, new CallbackContext("regular-callback", webView) {
|
||||
@Override
|
||||
public void sendPluginResult(PluginResult pluginResult) {
|
||||
|
||||
synchronized (semaphore) {
|
||||
for (CallbackContext callback: highAccuracyRequestList) {
|
||||
callback.sendPluginResult(pluginResult);
|
||||
}
|
||||
highAccuracyRequestList.clear();
|
||||
if (regularAccuracyRequestList.size() == 0 && highAccuracyRequestList.size() == 0) {
|
||||
googleApiClient.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// private void _checkLocationSettings() {
|
||||
//
|
||||
// LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().setAlwaysShow(true);
|
||||
//
|
||||
// LocationRequest locationRequest;
|
||||
// locationRequest = LocationRequest.create()
|
||||
// .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
|
||||
// builder.addLocationRequest(locationRequest);
|
||||
//
|
||||
// if (enableHighAccuracy) {
|
||||
// locationRequest = LocationRequest.create()
|
||||
// .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
|
||||
// builder.addLocationRequest(locationRequest);
|
||||
// }
|
||||
//
|
||||
// PendingResult<LocationSettingsResult> locationSettingsResult =
|
||||
// LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
|
||||
//
|
||||
// locationSettingsResult.setResultCallback(new ResultCallback<LocationSettingsResult>() {
|
||||
//
|
||||
// @Override
|
||||
// public void onResult(@NonNull LocationSettingsResult result) {
|
||||
// final Status status = result.getStatus();
|
||||
// switch (status.getStatusCode()) {
|
||||
// case LocationSettingsStatusCodes.SUCCESS:
|
||||
// _requestLocationUpdate(false, enableHighAccuracy, callbackContext);
|
||||
// break;
|
||||
//
|
||||
// case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
|
||||
// // Location settings are not satisfied. But could be fixed by showing the user
|
||||
// // a dialog.
|
||||
// try {
|
||||
// //Keep the callback id
|
||||
// Bundle bundle = new Bundle();
|
||||
// bundle.putInt("type", ACTIVITY_LOCATION_DIALOG);
|
||||
// bundle.putString("callbackId", callbackContext.getCallbackId());
|
||||
// bundle.putBoolean("enableHighAccuracy", enableHighAccuracy);
|
||||
// int hashCode = bundle.hashCode();
|
||||
//
|
||||
// bufferForLocationDialog.put("bundle_" + hashCode, bundle);
|
||||
// //PluginLocationService.this.sendNoResult(callbackContext);
|
||||
//
|
||||
// // Show the dialog by calling startResolutionForResult(),
|
||||
// // and check the result in onActivityResult().
|
||||
// cordova.setActivityResultCallback(PluginLocationService.this);
|
||||
// status.startResolutionForResult(cordova.getActivity(), hashCode);
|
||||
// } catch (IntentSender.SendIntentException e) {
|
||||
// // Show the dialog that is original version of this plugin.
|
||||
// _showLocationSettingsPage(enableHighAccuracy, callbackContext);
|
||||
// }
|
||||
// break;
|
||||
//
|
||||
// case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
|
||||
// // Location settings are not satisfied. However, we have no way to fix the
|
||||
// // settings so we won't show the dialog.
|
||||
//
|
||||
// JSONObject jsResult = new JSONObject();
|
||||
// try {
|
||||
// jsResult.put("status", false);
|
||||
// jsResult.put("error_code", "service_not_available");
|
||||
// jsResult.put("error_message", "This app has been rejected to use Location Services.");
|
||||
// } catch (JSONException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// callbackContext.error(jsResult);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private void _showLocationSettingsPage(final boolean enableHighAccuracy, final CallbackContext callbackContext) {
|
||||
// //Ask the user to turn on the location services.
|
||||
// AlertDialog.Builder builder = new AlertDialog.Builder(this.activity);
|
||||
// builder.setTitle("Improve location accuracy");
|
||||
// builder.setMessage("To enhance your Maps experience:\n\n" +
|
||||
// " - Enable Google apps location access\n\n" +
|
||||
// " - Turn on GPS and mobile network location");
|
||||
// builder.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(DialogInterface dialog, int which) {
|
||||
// //Keep the callback id
|
||||
// Bundle bundle = new Bundle();
|
||||
// bundle.putInt("type", ACTIVITY_LOCATION_PAGE);
|
||||
// bundle.putString("callbackId", callbackContext.getCallbackId());
|
||||
// bundle.putBoolean("enableHighAccuracy", enableHighAccuracy);
|
||||
// int hashCode = bundle.hashCode();
|
||||
//
|
||||
// bufferForLocationDialog.put("bundle_" + hashCode, bundle);
|
||||
// //PluginLocationService.this.sendNoResult(callbackContext);
|
||||
//
|
||||
// //Launch settings, allowing user to make a change
|
||||
// cordova.setActivityResultCallback(PluginLocationService.this);
|
||||
// Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
||||
// activity.startActivityForResult(intent, hashCode);
|
||||
// }
|
||||
// });
|
||||
// builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(DialogInterface dialog, int which) {
|
||||
// //No location service, no Activity
|
||||
// dialog.dismiss();
|
||||
//
|
||||
// JSONObject result = new JSONObject();
|
||||
// try {
|
||||
// result.put("status", false);
|
||||
// result.put("error_code", "service_denied");
|
||||
// result.put("error_message", "This app has been rejected to use Location Services.");
|
||||
// } catch (JSONException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// callbackContext.error(result);
|
||||
// }
|
||||
// });
|
||||
// builder.create().show();
|
||||
// }
|
||||
|
||||
@SuppressWarnings("MissingPermission")
|
||||
private void _requestLocationUpdate(final boolean isRetry, final boolean enableHighAccuracy, final CallbackContext callbackContext) {
|
||||
|
||||
Log.d(TAG, "---->_requestLocationUpdate (isRetry = " + isRetry + ")");
|
||||
int priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
|
||||
if (enableHighAccuracy || "Genymotion".equals(Build.MANUFACTURER)) {
|
||||
priority = LocationRequest.PRIORITY_HIGH_ACCURACY;
|
||||
}
|
||||
|
||||
if (!isRetry) {
|
||||
LocationServices.getFusedLocationProviderClient(cordova.getActivity())
|
||||
.getLastLocation()
|
||||
.addOnSuccessListener(new OnSuccessListener<Location>() {
|
||||
@Override
|
||||
public void onSuccess(Location location) {
|
||||
lastLocation = location;
|
||||
if (lastLocation != null && Calendar.getInstance().getTimeInMillis() - lastLocation.getTime() <= 2000) {
|
||||
Log.d(TAG, "---->The last location is obtained in 2 sec.");
|
||||
//---------------------------------------------------------------------
|
||||
// If the user requests the location in two seconds from the last time,
|
||||
// return the last result in order to save battery usage.
|
||||
// (Don't request the device location too much! Save battery usage!)
|
||||
//---------------------------------------------------------------------
|
||||
JSONObject result;
|
||||
try {
|
||||
result = PluginUtil.location2Json(lastLocation);
|
||||
result.put("status", true);
|
||||
|
||||
PluginResult successResult = new PluginResult(PluginResult.Status.OK, result);
|
||||
synchronized (semaphore) {
|
||||
for (CallbackContext callback : regularAccuracyRequestList) {
|
||||
callback.sendPluginResult(successResult);
|
||||
}
|
||||
for (CallbackContext callback: highAccuracyRequestList) {
|
||||
callback.sendPluginResult(successResult);
|
||||
}
|
||||
regularAccuracyRequestList.clear();
|
||||
highAccuracyRequestList.clear();
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
googleApiClient.disconnect();
|
||||
} else {
|
||||
Log.d(TAG, "---->The last location is expired. Let's get the latest location...");
|
||||
_requestLocationUpdate(true, enableHighAccuracy, callbackContext);
|
||||
}
|
||||
}
|
||||
})
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception e) {
|
||||
_requestLocationUpdate(true, enableHighAccuracy, callbackContext);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// It seems setting the values to both setFastestInterval() and setInterval() is important.
|
||||
// https://akira-watson.com/android/fusedlocationproviderapi.html
|
||||
LocationRequest locationRequest= LocationRequest.create()
|
||||
//.setNumUpdates(2)
|
||||
.setFastestInterval(5000)
|
||||
.setInterval(10000)
|
||||
//.setSmallestDisplacement(0)
|
||||
.setPriority(priority)
|
||||
.setExpirationDuration(12000)
|
||||
.setMaxWaitTime(5000);
|
||||
|
||||
LocationServices.getFusedLocationProviderClient(cordova.getActivity()).requestLocationUpdates(locationRequest, new LocationCallback() {
|
||||
@Override
|
||||
public void onLocationResult(LocationResult locationResult) {
|
||||
Log.d(TAG, "---->onLocationResult");
|
||||
Location location = null;
|
||||
if (locationResult.getLocations().size() > 0) {
|
||||
location = locationResult.getLocations().get(0);
|
||||
lastLocation = location;
|
||||
} else if (locationResult.getLastLocation() != null) {
|
||||
location = locationResult.getLastLocation();
|
||||
}
|
||||
|
||||
Log.d(TAG, "===> location =" + location);
|
||||
if (location != null) {
|
||||
JSONObject result;
|
||||
Log.d(TAG, "===> location =" + callbackContext.getCallbackId());
|
||||
try {
|
||||
result = PluginUtil.location2Json(location);
|
||||
result.put("status", true);
|
||||
callbackContext.success(result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// Send back the error result
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("status", false);
|
||||
result.put("error_code", "cannot_detect");
|
||||
result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_can_not_get_location"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.error(result);
|
||||
}
|
||||
|
||||
googleApiClient.disconnect();
|
||||
}
|
||||
}, Looper.myLooper());
|
||||
|
||||
}
|
||||
|
||||
private void _onActivityResultLocationPage(Bundle bundle) {
|
||||
String callbackId = bundle.getString("callbackId");
|
||||
CallbackContext callbackContext = new CallbackContext(callbackId, this.webView);
|
||||
|
||||
LocationManager locationManager = (LocationManager) this.activity.getSystemService(Context.LOCATION_SERVICE);
|
||||
List<String> providers = locationManager.getAllProviders();
|
||||
int availableProviders = 0;
|
||||
//if (mPluginLayout != null && mPluginLayout.isDebug) {
|
||||
Log.d(TAG, "---debug at getMyLocation(available providers)--");
|
||||
//}
|
||||
Iterator<String> iterator = providers.iterator();
|
||||
String provider;
|
||||
boolean isAvailable;
|
||||
while(iterator.hasNext()) {
|
||||
provider = iterator.next();
|
||||
if ("passive".equals(provider)) {
|
||||
continue;
|
||||
}
|
||||
isAvailable = locationManager.isProviderEnabled(provider);
|
||||
if (isAvailable) {
|
||||
availableProviders++;
|
||||
}
|
||||
//if (mPluginLayout != null && mPluginLayout.isDebug) {
|
||||
Log.d(TAG, " " + provider + " = " + (isAvailable ? "" : "not ") + "available");
|
||||
//}
|
||||
}
|
||||
if (availableProviders == 0) {
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("status", false);
|
||||
result.put("error_code", "not_available");
|
||||
result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_no_location_providers"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.error(result);
|
||||
return;
|
||||
}
|
||||
|
||||
_inviteLocationUpdateAfterActivityResult(bundle);
|
||||
}
|
||||
|
||||
private void _inviteLocationUpdateAfterActivityResult(Bundle bundle) {
|
||||
boolean enableHighAccuracy = bundle.getBoolean("enableHighAccuracy");
|
||||
String callbackId = bundle.getString("callbackId");
|
||||
CallbackContext callbackContext = new CallbackContext(callbackId, this.webView);
|
||||
this._requestLocationUpdate(false, enableHighAccuracy, callbackContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (!bufferForLocationDialog.containsKey("bundle_" + requestCode)) {
|
||||
Log.e(TAG, "no key");
|
||||
return;
|
||||
}
|
||||
Bundle query = bufferForLocationDialog.get("bundle_" + requestCode);
|
||||
Log.d(TAG, "====> onActivityResult (" + resultCode + ")");
|
||||
|
||||
switch (query.getInt("type")) {
|
||||
case ACTIVITY_LOCATION_DIALOG:
|
||||
// User was asked to enable the location setting.
|
||||
switch (resultCode) {
|
||||
case Activity.RESULT_OK:
|
||||
// All required changes were successfully made
|
||||
_inviteLocationUpdateAfterActivityResult(query);
|
||||
break;
|
||||
case Activity.RESULT_CANCELED:
|
||||
// The user was asked to change settings, but chose not to
|
||||
_userRefusedToUseLocationAfterActivityResult(query);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ACTIVITY_LOCATION_PAGE:
|
||||
_onActivityResultLocationPage(query);
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void _userRefusedToUseLocationAfterActivityResult(Bundle bundle) {
|
||||
String callbackId = bundle.getString("callbackId");
|
||||
CallbackContext callbackContext = new CallbackContext(callbackId, this.webView);
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("status", false);
|
||||
result.put("error_code", "service_denied");
|
||||
result.put("error_message", PluginUtil.getPgmStrings(activity,"pgm_location_rejected_by_user"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.error(result);
|
||||
}
|
||||
|
||||
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
||||
int[] grantResults) throws JSONException {
|
||||
synchronized (semaphore) {
|
||||
semaphore.notify();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,779 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
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.MarkerOptions;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class PluginMarkerCluster extends PluginMarker {
|
||||
|
||||
private final static Map<String, STATUS> pluginMarkers = new ConcurrentHashMap<String, STATUS>();
|
||||
private final static Map<String, Integer> waitCntManager = new ConcurrentHashMap<String, Integer>();
|
||||
private final static Map<String, Boolean> debugFlags = new ConcurrentHashMap<String, Boolean>();
|
||||
private final static ArrayList<String> deleteMarkers = new ArrayList<String>();
|
||||
|
||||
private final Object semaphore = new Object();
|
||||
private boolean stopFlag = false;
|
||||
private final Object deleteThreadLock = new Object();
|
||||
|
||||
enum STATUS {
|
||||
WORKING,
|
||||
CREATED,
|
||||
DELETED
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
if (!deleteThread.isAlive()) {
|
||||
deleteThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopFlag = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
synchronized (semaphore) {
|
||||
synchronized (pluginMarkers) {
|
||||
synchronized (deleteMarkers) {
|
||||
String clusterId_markerId;
|
||||
if (pluginMarkers.size() > 0) {
|
||||
String[] keys = pluginMarkers.keySet().toArray(new String[pluginMarkers.size()]);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
clusterId_markerId = keys[i];
|
||||
pluginMarkers.put(clusterId_markerId, STATUS.DELETED);
|
||||
deleteMarkers.add(clusterId_markerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// Delete thread
|
||||
//---------------------
|
||||
private Thread deleteThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while(!stopFlag) {
|
||||
synchronized (deleteThreadLock) {
|
||||
try {
|
||||
deleteThreadLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (deleteMarkers.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cordova.getActivity().runOnUiThread(removeOverlaysOnUiThread);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private Runnable removeOverlaysOnUiThread = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (deleteMarkers) {
|
||||
String markerId;
|
||||
Marker marker;
|
||||
STATUS status;
|
||||
String cacheKey;
|
||||
if (deleteMarkers.size() > 0) {
|
||||
String[] targetIDs = deleteMarkers.toArray(new String[deleteMarkers.size()]);
|
||||
|
||||
for (int i = targetIDs.length - 1; i > -1; i--) {
|
||||
markerId = targetIDs[i];
|
||||
|
||||
marker = self.getMarker(markerId);
|
||||
synchronized (pluginMarkers) {
|
||||
status = pluginMarkers.get(markerId);
|
||||
|
||||
if (!STATUS.WORKING.equals(status)) {
|
||||
synchronized (pluginMap.objects) {
|
||||
_removeMarker(marker);
|
||||
marker = null;
|
||||
|
||||
cacheKey = (String) pluginMap.objects.remove("marker_icon_" + markerId);
|
||||
if (cacheKey != null && iconCacheKeys.containsKey(cacheKey)) {
|
||||
int count = iconCacheKeys.get(cacheKey);
|
||||
if (count < 1) {
|
||||
iconCacheKeys.remove(cacheKey);
|
||||
AsyncLoadImage.removeBitmapFromMemCahce(cacheKey);
|
||||
} else {
|
||||
iconCacheKeys.put(cacheKey, count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pluginMap.objects.remove(markerId);
|
||||
pluginMap.objects.remove("marker_property_" + markerId);
|
||||
pluginMap.objects.remove("marker_imageSize_" + markerId);
|
||||
}
|
||||
pluginMarkers.remove(markerId);
|
||||
deleteMarkers.remove(i);
|
||||
} else {
|
||||
pluginMarkers.put(markerId, STATUS.DELETED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String clusterId = args.getString(0);
|
||||
//Log.d(TAG, "-->remove = " + clusterId);
|
||||
synchronized (debugFlags) {
|
||||
debugFlags.remove(clusterId);
|
||||
waitCntManager.remove(clusterId);
|
||||
}
|
||||
synchronized (pluginMarkers) {
|
||||
for(String key: pluginMarkers.keySet()) {
|
||||
if (key.startsWith(clusterId)) {
|
||||
//Log.d(TAG, "-->delete = " + key);
|
||||
pluginMarkers.put(key, STATUS.CREATED);
|
||||
deleteMarkers.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
String cacheKey;
|
||||
Set<String> keySet = pluginMap.objects.keySet();
|
||||
Marker marker;
|
||||
String[] objectIdArray = keySet.toArray(new String[keySet.size()]);
|
||||
for (String objectId : objectIdArray) {
|
||||
if (objectId.contains(clusterId)) {
|
||||
if (objectId.startsWith("marker_icon")) {
|
||||
cacheKey = (String) pluginMap.objects.remove(objectId);
|
||||
|
||||
if (iconCacheKeys.containsKey(cacheKey)) {
|
||||
int count = iconCacheKeys.get(cacheKey);
|
||||
if (count < 1) {
|
||||
iconCacheKeys.remove(cacheKey);
|
||||
AsyncLoadImage.removeBitmapFromMemCahce(cacheKey);
|
||||
} else {
|
||||
iconCacheKeys.put(cacheKey, count - 1);
|
||||
}
|
||||
}
|
||||
} else if (objectId.startsWith("marker_property_") ||
|
||||
objectId.startsWith("marker_imageSize_")) {
|
||||
pluginMap.objects.remove(objectId);
|
||||
//} else if (objectId.startsWith("marker_")) {
|
||||
// marker = (Marker) pluginMap.objects.remove(objectId);
|
||||
// marker.remove();
|
||||
} else {
|
||||
pluginMap.objects.remove(objectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
synchronized (deleteThreadLock) {
|
||||
deleteThreadLock.notify();
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
}
|
||||
/**
|
||||
* Create a marker
|
||||
*
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
JSONObject params = args.getJSONObject(1);
|
||||
String hashCode = args.getString(2);
|
||||
JSONArray positionList = params.getJSONArray("positionList");
|
||||
JSONArray geocellList = new JSONArray();
|
||||
JSONObject position;
|
||||
|
||||
for (int i = 0; i < positionList.length(); i++) {
|
||||
position = positionList.getJSONObject(i);
|
||||
geocellList.put(getGeocell(position.getDouble("lat"), position.getDouble("lng"), 12));
|
||||
}
|
||||
|
||||
String id = "markercluster_" + hashCode;
|
||||
debugFlags.put(id, params.getBoolean("debug"));
|
||||
|
||||
final JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("geocellList", geocellList);
|
||||
result.put("hashCode", hashCode);
|
||||
result.put("__pgmId", id);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
callbackContext.success(result);
|
||||
}
|
||||
|
||||
|
||||
public void redrawClusters(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
|
||||
final HashSet<String> updateClusterIDs = new HashSet<String>();
|
||||
final HashMap<String, Bundle> changeProperties = new HashMap<String, Bundle>();
|
||||
final String clusterId = args.getString(0);
|
||||
final boolean isDebug = debugFlags.get(clusterId);
|
||||
final JSONObject params = args.getJSONObject(1);
|
||||
|
||||
String markerId, clusterId_markerId;
|
||||
JSONArray new_or_update = null;
|
||||
if (params.has("new_or_update")) {
|
||||
new_or_update = params.getJSONArray("new_or_update");
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// Determine new or update
|
||||
//---------------------------
|
||||
int new_or_updateCnt = 0;
|
||||
if (new_or_update != null) {
|
||||
new_or_updateCnt = new_or_update.length();
|
||||
}
|
||||
JSONObject clusterData, positionJSON;
|
||||
Bundle properties;
|
||||
boolean isNew;
|
||||
for (int i = 0; i < new_or_updateCnt; i++) {
|
||||
clusterData = new_or_update.getJSONObject(i);
|
||||
positionJSON = clusterData.getJSONObject("position");
|
||||
markerId = clusterData.getString("__pgmId");
|
||||
clusterId_markerId = clusterId + "-" + markerId;
|
||||
|
||||
// Save the marker properties
|
||||
pluginMap.objects.put("marker_property_" + clusterId_markerId, clusterData);
|
||||
|
||||
// Set the WORKING status flag
|
||||
synchronized (pluginMarkers) {
|
||||
pluginMarkers.put(clusterId_markerId, STATUS.WORKING);
|
||||
}
|
||||
updateClusterIDs.add(clusterId_markerId);
|
||||
|
||||
// Prepare the marker properties for addMarker()
|
||||
properties = new Bundle();
|
||||
properties.putDouble("lat", positionJSON.getDouble("lat"));
|
||||
properties.putDouble("lng", positionJSON.getDouble("lng"));
|
||||
if (clusterData.has("title")) {
|
||||
properties.putString("title", clusterData.getString("title"));
|
||||
}
|
||||
properties.putString("__pgmId", clusterId_markerId);
|
||||
|
||||
if (clusterData.has("icon")) {
|
||||
Object iconObj = clusterData.get("icon");
|
||||
|
||||
if (iconObj instanceof String) {
|
||||
Bundle iconProperties = new Bundle();
|
||||
iconProperties.putString("url", (String) iconObj);
|
||||
properties.putBundle("icon", iconProperties);
|
||||
|
||||
} else if (iconObj instanceof JSONObject) {
|
||||
JSONObject icon = clusterData.getJSONObject("icon");
|
||||
Bundle iconProperties = PluginUtil.Json2Bundle(icon);
|
||||
if (clusterData.has("isClusterIcon") && clusterData.getBoolean("isClusterIcon")) {
|
||||
if (icon.has("label")) {
|
||||
JSONObject label = icon.getJSONObject("label");
|
||||
if (isDebug) {
|
||||
label.put("text", markerId);
|
||||
} else {
|
||||
label.put("text", clusterData.getInt("count") + "");
|
||||
}
|
||||
if (label.has("color")) {
|
||||
label.put("color", PluginUtil.parsePluginColor(label.getJSONArray("color")));
|
||||
}
|
||||
iconProperties.putBundle("label", PluginUtil.Json2Bundle(label));
|
||||
} else {
|
||||
Bundle label = new Bundle();
|
||||
if (isDebug) {
|
||||
label.putString("text", markerId);
|
||||
} else {
|
||||
label.putInt("fontSize", 15);
|
||||
label.putBoolean("bold", true);
|
||||
label.putString("text", clusterData.getInt("count") + "");
|
||||
}
|
||||
iconProperties.putBundle("label", label);
|
||||
}
|
||||
}
|
||||
if (icon.has("anchor")) {
|
||||
double[] anchor = new double[2];
|
||||
anchor[0] = icon.getJSONArray("anchor").getDouble(0);
|
||||
anchor[1] = icon.getJSONArray("anchor").getDouble(1);
|
||||
iconProperties.putDoubleArray("anchor", anchor);
|
||||
}
|
||||
if (icon.has("infoWindowAnchor")) {
|
||||
double[] anchor = new double[2];
|
||||
anchor[0] = icon.getJSONArray("infoWindowAnchor").getDouble(0);
|
||||
anchor[1] = icon.getJSONArray("infoWindowAnchor").getDouble(1);
|
||||
iconProperties.putDoubleArray("infoWindowAnchor", anchor);
|
||||
}
|
||||
properties.putBundle("icon", iconProperties);
|
||||
}
|
||||
}
|
||||
|
||||
changeProperties.put(clusterId_markerId, properties);
|
||||
}
|
||||
|
||||
//Log.d(TAG, "---> deleteCnt : " + deleteCnt + ", newCnt : " + newCnt + ", updateCnt : " + updateCnt + ", reuseCnt : " + reuseCnt);
|
||||
|
||||
if (updateClusterIDs.size() == 0) {
|
||||
deleteProcess(clusterId, params);
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// mapping markers on the map
|
||||
//---------------------------
|
||||
final JSONObject allResults = new JSONObject();
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Iterator<String> iterator;
|
||||
String clusterId_markerId;
|
||||
Bundle markerProperties;
|
||||
Marker marker;
|
||||
Boolean isNew;
|
||||
|
||||
//---------
|
||||
// new or update
|
||||
//---------
|
||||
int waitCnt = updateClusterIDs.size();
|
||||
int currentCnt = 0;
|
||||
waitCntManager.put(clusterId, updateClusterIDs.size());
|
||||
iterator = updateClusterIDs.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
currentCnt++;
|
||||
clusterId_markerId = iterator.next();
|
||||
//Log.d(TAG, "---> progress = " + currentCnt + "/ " + waitCnt + ", " + clusterId_markerId);
|
||||
synchronized (pluginMarkers) {
|
||||
pluginMarkers.put(clusterId_markerId, STATUS.WORKING);
|
||||
}
|
||||
isNew = !pluginMap.objects.containsKey(clusterId_markerId);
|
||||
|
||||
// Get the marker properties
|
||||
markerProperties = changeProperties.get(clusterId_markerId);
|
||||
if (clusterId_markerId.contains("-marker_")) {
|
||||
//-------------------
|
||||
// regular marker
|
||||
//-------------------
|
||||
if (isNew) {
|
||||
final String fMarkerId = clusterId_markerId;
|
||||
JSONObject properties = (JSONObject) pluginMap.objects.get("marker_property_" + clusterId_markerId);
|
||||
try {
|
||||
_create(clusterId_markerId, properties, new ICreateMarkerCallback() {
|
||||
@Override
|
||||
public void onSuccess(Marker marker) {
|
||||
|
||||
synchronized (pluginMarkers) {
|
||||
if (pluginMarkers.get(fMarkerId) == STATUS.DELETED) {
|
||||
_removeMarker(marker);
|
||||
pluginMarkers.remove(fMarkerId);
|
||||
} else {
|
||||
pluginMarkers.put(fMarkerId, STATUS.CREATED);
|
||||
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
if (icons.containsKey(fMarkerId)) {
|
||||
Bitmap icon = icons.get(fMarkerId);
|
||||
try {
|
||||
result.put("width", icon.getWidth() / density);
|
||||
result.put("height", icon.getHeight() / density);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
result.put("width", 24);
|
||||
result.put("height", 42);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
allResults.put(fMarkerId.split("-")[1], result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
decreaseWaitCnt(clusterId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
synchronized (pluginMarkers) {
|
||||
pluginMarkers.put(fMarkerId, STATUS.DELETED);
|
||||
}
|
||||
Log.e(TAG, message);
|
||||
decreaseWaitCnt(clusterId);
|
||||
synchronized (deleteMarkers) {
|
||||
deleteMarkers.add(fMarkerId);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
decreaseWaitCnt(clusterId);
|
||||
}
|
||||
} else {
|
||||
marker = getMarker(clusterId_markerId);
|
||||
//----------------------------------------
|
||||
// Set the title and snippet properties
|
||||
//----------------------------------------
|
||||
if (markerProperties.containsKey("title")) {
|
||||
marker.setTitle(markerProperties.getString("title"));
|
||||
}
|
||||
if (markerProperties.containsKey("snippet")) {
|
||||
marker.setSnippet(markerProperties.getString("snippet"));
|
||||
}
|
||||
synchronized (pluginMarkers) {
|
||||
if (pluginMarkers.get(clusterId_markerId) == STATUS.DELETED) {
|
||||
_removeMarker(marker);
|
||||
pluginMarkers.remove(clusterId_markerId);
|
||||
} else {
|
||||
pluginMarkers.put(clusterId_markerId, STATUS.CREATED);
|
||||
}
|
||||
}
|
||||
decreaseWaitCnt(clusterId);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//--------------------------
|
||||
// cluster icon
|
||||
//--------------------------
|
||||
if (isNew) {
|
||||
// If the requested id is new location, create a marker
|
||||
marker = map.addMarker(new MarkerOptions()
|
||||
.position(new LatLng(markerProperties.getDouble("lat"), markerProperties.getDouble("lng")))
|
||||
.visible(false));
|
||||
marker.setTag(clusterId_markerId);
|
||||
|
||||
// Store the marker instance with markerId
|
||||
synchronized (pluginMap.objects) {
|
||||
pluginMap.objects.put(clusterId_markerId, marker);
|
||||
}
|
||||
} else {
|
||||
marker = getMarker(clusterId_markerId);
|
||||
}
|
||||
//----------------------------------------
|
||||
// Set the title and snippet properties
|
||||
//----------------------------------------
|
||||
if (markerProperties.containsKey("title")) {
|
||||
marker.setTitle(markerProperties.getString("title"));
|
||||
}
|
||||
if (markerProperties.containsKey("snippet")) {
|
||||
marker.setSnippet(markerProperties.getString("snippet"));
|
||||
}
|
||||
/*
|
||||
if (!isNew) {
|
||||
synchronized (pluginMarkers) {
|
||||
pluginMarkers.put(markerId, STATUS.CREATED);
|
||||
}
|
||||
decreaseWaitCnt(clusterId);
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
if (markerProperties.containsKey("icon")) {
|
||||
Bundle icon = markerProperties.getBundle("icon");
|
||||
final String fMarkerId = clusterId_markerId;
|
||||
//Log.d(TAG, "---> targetMarkerId = " + targetMarkerId + ", marker = " + marker);
|
||||
setIconToClusterMarker(clusterId_markerId, marker, icon, new PluginAsyncInterface() {
|
||||
@Override
|
||||
public void onPostExecute(Object object) {
|
||||
//--------------------------------------
|
||||
// Marker was updated
|
||||
//--------------------------------------
|
||||
decreaseWaitCnt(clusterId);
|
||||
synchronized (pluginMarkers) {
|
||||
//Log.d(TAG, "create : " + fMarkerId + " = CREATED");
|
||||
pluginMarkers.put(fMarkerId, STATUS.CREATED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMsg) {
|
||||
//--------------------------------------
|
||||
// Could not read icon for some reason
|
||||
//--------------------------------------
|
||||
Log.e(TAG, errorMsg);
|
||||
decreaseWaitCnt(clusterId);
|
||||
synchronized (deleteMarkers) {
|
||||
deleteMarkers.add(fMarkerId);
|
||||
}
|
||||
synchronized (pluginMarkers) {
|
||||
pluginMarkers.put(fMarkerId, STATUS.DELETED);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//--------------------
|
||||
// No icon for marker
|
||||
//--------------------
|
||||
synchronized (pluginMarkers) {
|
||||
//Log.d(TAG, "create : " + clusterId_markerId + " = CREATED");
|
||||
pluginMarkers.put(clusterId_markerId, STATUS.CREATED);
|
||||
}
|
||||
decreaseWaitCnt(clusterId);
|
||||
}
|
||||
}
|
||||
updateClusterIDs.clear();
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
synchronized (semaphore) {
|
||||
try {
|
||||
semaphore.wait();
|
||||
deleteProcess(clusterId, params);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
callbackContext.success(allResults);
|
||||
|
||||
}
|
||||
private void deleteProcess(final String clusterId, final JSONObject params) {
|
||||
if (!params.has("delete")) {
|
||||
return;
|
||||
}
|
||||
cordova.getThreadPool().submit(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
JSONArray deleteClusters = params.getJSONArray("delete");
|
||||
if (deleteClusters != null) {
|
||||
//-------------------------------------
|
||||
// delete markers on the delete thread
|
||||
//-------------------------------------
|
||||
int deleteCnt = deleteClusters.length();
|
||||
String clusterId_markerId;
|
||||
for (int i = 0; i < deleteCnt; i++) {
|
||||
clusterId_markerId = clusterId + "-" + deleteClusters.getString(i);
|
||||
deleteMarkers.add(clusterId_markerId);
|
||||
}
|
||||
|
||||
synchronized (deleteThreadLock) {
|
||||
deleteThreadLock.notify();
|
||||
}
|
||||
|
||||
}
|
||||
} catch (Exception e) {e.printStackTrace();}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void decreaseWaitCnt(String clusterId) {
|
||||
|
||||
//--------------------------------------
|
||||
// Icon is set to marker
|
||||
//--------------------------------------
|
||||
synchronized (waitCntManager) {
|
||||
int waitCnt = waitCntManager.get(clusterId);
|
||||
waitCnt = waitCnt - 1;
|
||||
//Log.d(TAG, "--->waitCnt = " + waitCnt);
|
||||
if (waitCnt == 0) {
|
||||
synchronized (semaphore) {
|
||||
semaphore.notify();
|
||||
}
|
||||
|
||||
}
|
||||
waitCntManager.put(clusterId, waitCnt);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIconToClusterMarker(final String markerId, final Marker marker, final Bundle iconProperty, final PluginAsyncInterface callback) {
|
||||
synchronized (pluginMarkers) {
|
||||
if (STATUS.DELETED.equals(pluginMarkers.get(markerId))) {
|
||||
synchronized (pluginMap.objects) {
|
||||
PluginMarkerCluster.this._removeMarker(marker);
|
||||
pluginMap.objects.remove(markerId);
|
||||
pluginMap.objects.remove("marker_property_" + markerId);
|
||||
}
|
||||
pluginMarkers.remove(markerId);
|
||||
callback.onError("marker has been removed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
PluginMarkerCluster.super.setIcon_(marker, iconProperty, new PluginAsyncInterface() {
|
||||
@Override
|
||||
public void onPostExecute(Object object) {
|
||||
Marker marker = (Marker) object;
|
||||
synchronized (pluginMarkers) {
|
||||
if (STATUS.DELETED.equals(pluginMarkers.get(markerId))) {
|
||||
synchronized (pluginMap.objects) {
|
||||
PluginMarkerCluster.this._removeMarker(marker);
|
||||
pluginMap.objects.remove(markerId);
|
||||
pluginMap.objects.remove("marker_property_" + markerId);
|
||||
}
|
||||
pluginMarkers.remove(markerId);
|
||||
callback.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
marker.setVisible(true);
|
||||
|
||||
//Log.d(TAG, "create : " + markerId + " = CREATED");
|
||||
pluginMarkers.put(markerId, STATUS.CREATED);
|
||||
callback.onPostExecute(object);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorMsg) {
|
||||
synchronized (pluginMap.objects) {
|
||||
if (marker != null && marker.getTag() != null) {
|
||||
PluginMarkerCluster.this._removeMarker(marker);
|
||||
}
|
||||
pluginMap.objects.remove(markerId);
|
||||
pluginMap.objects.remove("marker_property_" + markerId);
|
||||
pluginMarkers.remove(markerId);
|
||||
pluginMap.objects.remove(markerId);
|
||||
pluginMarkers.put(markerId, STATUS.DELETED);
|
||||
}
|
||||
callback.onPostExecute(errorMsg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
// The maximum *practical* geocell resolution.
|
||||
private static final int GEOCELL_GRID_SIZE = 4;
|
||||
public static final String GEOCELL_ALPHABET = "0123456789abcdef";
|
||||
|
||||
private String getGeocell(double lat, double lng, int resolution) {
|
||||
StringBuilder cell = new StringBuilder();
|
||||
double north = 90.0;
|
||||
double south = -90.0;
|
||||
double east = 180.0;
|
||||
double west = -180.0;
|
||||
double subcell_lng_span, subcell_lat_span;
|
||||
byte x, y;
|
||||
while(cell.length() < resolution + 1) {
|
||||
subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE;
|
||||
subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE;
|
||||
|
||||
x = (byte)Math.min(Math.floor(GEOCELL_GRID_SIZE * (lng - west) / (east - west)), GEOCELL_GRID_SIZE - 1);
|
||||
y = (byte)Math.min(Math.floor(GEOCELL_GRID_SIZE * (lat - south) / (north - south)), GEOCELL_GRID_SIZE - 1);
|
||||
cell.append(_subdiv_char(x, y));
|
||||
|
||||
south += subcell_lat_span * y;
|
||||
north = south + subcell_lat_span;
|
||||
|
||||
west += subcell_lng_span * x;
|
||||
east = west + subcell_lng_span;
|
||||
}
|
||||
return cell.toString();
|
||||
}
|
||||
private char _subdiv_char(int posX, int posY) {
|
||||
return GEOCELL_ALPHABET.charAt(
|
||||
(posY & 2) << 2 |
|
||||
(posX & 2) << 1 |
|
||||
(posY & 1) << 1 |
|
||||
(posX & 1) << 0);
|
||||
}
|
||||
|
||||
private double[] _subdiv_xy(char cellChar) {
|
||||
int charI = GEOCELL_ALPHABET.indexOf(cellChar);
|
||||
return new double[]{
|
||||
(double)((charI & 4) >> 1 | (charI & 1) >> 0) + 0.0f,
|
||||
(double)((charI & 8) >> 2 | (charI & 2) >> 1) + 0.0f
|
||||
};
|
||||
}
|
||||
|
||||
private LatLngBounds computeBox(String geocell) {
|
||||
|
||||
//String tmp[] = geocell.split("-");
|
||||
//geocell = tmp[1];
|
||||
|
||||
double subcell_lng_span, subcell_lat_span;
|
||||
double x, y;
|
||||
double xy[];
|
||||
BoundBox bbox = new BoundBox(90.0, 180.0, -90.0, -180.0);
|
||||
|
||||
while (geocell.length() > 0) {
|
||||
subcell_lng_span = (double)(bbox.getEast() - bbox.getWest()) / (double)GEOCELL_GRID_SIZE;
|
||||
subcell_lat_span = (double)(bbox.getNorth() - bbox.getSouth()) / (double)GEOCELL_GRID_SIZE;
|
||||
|
||||
xy = _subdiv_xy(geocell.charAt(0));
|
||||
x = xy[0];
|
||||
y = xy[1];
|
||||
|
||||
bbox = new BoundBox(bbox.getSouth() + subcell_lat_span * (y + 1.0f),
|
||||
bbox.getWest() + subcell_lng_span * (double)(x + 1.0f),
|
||||
bbox.getSouth() + subcell_lat_span * y,
|
||||
bbox.getWest() + subcell_lng_span * x);
|
||||
|
||||
geocell = geocell.substring(1);
|
||||
}
|
||||
LatLng sw = new LatLng(bbox.getSouth(), bbox.getWest());
|
||||
LatLng ne = new LatLng(bbox.getNorth(), bbox.getEast());
|
||||
return new LatLngBounds(sw, ne);
|
||||
}
|
||||
|
||||
private class BoundBox {
|
||||
double north_, south_, east_, west_;
|
||||
|
||||
BoundBox(double north, double east, double south, double west) {
|
||||
if (south > north) {
|
||||
south_ = north;
|
||||
north_ = south;
|
||||
} else {
|
||||
south_ = south;
|
||||
north_ = north;
|
||||
}
|
||||
west_ = west;
|
||||
east_ = east;
|
||||
}
|
||||
|
||||
|
||||
public double getNorth() {
|
||||
return north_;
|
||||
}
|
||||
|
||||
public double getSouth() {
|
||||
return south_;
|
||||
}
|
||||
|
||||
public double getWest() {
|
||||
return west_;
|
||||
}
|
||||
|
||||
public double getEast() {
|
||||
return east_;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,753 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
import com.google.android.gms.maps.model.LatLngBounds;
|
||||
import com.google.android.gms.maps.model.Polygon;
|
||||
import com.google.android.gms.maps.model.PolygonOptions;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
//Future implement
|
||||
//TODO: https://codepen.io/jhawes/pen/ujdgK
|
||||
|
||||
|
||||
public class PluginPolygon extends MyPlugin implements MyPluginInterface {
|
||||
|
||||
private String polygonHashCode;
|
||||
|
||||
/**
|
||||
* Create polygon
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@Override
|
||||
public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
self = this;
|
||||
|
||||
final PolygonOptions polygonOptions = new PolygonOptions();
|
||||
int color;
|
||||
final LatLngBounds.Builder builder = new LatLngBounds.Builder();
|
||||
final JSONObject properties = new JSONObject();
|
||||
final ArrayList<LatLngBounds> holeBounds = new ArrayList<LatLngBounds>();
|
||||
final ArrayList<LatLng> path = new ArrayList<LatLng>();
|
||||
final ArrayList<ArrayList<LatLng>> holePaths = new ArrayList<ArrayList<LatLng>>();
|
||||
|
||||
JSONObject opts = args.getJSONObject(1);
|
||||
final String hashCode = args.getString(2);
|
||||
polygonHashCode = hashCode;
|
||||
if (opts.has("points")) {
|
||||
JSONArray points = opts.getJSONArray("points");
|
||||
ArrayList<LatLng> path2 = PluginUtil.JSONArray2LatLngList(points);
|
||||
for (int i = 0; i < path2.size(); i++) {
|
||||
polygonOptions.add(path2.get(i));
|
||||
path.add(path2.get(i));
|
||||
builder.include(path2.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.has("holes")) {
|
||||
JSONArray holes = opts.getJSONArray("holes");
|
||||
int i;
|
||||
JSONArray latLngArray;
|
||||
ArrayList<LatLng> hole;
|
||||
Iterator<LatLng> iterator;
|
||||
LatLng latLng;
|
||||
LatLngBounds.Builder builder2;
|
||||
for (i = 0; i < holes.length(); i++) {
|
||||
latLngArray = holes.getJSONArray(i);
|
||||
hole = PluginUtil.JSONArray2LatLngList(latLngArray);
|
||||
polygonOptions.addHole(hole);
|
||||
holePaths.add(hole);
|
||||
iterator = hole.iterator();
|
||||
builder2 = new LatLngBounds.Builder();
|
||||
while (iterator.hasNext()) {
|
||||
latLng = iterator.next();
|
||||
builder2.include(latLng);
|
||||
}
|
||||
holeBounds.add(builder2.build());
|
||||
}
|
||||
}
|
||||
if (opts.has("strokeColor")) {
|
||||
color = PluginUtil.parsePluginColor(opts.getJSONArray("strokeColor"));
|
||||
polygonOptions.strokeColor(color);
|
||||
}
|
||||
if (opts.has("fillColor")) {
|
||||
color = PluginUtil.parsePluginColor(opts.getJSONArray("fillColor"));
|
||||
polygonOptions.fillColor(color);
|
||||
}
|
||||
if (opts.has("strokeWidth")) {
|
||||
polygonOptions.strokeWidth((float)(opts.getDouble("strokeWidth") * density));
|
||||
}
|
||||
if (opts.has("visible")) {
|
||||
polygonOptions.visible(opts.getBoolean("visible"));
|
||||
}
|
||||
if (opts.has("geodesic")) {
|
||||
polygonOptions.geodesic(opts.getBoolean("geodesic"));
|
||||
}
|
||||
if (opts.has("zIndex")) {
|
||||
polygonOptions.zIndex(opts.getInt("zIndex"));
|
||||
}
|
||||
if (opts.has("clickable")) {
|
||||
properties.put("isClickable", opts.getBoolean("clickable"));
|
||||
} else {
|
||||
properties.put("isClickable", true);
|
||||
}
|
||||
properties.put("isVisible", polygonOptions.isVisible());
|
||||
properties.put("zIndex", polygonOptions.getZIndex());
|
||||
properties.put("isGeodesic", polygonOptions.isGeodesic());
|
||||
|
||||
// Since this plugin uses own detecting process,
|
||||
// set false to the clickable property.
|
||||
polygonOptions.clickable(false);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Polygon polygon = map.addPolygon(polygonOptions);
|
||||
String id = hashCode;
|
||||
polygon.setTag(hashCode);
|
||||
pluginMap.objects.put("polygon_"+ id, polygon);
|
||||
pluginMap.objects.put("polygon_bounds_" + id, builder.build());
|
||||
pluginMap.objects.put("polygon_path_" + id, path);
|
||||
pluginMap.objects.put("polygon_holePaths_" + id, holePaths);
|
||||
pluginMap.objects.put("polygon_property_" + id, properties);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
result.put("hashCode", hashCode);
|
||||
result.put("__pgmId", "polygon_"+ id);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.success(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Set<String> keySet = pluginMap.objects.keys;
|
||||
if (keySet.size() > 0) {
|
||||
String[] objectIdArray = keySet.toArray(new String[keySet.size()]);
|
||||
|
||||
for (String objectId : objectIdArray) {
|
||||
if (pluginMap.objects.containsKey(objectId)) {
|
||||
if (objectId.contains("property")) {
|
||||
Polygon polygon = (Polygon) pluginMap.objects.remove(objectId.replace("property_", ""));
|
||||
if (polygon != null) {
|
||||
polygon.remove();
|
||||
}
|
||||
}
|
||||
Object object = pluginMap.objects.remove(objectId);
|
||||
object = null;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* set fill color
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setFillColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
int color = PluginUtil.parsePluginColor(args.getJSONArray(1));
|
||||
this.setInt("setFillColor", id, color, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set stroke color
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setStrokeColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
int color = PluginUtil.parsePluginColor(args.getJSONArray(1));
|
||||
this.setInt("setStrokeColor", id, color, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set stroke width
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setStrokeWidth(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float width = (float)(args.getDouble(1) * density);
|
||||
this.setFloat("setStrokeWidth", id, width, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set z-index
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final float zIndex = (float) args.getDouble(1);
|
||||
this.setFloat("setZIndex", id, zIndex, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set geodesic
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setGeodesic(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
boolean isGeodisic = args.getBoolean(1);
|
||||
this.setBoolean("setGeodesic", id, isGeodisic, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the polygon
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
if (polygon == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
pluginMap.objects.remove(id);
|
||||
|
||||
pluginMap.objects.remove("polygon_bounds_" + polygonHashCode);
|
||||
pluginMap.objects.remove("polygon_property_" + polygonHashCode);
|
||||
pluginMap.objects.remove("polygon_path_" + polygonHashCode);
|
||||
pluginMap.objects.remove("polygon_holePaths_" + polygonHashCode);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
polygon.remove();
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void removePointAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int index = args.getInt(1);
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_path_" + polygonHashCode;
|
||||
final ArrayList<LatLng> path = (ArrayList<LatLng>)pluginMap.objects.get(propertyId);
|
||||
if (path.size() > 0) {
|
||||
path.remove(index);
|
||||
}
|
||||
pluginMap.objects.put(propertyId, path);
|
||||
|
||||
//-----------------------------------
|
||||
// Recalculate the polygon bounds
|
||||
//-----------------------------------
|
||||
propertyId = "polygon_bounds_" + polygonHashCode;
|
||||
if (path.size() > 0) {
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
} else {
|
||||
pluginMap.objects.remove(propertyId);
|
||||
}
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (path.size() > 0) {
|
||||
try {
|
||||
polygon.setPoints(path);
|
||||
} catch (Exception e) {
|
||||
// Ignore this error
|
||||
//e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
polygon.setVisible(false);
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setPoints(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String id = args.getString(0);
|
||||
final JSONArray positionList = args.getJSONArray(1);
|
||||
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_path_" + polygonHashCode;
|
||||
final ArrayList<LatLng> path = (ArrayList<LatLng>)pluginMap.objects.get(propertyId);
|
||||
path.clear();
|
||||
JSONObject position;
|
||||
for (int i = 0; i < positionList.length(); i++) {
|
||||
position = positionList.getJSONObject(i);
|
||||
path.add(new LatLng(position.getDouble("lat"), position.getDouble("lng")));
|
||||
}
|
||||
pluginMap.objects.put(propertyId, path);
|
||||
|
||||
//-----------------------------------
|
||||
// Recalculate the polygon bounds
|
||||
//-----------------------------------
|
||||
propertyId = "polygon_bounds_" + polygonHashCode;
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setPoints(path);
|
||||
if (path.size() > 0) {
|
||||
polygon.setVisible(polygon.isVisible());
|
||||
} else {
|
||||
polygon.setVisible(false);
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a point
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void insertPointAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int index = args.getInt(1);
|
||||
JSONObject position = args.getJSONObject(2);
|
||||
final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng"));
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
boolean shouldBeVisible = false;
|
||||
String propertyId = "polygon_path_" + polygonHashCode;
|
||||
final ArrayList<LatLng> path = (ArrayList<LatLng>)pluginMap.objects.get(propertyId);
|
||||
if (path.size() == 0) {
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get("polygon_property_" + polygonHashCode);
|
||||
if (properties.getBoolean("isVisible")) {
|
||||
shouldBeVisible = true;
|
||||
}
|
||||
}
|
||||
path.add(index, latLng);
|
||||
pluginMap.objects.put(propertyId, path);
|
||||
|
||||
//-----------------------------------
|
||||
// Recalculate the polygon bounds
|
||||
//-----------------------------------
|
||||
propertyId = "polygon_bounds_" + polygonHashCode;
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
|
||||
final boolean changeToVisible = shouldBeVisible;
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setPoints(path);
|
||||
if (changeToVisible) {
|
||||
polygon.setVisible(true);
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setPointAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int index = args.getInt(1);
|
||||
JSONObject position = args.getJSONObject(2);
|
||||
final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng"));
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_path_" + polygonHashCode;
|
||||
final ArrayList<LatLng> path = (ArrayList<LatLng>)pluginMap.objects.get(propertyId);
|
||||
path.set(index, latLng);
|
||||
pluginMap.objects.put(propertyId, path);
|
||||
|
||||
//-----------------------------------
|
||||
// Recalculate the polygon bounds
|
||||
//-----------------------------------
|
||||
propertyId = "polygon_bounds_" + polygonHashCode;
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setPoints(path);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setHoles(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final JSONArray holeList = args.getJSONArray(1);
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
for (int i = 0; i < holes.size(); i++) {
|
||||
holes.get(i).clear();
|
||||
}
|
||||
holes.clear();
|
||||
|
||||
JSONObject position;
|
||||
for (int i = 0; i < holeList.length(); i++) {
|
||||
ArrayList<LatLng> hole = new ArrayList<LatLng>();
|
||||
JSONArray holePositions = holeList.getJSONArray(i);
|
||||
for (int j = 0; j < holePositions.length(); j++) {
|
||||
position = holePositions.getJSONObject(j);
|
||||
hole.add(new LatLng(position.getDouble("lat"), position.getDouble("lng")));
|
||||
}
|
||||
holes.add(hole);
|
||||
}
|
||||
|
||||
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setHoles(holes);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void insertPointOfHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int holeIndex = args.getInt(1);
|
||||
final int pointIndex = args.getInt(2);
|
||||
JSONObject position = args.getJSONObject(3);
|
||||
final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng"));
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
ArrayList<LatLng> hole = null;
|
||||
if (holeIndex < holes.size()) {
|
||||
hole = holes.get(holeIndex);
|
||||
}
|
||||
if (hole == null) {
|
||||
hole = new ArrayList<LatLng>();
|
||||
}
|
||||
if (holes.size() == 0) {
|
||||
holes.add(hole);
|
||||
}
|
||||
hole.add(pointIndex, latLng);
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setHoles(holes);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setPointOfHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int holeIndex = args.getInt(1);
|
||||
final int pointIndex = args.getInt(2);
|
||||
JSONObject position = args.getJSONObject(3);
|
||||
final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng"));
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
ArrayList<LatLng> hole = null;
|
||||
if (holeIndex < holes.size()) {
|
||||
hole = holes.get(holeIndex);
|
||||
}
|
||||
if (hole == null) {
|
||||
hole = new ArrayList<LatLng>();
|
||||
}
|
||||
if (holes.size() == 0) {
|
||||
holes.add(hole);
|
||||
}
|
||||
hole.set(pointIndex, latLng);
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
final ArrayList<LatLng> newHole = hole;
|
||||
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setHoles(holes);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void removePointOfHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int holeIndex = args.getInt(1);
|
||||
final int pointIndex = args.getInt(2);
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
ArrayList<LatLng> hole = null;
|
||||
if (holeIndex < holes.size()) {
|
||||
hole = holes.get(holeIndex);
|
||||
}
|
||||
if (hole == null) {
|
||||
hole = new ArrayList<LatLng>();
|
||||
}
|
||||
if (holes.size() == 0) {
|
||||
holes.add(hole);
|
||||
}
|
||||
hole.remove(pointIndex);
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
final ArrayList<LatLng> newHole = hole;
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setHoles(holes);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void insertHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int holeIndex = args.getInt(1);
|
||||
|
||||
JSONArray holeJson = args.getJSONArray(2);
|
||||
final ArrayList<LatLng> newHole = PluginUtil.JSONArray2LatLngList(holeJson);
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
holes.add(holeIndex, newHole);
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
try {
|
||||
polygon.setHoles(holes);
|
||||
} catch (Exception e) {
|
||||
// Ignore this error
|
||||
//e.printStackTrace();
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int holeIndex = args.getInt(1);
|
||||
|
||||
JSONArray holeJson = args.getJSONArray(2);
|
||||
final ArrayList<LatLng> newHole = PluginUtil.JSONArray2LatLngList(holeJson);
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
holes.set(holeIndex, newHole);
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setHoles(holes);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set points
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void removeHoleAt(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final int holeIndex = args.getInt(1);
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
//------------------------
|
||||
// Update the hole list
|
||||
//------------------------
|
||||
String propertyId = "polygon_holePaths_" + polygonHashCode;
|
||||
final ArrayList<ArrayList<LatLng>> holes = (ArrayList<ArrayList<LatLng>>) pluginMap.objects.get(propertyId);
|
||||
holes.remove(holeIndex);
|
||||
pluginMap.objects.put(propertyId, holes);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Update the polygon
|
||||
polygon.setHoles(holes);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Set visibility for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
final boolean isVisible = args.getBoolean(1);
|
||||
String id = args.getString(0);
|
||||
|
||||
final Polygon polygon = this.getPolygon(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
polygon.setVisible(isVisible);
|
||||
}
|
||||
});
|
||||
String propertyId = "polygon_property_" + polygonHashCode;
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isVisible", isVisible);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clickable for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean clickable = args.getBoolean(1);
|
||||
String propertyId = id.replace("polygon_", "polygon_property_");
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isClickable", clickable);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.maps.model.CircleOptions;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
import com.google.android.gms.maps.model.LatLngBounds;
|
||||
import com.google.android.gms.maps.model.MarkerOptions;
|
||||
import com.google.android.gms.maps.model.Polyline;
|
||||
import com.google.android.gms.maps.model.PolylineOptions;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PluginPolyline extends MyPlugin implements MyPluginInterface {
|
||||
private String polylineHashCode;
|
||||
|
||||
/**
|
||||
* Create polyline
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void create(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
self = this;
|
||||
|
||||
final PolylineOptions polylineOptions = new PolylineOptions();
|
||||
int color;
|
||||
final LatLngBounds.Builder builder = new LatLngBounds.Builder();
|
||||
final JSONObject properties = new JSONObject();
|
||||
|
||||
JSONObject opts = args.getJSONObject(1);
|
||||
final String hashCode = args.getString(2);
|
||||
polylineHashCode = hashCode;
|
||||
|
||||
if (opts.has("points")) {
|
||||
JSONArray points = opts.getJSONArray("points");
|
||||
List<LatLng> path = PluginUtil.JSONArray2LatLngList(points);
|
||||
int i = 0;
|
||||
for (i = 0; i < path.size(); i++) {
|
||||
polylineOptions.add(path.get(i));
|
||||
builder.include(path.get(i));
|
||||
}
|
||||
}
|
||||
if (opts.has("color")) {
|
||||
color = PluginUtil.parsePluginColor(opts.getJSONArray("color"));
|
||||
polylineOptions.color(color);
|
||||
}
|
||||
if (opts.has("width")) {
|
||||
polylineOptions.width((float)(opts.getDouble("width") * density));
|
||||
}
|
||||
if (opts.has("visible")) {
|
||||
polylineOptions.visible(opts.getBoolean("visible"));
|
||||
}
|
||||
if (opts.has("geodesic")) {
|
||||
polylineOptions.geodesic(opts.getBoolean("geodesic"));
|
||||
}
|
||||
if (opts.has("zIndex")) {
|
||||
polylineOptions.zIndex(opts.getInt("zIndex"));
|
||||
}
|
||||
if (opts.has("clickable")) {
|
||||
properties.put("isClickable", opts.getBoolean("clickable"));
|
||||
} else {
|
||||
properties.put("isClickable", true);
|
||||
}
|
||||
properties.put("isVisible", polylineOptions.isVisible());
|
||||
|
||||
// Since this plugin provide own click detection,
|
||||
// disable default clickable feature.
|
||||
polylineOptions.clickable(false);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
Polyline polyline = map.addPolyline(polylineOptions);
|
||||
polyline.setTag(hashCode);
|
||||
String id = "polyline_" + hashCode;
|
||||
pluginMap.objects.put(id, polyline);
|
||||
|
||||
String boundsId = "polyline_bounds_" + hashCode;
|
||||
pluginMap.objects.put(boundsId, builder.build());
|
||||
|
||||
String propertyId = "polyline_property_" + hashCode;
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
|
||||
try {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("hashCode", hashCode);
|
||||
result.put("__pgmId", id);
|
||||
callbackContext.success(result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* set color
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setStrokeColor(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
int color = PluginUtil.parsePluginColor(args.getJSONArray(1));
|
||||
this.setInt("setColor", id, color, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set width
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setStrokeWidth(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float width = (float)(args.getDouble(1) * density);
|
||||
this.setFloat("setWidth", id, width, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* set z-index
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float zIndex = (float) args.getDouble(1);
|
||||
this.setFloat("setZIndex", id, zIndex, callbackContext);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove the polyline
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final Polyline polyline = this.getPolyline(id);
|
||||
if (polyline == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
pluginMap.objects.remove(id);
|
||||
|
||||
id = "polyline_bounds_" + polylineHashCode;
|
||||
pluginMap.objects.remove(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
polyline.remove();
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
public void setPoints(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String id = args.getString(0);
|
||||
final JSONArray positionList = args.getJSONArray(1);
|
||||
|
||||
final Polyline polyline = this.getPolyline(id);
|
||||
// Recalculate the polygon bounds
|
||||
final String propertyId = "polyline_bounds_" + polylineHashCode;
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
List<LatLng> path = polyline.getPoints();
|
||||
path.clear();
|
||||
JSONObject position;
|
||||
for (int i = 0; i < positionList.length(); i++) {
|
||||
position = positionList.getJSONObject(i);
|
||||
path.add(new LatLng(position.getDouble("lat"), position.getDouble("lng")));
|
||||
}
|
||||
polyline.setPoints(path);
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
public void removePointAt(final JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String id = args.getString(0);
|
||||
final int index = args.getInt(1);
|
||||
final Polyline polyline = this.getPolyline(id);
|
||||
|
||||
// Recalculate the polygon bounds
|
||||
final String propertyId = "polyline_bounds_" + polylineHashCode;
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<LatLng> path = polyline.getPoints();
|
||||
if (path.size() > index) {
|
||||
path.remove(index);
|
||||
if (path.size() > 0) {
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
} else {
|
||||
pluginMap.objects.remove(propertyId);
|
||||
}
|
||||
|
||||
polyline.setPoints(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
callbackContext.success();
|
||||
}
|
||||
public void insertPointAt(final JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String id = args.getString(0);
|
||||
final int index = args.getInt(1);
|
||||
JSONObject position = args.getJSONObject(2);
|
||||
final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng"));
|
||||
|
||||
final Polyline polyline = this.getPolyline(id);
|
||||
|
||||
|
||||
// Recalculate the polygon bounds
|
||||
final String propertyId = "polyline_bounds_" + polylineHashCode;
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<LatLng> path = polyline.getPoints();
|
||||
if (path.size() >= index) {
|
||||
path.add(index, latLng);
|
||||
polyline.setPoints(path);
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
}
|
||||
}
|
||||
});
|
||||
callbackContext.success();
|
||||
}
|
||||
public void setPointAt(final JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
String id = args.getString(0);
|
||||
final int index = args.getInt(1);
|
||||
JSONObject position = args.getJSONObject(2);
|
||||
final LatLng latLng = new LatLng(position.getDouble("lat"), position.getDouble("lng"));
|
||||
|
||||
|
||||
final Polyline polyline = this.getPolyline(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<LatLng> path = polyline.getPoints();
|
||||
if (path.size() > index) {
|
||||
path.set(index, latLng);
|
||||
|
||||
// Recalculate the polygon bounds
|
||||
String propertyId = "polyline_bounds_" + polylineHashCode;
|
||||
pluginMap.objects.put(propertyId, PluginUtil.getBoundsFromPath(path));
|
||||
|
||||
polyline.setPoints(path);
|
||||
}
|
||||
}
|
||||
});
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* set geodesic
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setGeodesic(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
boolean isGeodisic = args.getBoolean(1);
|
||||
this.setBoolean("setGeodesic", id, isGeodisic, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean isVisible = args.getBoolean(1);
|
||||
|
||||
final Polyline polyline = this.getPolyline(id);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
polyline.setVisible(isVisible);
|
||||
}
|
||||
});
|
||||
String propertyId = "polyline_property_" + polylineHashCode;
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isVisible", isVisible);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set clickable for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setClickable(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
final boolean clickable = args.getBoolean(1);
|
||||
String propertyId = id.replace("polyline_", "polyline_property_");
|
||||
JSONObject properties = (JSONObject)pluginMap.objects.get(propertyId);
|
||||
properties.put("isClickable", clickable);
|
||||
pluginMap.objects.put(propertyId, properties);
|
||||
callbackContext.success();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Point;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.android.gms.maps.OnStreetViewPanoramaReadyCallback;
|
||||
import com.google.android.gms.maps.StreetViewPanorama;
|
||||
import com.google.android.gms.maps.StreetViewPanoramaOptions;
|
||||
import com.google.android.gms.maps.StreetViewPanoramaView;
|
||||
import com.google.android.gms.maps.model.LatLng;
|
||||
import com.google.android.gms.maps.model.StreetViewPanoramaCamera;
|
||||
import com.google.android.gms.maps.model.StreetViewPanoramaLink;
|
||||
import com.google.android.gms.maps.model.StreetViewPanoramaLocation;
|
||||
import com.google.android.gms.maps.model.StreetViewPanoramaOrientation;
|
||||
import com.google.android.gms.maps.model.StreetViewSource;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class PluginStreetViewPanorama extends MyPlugin implements
|
||||
IPluginView, StreetViewPanorama.OnStreetViewPanoramaCameraChangeListener,
|
||||
StreetViewPanorama.OnStreetViewPanoramaChangeListener,
|
||||
StreetViewPanorama.OnStreetViewPanoramaClickListener {
|
||||
|
||||
private Activity mActivity;
|
||||
private StreetViewPanoramaView panoramaView;
|
||||
private StreetViewPanorama panorama;
|
||||
private String panoramaId;
|
||||
private boolean isVisible = true;
|
||||
private boolean isClickable = true;
|
||||
private final String TAG = "StreetView";
|
||||
private String divId;
|
||||
private int viewDepth = 0;
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, final CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
mActivity = cordova.getActivity();
|
||||
}
|
||||
|
||||
public int getViewDepth() {
|
||||
return viewDepth;
|
||||
}
|
||||
public String getDivId() {
|
||||
return this.divId;
|
||||
}
|
||||
public String getOverlayId() {
|
||||
return this.panoramaId;
|
||||
}
|
||||
public ViewGroup getView() {
|
||||
return this.panoramaView;
|
||||
}
|
||||
|
||||
|
||||
public boolean getVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
public boolean getClickable() {
|
||||
return isClickable;
|
||||
}
|
||||
public void getPanorama(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
JSONObject meta = args.getJSONObject(0);
|
||||
panoramaId = meta.getString("__pgmId");
|
||||
viewDepth = meta.getInt("depth");
|
||||
|
||||
JSONObject jsOptions = args.getJSONObject(1);
|
||||
divId = args.getString(2);
|
||||
|
||||
StreetViewPanoramaOptions svOptions = new StreetViewPanoramaOptions();
|
||||
if (jsOptions.has("camera")) {
|
||||
JSONObject cameraOpts = jsOptions.getJSONObject("camera");
|
||||
Object target = cameraOpts.get("target");
|
||||
if (target instanceof JSONObject) {
|
||||
JSONObject targetJson = cameraOpts.getJSONObject("target");
|
||||
LatLng position = new LatLng(targetJson.getDouble("lat"), targetJson.getDouble("lng"));
|
||||
|
||||
if (cameraOpts.has("source")) {
|
||||
StreetViewSource source = "OUTDOOR".equals(cameraOpts.getString("source")) ?
|
||||
StreetViewSource.OUTDOOR : StreetViewSource.DEFAULT;
|
||||
if (cameraOpts.has("radius")) {
|
||||
svOptions.position(position, cameraOpts.getInt("radius"), source);
|
||||
} else {
|
||||
svOptions.position(position, source);
|
||||
}
|
||||
} else {
|
||||
if (cameraOpts.has("radius")) {
|
||||
svOptions.position(position, cameraOpts.getInt("radius"));
|
||||
} else {
|
||||
svOptions.position(position);
|
||||
}
|
||||
}
|
||||
} else if (target instanceof String) {
|
||||
svOptions.panoramaId(cameraOpts.getString("target"));
|
||||
}
|
||||
|
||||
if (cameraOpts.has("bearing") ||
|
||||
cameraOpts.has("tilt") ||
|
||||
cameraOpts.has("zoom")) {
|
||||
StreetViewPanoramaCamera.Builder builder = StreetViewPanoramaCamera.builder();
|
||||
if (cameraOpts.has("bearing")) {
|
||||
builder.bearing = (float) cameraOpts.getDouble("bearing");
|
||||
}
|
||||
if (cameraOpts.has("tilt")) {
|
||||
builder.tilt = (float) cameraOpts.getDouble("tilt");
|
||||
}
|
||||
if (cameraOpts.has("zoom")) {
|
||||
builder.zoom = (float) cameraOpts.getDouble("zoom");
|
||||
}
|
||||
svOptions.panoramaCamera(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
if (jsOptions.has("gestures")) {
|
||||
JSONObject gestures = jsOptions.getJSONObject("gestures");
|
||||
if (gestures.has("panning")) {
|
||||
svOptions.panningGesturesEnabled(gestures.getBoolean("panning"));
|
||||
}
|
||||
if (gestures.has("zoom")) {
|
||||
svOptions.zoomGesturesEnabled(gestures.getBoolean("zoom"));
|
||||
}
|
||||
}
|
||||
|
||||
if (jsOptions.has("controls")) {
|
||||
JSONObject controls = jsOptions.getJSONObject("controls");
|
||||
if (controls.has("navigation")) {
|
||||
svOptions.userNavigationEnabled(controls.getBoolean("navigation"));
|
||||
}
|
||||
if (controls.has("streetNames")) {
|
||||
svOptions.streetNamesEnabled(controls.getBoolean("streetNames"));
|
||||
}
|
||||
}
|
||||
panoramaView = new StreetViewPanoramaView(mActivity, svOptions);
|
||||
|
||||
mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panoramaView.onCreate(null);
|
||||
panoramaView.setTag(getViewDepth());
|
||||
|
||||
panoramaView.getStreetViewPanoramaAsync(new OnStreetViewPanoramaReadyCallback() {
|
||||
@Override
|
||||
public void onStreetViewPanoramaReady(StreetViewPanorama streetViewPanorama) {
|
||||
panoramaView.onResume();
|
||||
panorama = streetViewPanorama;
|
||||
|
||||
panorama.setOnStreetViewPanoramaCameraChangeListener(PluginStreetViewPanorama.this);
|
||||
panorama.setOnStreetViewPanoramaChangeListener(PluginStreetViewPanorama.this);
|
||||
panorama.setOnStreetViewPanoramaClickListener(PluginStreetViewPanorama.this);
|
||||
|
||||
// Don't support this because iOS does not support this feature.
|
||||
//panorama.setOnStreetViewPanoramaLongClickListener(PluginStreetViewPanorama.this);
|
||||
|
||||
|
||||
mapCtrl.mPluginLayout.addPluginOverlay(PluginStreetViewPanorama.this);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause(boolean multitasking) {
|
||||
super.onPause(multitasking);
|
||||
if (panoramaView != null && panoramaView.isActivated()) {
|
||||
panoramaView.onPause();
|
||||
}
|
||||
|
||||
//mapCtrl.mPluginLayout.removePluginOverlay(this.panoramaId);
|
||||
|
||||
}
|
||||
@Override
|
||||
public void onResume(boolean multitasking) {
|
||||
super.onResume(multitasking);
|
||||
if (panoramaView != null && panoramaView.isActivated()) {
|
||||
panoramaView.onResume();
|
||||
}
|
||||
//mapCtrl.mPluginLayout.addPluginOverlay(PluginStreetViewPanorama.this);
|
||||
}
|
||||
|
||||
public void attachToWebView(JSONArray args, final CallbackContext callbackContext) {
|
||||
mapCtrl.mPluginLayout.addPluginOverlay(this);
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
public void detachFromWebView(JSONArray args, final CallbackContext callbackContext) {
|
||||
mapCtrl.mPluginLayout.removePluginOverlay(this.panoramaId);
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
public void setPanningGesturesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final boolean boolValue = args.getBoolean(0);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panorama.setPanningGesturesEnabled(boolValue);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setZoomGesturesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final boolean boolValue = args.getBoolean(0);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panorama.setZoomGesturesEnabled(boolValue);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setNavigationEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final boolean boolValue = args.getBoolean(0);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panorama.setUserNavigationEnabled(boolValue);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setStreetNamesEnabled(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final boolean boolValue = args.getBoolean(0);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panorama.setStreetNamesEnabled(boolValue);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
public void setVisible(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final boolean boolValue = args.getBoolean(0);
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panoramaView.setVisibility(boolValue ? View.VISIBLE : View.INVISIBLE);
|
||||
callbackContext.success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void setPosition(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
JSONObject cameraOpts = args.getJSONObject(0);
|
||||
Object target = cameraOpts.get("target");
|
||||
|
||||
if (target instanceof JSONObject) {
|
||||
JSONObject targetJson = (JSONObject)target;
|
||||
LatLng position = new LatLng(targetJson.getDouble("lat"), targetJson.getDouble("lng"));
|
||||
|
||||
if (cameraOpts.has("source")) {
|
||||
StreetViewSource source = "OUTDOOR".equals(cameraOpts.getString("source")) ?
|
||||
StreetViewSource.OUTDOOR : StreetViewSource.DEFAULT;
|
||||
if (cameraOpts.has("radius")) {
|
||||
panorama.setPosition(position, cameraOpts.getInt("radius"), source);
|
||||
} else {
|
||||
panorama.setPosition(position, source);
|
||||
}
|
||||
} else {
|
||||
if (cameraOpts.has("radius")) {
|
||||
panorama.setPosition(position, cameraOpts.getInt("radius"));
|
||||
} else {
|
||||
panorama.setPosition(position);
|
||||
}
|
||||
}
|
||||
} else if (target instanceof String) {
|
||||
panorama.setPosition(cameraOpts.getString("target"));
|
||||
}
|
||||
|
||||
callbackContext.success();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("" + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setPov(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
final JSONObject cameraPosition = args.getJSONObject(0);
|
||||
|
||||
StreetViewPanoramaCamera currentCamera = panorama.getPanoramaCamera();
|
||||
float bearing = cameraPosition.has("bearing") ? (float) cameraPosition.getDouble("bearing") : currentCamera.bearing;
|
||||
float tilt = cameraPosition.has("tilt") ? (float) cameraPosition.getDouble("tilt") : currentCamera.tilt;
|
||||
float zoom = cameraPosition.has("zoom") ? (float) cameraPosition.getDouble("zoom") : currentCamera.zoom;
|
||||
long duration = cameraPosition.has("duration") ? (long) cameraPosition.getDouble("duration") : 1000;
|
||||
|
||||
StreetViewPanoramaCamera newCamera = new StreetViewPanoramaCamera.Builder()
|
||||
.bearing(bearing)
|
||||
.zoom(zoom)
|
||||
.tilt(tilt)
|
||||
.build();
|
||||
panorama.animateTo(newCamera, duration);
|
||||
|
||||
callbackContext.success();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("" + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void remove(JSONArray args, final CallbackContext callbackContext) {
|
||||
mapCtrl.mPluginLayout.removePluginOverlay(this.panoramaId);
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
panorama.setOnStreetViewPanoramaLongClickListener(null);
|
||||
panorama.setOnStreetViewPanoramaClickListener(null);
|
||||
panorama.setOnStreetViewPanoramaChangeListener(null);
|
||||
panorama.setOnStreetViewPanoramaCameraChangeListener(null);
|
||||
|
||||
System.gc();
|
||||
Runtime.getRuntime().gc();
|
||||
if (callbackContext != null) {
|
||||
callbackContext.success();
|
||||
}
|
||||
PluginStreetViewPanorama.this.onDestroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreetViewPanoramaCameraChange(StreetViewPanoramaCamera streetViewPanoramaCamera) {
|
||||
try {
|
||||
JSONObject camera = new JSONObject();
|
||||
camera.put("bearing", streetViewPanoramaCamera.bearing);
|
||||
camera.put("tilt", streetViewPanoramaCamera.tilt);
|
||||
camera.put("zoom", streetViewPanoramaCamera.zoom);
|
||||
|
||||
// StreetViewPanoramaOrientation svOrientation = streetViewPanoramaCamera.getOrientation();
|
||||
// JSONObject orientation = new JSONObject();
|
||||
// orientation.put("bearing", svOrientation.bearing);
|
||||
// orientation.put("tilt", svOrientation.tilt);
|
||||
// camera.put("orientation", orientation);
|
||||
|
||||
String jsonStr = camera.toString(0);
|
||||
jsCallback(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onPanoramaCameraChange', args: [%s]});}",
|
||||
panoramaId, panoramaId, "panorama_camera_change", jsonStr));
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void jsCallback(final String js) {
|
||||
this.mActivity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.loadUrl(js);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreetViewPanoramaChange(StreetViewPanoramaLocation streetViewPanoramaLocation) {
|
||||
try {
|
||||
String jsonStr = "null";
|
||||
if (streetViewPanoramaLocation != null) {
|
||||
JSONObject location = new JSONObject();
|
||||
location.put("panoId", streetViewPanoramaLocation.panoId);
|
||||
|
||||
JSONObject position = new JSONObject();
|
||||
position.put("lat", streetViewPanoramaLocation.position.latitude);
|
||||
position.put("lng", streetViewPanoramaLocation.position.longitude);
|
||||
location.put("latLng", position);
|
||||
|
||||
JSONArray links = new JSONArray();
|
||||
for (StreetViewPanoramaLink stLink : streetViewPanoramaLocation.links) {
|
||||
JSONObject link = new JSONObject();
|
||||
link.put("panoId", stLink.panoId);
|
||||
link.put("bearing", stLink.bearing);
|
||||
links.put(link);
|
||||
}
|
||||
location.put("links", links);
|
||||
|
||||
jsonStr = location.toString(0);
|
||||
}
|
||||
jsCallback(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onPanoramaLocationChange', args: [%s]});}",
|
||||
panoramaId, panoramaId, "panorama_location_change", jsonStr));
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStreetViewPanoramaClick(StreetViewPanoramaOrientation streetViewPanoramaOrientation) {
|
||||
|
||||
try {
|
||||
JSONObject clickInfo = new JSONObject();
|
||||
JSONObject orientation = new JSONObject();
|
||||
orientation.put("bearing", streetViewPanoramaOrientation.bearing);
|
||||
orientation.put("tilt", streetViewPanoramaOrientation.tilt);
|
||||
clickInfo.put("orientation", orientation);
|
||||
|
||||
Point point = panorama.orientationToPoint(streetViewPanoramaOrientation);
|
||||
JSONArray pointArray = new JSONArray();
|
||||
pointArray.put((int)((double)point.x / (double)density));
|
||||
pointArray.put((int)((double)point.y / (double)density));
|
||||
clickInfo.put("point", pointArray);
|
||||
|
||||
String jsonStr = clickInfo.toString(0);
|
||||
jsCallback(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"javascript:if('%s' in plugin.google.maps){plugin.google.maps['%s']({evtName:'%s', callback:'_onPanoramaEvent', args: [%s]});}",
|
||||
panoramaId, panoramaId, "panorama_click", jsonStr));
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.view.View;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.google.android.gms.maps.model.TileOverlay;
|
||||
import com.google.android.gms.maps.model.TileOverlayOptions;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPreferences;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class PluginTileOverlay extends MyPlugin implements MyPluginInterface {
|
||||
|
||||
|
||||
/**
|
||||
* Create tile overlay
|
||||
*
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void create(final JSONArray args,
|
||||
final CallbackContext callbackContext) throws JSONException {
|
||||
|
||||
|
||||
final JSONObject opts = args.getJSONObject(1);
|
||||
final String id = args.getString(2);
|
||||
final int tileSize = opts.getInt("tileSize");
|
||||
|
||||
|
||||
final TileOverlayOptions options = new TileOverlayOptions();
|
||||
if (opts.has("zIndex")) {
|
||||
options.zIndex((float)opts.getDouble("zIndex"));
|
||||
}
|
||||
if (opts.has("visible")) {
|
||||
options.visible(opts.getBoolean("visible"));
|
||||
}
|
||||
if (opts.has("opacity")) {
|
||||
options.transparency((float)(1 - opts.getDouble("opacity")));
|
||||
}
|
||||
|
||||
boolean isDebug = false;
|
||||
if (opts.has("debug")) {
|
||||
isDebug = opts.getBoolean("debug");
|
||||
}
|
||||
final boolean _isDebug = isDebug;
|
||||
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
String userAgent = "";
|
||||
if (opts.has("userAgent")) {
|
||||
try {
|
||||
userAgent = opts.getString("userAgent");
|
||||
} catch (JSONException e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if ("".equals(userAgent)) {
|
||||
View browserView = webView.getView();
|
||||
String browserViewName = browserView.getClass().getName();
|
||||
if ("org.xwalk.core.XWalkView".equals(browserViewName) ||
|
||||
"org.crosswalk.engine.XWalkCordovaView".equals(browserViewName)) {
|
||||
|
||||
CordovaPreferences preferences = webView.getPreferences();
|
||||
// Set xwalk webview settings by Cordova preferences.
|
||||
String xwalkUserAgent = preferences == null ? "" : preferences.getString("xwalkUserAgent", "");
|
||||
if (!xwalkUserAgent.isEmpty()) {
|
||||
userAgent = xwalkUserAgent;
|
||||
}
|
||||
|
||||
String appendUserAgent = preferences.getString("AppendUserAgent", "");
|
||||
if (!appendUserAgent.isEmpty()) {
|
||||
userAgent = userAgent + " " + appendUserAgent;
|
||||
}
|
||||
} else {
|
||||
userAgent = ((WebView) webView.getEngine().getView()).getSettings().getUserAgentString();
|
||||
}
|
||||
}
|
||||
|
||||
String currentPageUrl = webView.getUrl();
|
||||
|
||||
AssetManager assetManager = cordova.getActivity().getAssets();
|
||||
final PluginTileProvider tileProvider = new PluginTileProvider(pluginMap.getOverlayId(), id, webView, assetManager, currentPageUrl, userAgent, tileSize, _isDebug);
|
||||
tileProvider.setOnCacheClear(new PluginTileProvider.OnCacheClear() {
|
||||
@Override
|
||||
public void onCacheClear(int hashCode) {
|
||||
TileOverlay tileOverlay = (TileOverlay)PluginTileOverlay.this.getTileOverlay(hashCode+"");
|
||||
if (tileOverlay != null) {
|
||||
tileOverlay.clearTileCache();
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
});
|
||||
options.tileProvider(tileProvider);
|
||||
|
||||
|
||||
TileOverlay tileOverlay = map.addTileOverlay(options);
|
||||
//String id = tileOverlay.getId();
|
||||
|
||||
pluginMap.objects.put("tileoverlay_" + id, tileOverlay);
|
||||
pluginMap.objects.put("tileprovider_" + id, tileProvider);
|
||||
|
||||
try {
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("hashCode", id);
|
||||
result.put("__pgmId", "tileoverlay_" + id);
|
||||
callbackContext.success(result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("" + e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void onGetTileUrlFromJS(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
String urlKey = args.getString(1);
|
||||
String tileUrl = args.getString(2);
|
||||
String pluginId = "tileprovider_" + id;
|
||||
if (pluginMap.objects.containsKey(pluginId)) {
|
||||
((PluginTileProvider)(this.pluginMap.objects.get(pluginId))).onGetTileUrlFromJS(urlKey, tileUrl);
|
||||
}
|
||||
callbackContext.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* set z-index
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setZIndex(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
float zIndex = (float) args.getDouble(1);
|
||||
this.setFloat("setZIndex", id, zIndex, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set visibility for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setVisible(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
boolean visible = args.getBoolean(1);
|
||||
String id = args.getString(0);
|
||||
this.setBoolean("setVisible", id, visible, callbackContext);
|
||||
}
|
||||
/**
|
||||
* Remove this tile layer
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void remove(final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
String id = args.getString(0);
|
||||
TileOverlay tileOverlay = (TileOverlay)pluginMap.objects.get(id);
|
||||
if (tileOverlay == null) {
|
||||
callbackContext.success();
|
||||
return;
|
||||
}
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
String id = args.getString(0);
|
||||
TileOverlay tileOverlay = (TileOverlay)pluginMap.objects.remove(id);
|
||||
if (tileOverlay != null) {
|
||||
tileOverlay.clearTileCache();
|
||||
tileOverlay.remove();
|
||||
tileOverlay = null;
|
||||
}
|
||||
id = id.replace("tileoverlay_", "tileprovider_");
|
||||
|
||||
if (pluginMap.objects.containsKey(id)) {
|
||||
((PluginTileProvider)(pluginMap.objects.get(id))).remove();
|
||||
}
|
||||
//pluginMap.objects.put(id, null);
|
||||
PluginTileProvider provider = (PluginTileProvider) pluginMap.objects.remove(id);
|
||||
provider = null;
|
||||
callbackContext.success();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
callbackContext.error("" + e.getMessage());
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set fadeIn for the object
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setFadeIn(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
boolean visible = args.getBoolean(1);
|
||||
String id = args.getString(0);
|
||||
this.setBoolean("setFadeIn", id, visible, callbackContext);
|
||||
}
|
||||
/**
|
||||
* Set opacity for the tile layer
|
||||
* @param args
|
||||
* @param callbackContext
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void setOpacity(JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
double opacity = 1 - args.getDouble(1);
|
||||
String id = args.getString(0);
|
||||
this.setFloat("setTransparency", id, (float)opacity, callbackContext);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.maps.model.Tile;
|
||||
import com.google.android.gms.maps.model.TileProvider;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
public class PluginTileProvider implements TileProvider {
|
||||
private final String TAG = "TileProvider";
|
||||
private int tileSize = 512;
|
||||
private Paint tilePaint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||
private Paint debugPaint = null;
|
||||
private TextPaint debugTextPaint = null;
|
||||
private String userAgent = null;
|
||||
private static BitmapCache tileCache = null;
|
||||
private OnCacheClear listener = null;
|
||||
private String webPageUrl = null;
|
||||
private AssetManager assetManager;
|
||||
private CordovaWebView webView;
|
||||
private String mapId, pluginId;
|
||||
private final HashMap<String, String> tileUrlMap = new HashMap<String, String>();
|
||||
private boolean isDebug = false;
|
||||
private Handler handler;
|
||||
private final Object semaphore = new Object();
|
||||
private Bitmap emptyBitmap = null;
|
||||
private final HashSet<String> cacheKeys = new HashSet<String>();
|
||||
private boolean isRemoved = false;
|
||||
|
||||
@SuppressLint({"NewApi", "JavascriptInterface"})
|
||||
public PluginTileProvider(String mapId, String pluginId, CordovaWebView webView, AssetManager assetManager, String webPageUrl, String userAgent, int tileSize, boolean isDebug) {
|
||||
this.tileSize = tileSize;
|
||||
//this.tilePaint.setAlpha((int) (opacity * 255));
|
||||
this.userAgent = userAgent == null ? "Mozilla" : userAgent;
|
||||
this.webPageUrl = webPageUrl;
|
||||
this.assetManager = assetManager;
|
||||
this.webView = webView;
|
||||
this.mapId = mapId;
|
||||
this.pluginId = pluginId;
|
||||
|
||||
// Get max available VM memory, exceeding this amount will throw an
|
||||
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
|
||||
// int in its constructor.
|
||||
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
|
||||
|
||||
// Use 1/8th of the available memory for this memory cache.
|
||||
int cacheSize = maxMemory / 8;
|
||||
|
||||
tileCache = new BitmapCache(cacheSize);
|
||||
|
||||
this.isDebug = isDebug;
|
||||
if (isDebug) {
|
||||
debugPaint = new Paint();
|
||||
debugPaint.setTextSize(20);
|
||||
debugPaint.setColor(Color.RED);
|
||||
debugPaint.setStrokeWidth(1);
|
||||
debugPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
debugTextPaint = new TextPaint();
|
||||
debugTextPaint.setTextSize(20);
|
||||
debugTextPaint.setColor(Color.RED);
|
||||
debugTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
}
|
||||
|
||||
handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
}
|
||||
|
||||
public interface OnCacheClear {
|
||||
public void onCacheClear(int hashCode);
|
||||
}
|
||||
|
||||
|
||||
public void onGetTileUrlFromJS(String urlKey, String tileUrl) {
|
||||
synchronized (tileUrlMap) {
|
||||
tileUrlMap.put(urlKey, tileUrl);
|
||||
}
|
||||
synchronized (semaphore) {
|
||||
semaphore.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
isRemoved = true;
|
||||
synchronized (cacheKeys) {
|
||||
Iterator<String> iterator = cacheKeys.iterator();
|
||||
String cacheKey;
|
||||
Bitmap image;
|
||||
while(iterator.hasNext()) {
|
||||
cacheKey = iterator.next();
|
||||
image = tileCache.remove(cacheKey);
|
||||
if (image != null && !image.isRecycled()) {
|
||||
image.recycle();
|
||||
image = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
tileCache.evictAll();
|
||||
|
||||
}
|
||||
public void setOnCacheClear(OnCacheClear listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getTile(int x, int y, int zoom) {
|
||||
if (isRemoved) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String urlStr = null;
|
||||
String originalUrlStr = null;
|
||||
final String urlKey = String.format(Locale.US, "%s-%s-%d-%d-%d", mapId, pluginId, x, y, zoom);
|
||||
synchronized (semaphore) {
|
||||
final String js = String.format(Locale.ENGLISH, "javascript:if(window.cordova){cordova.fireDocumentEvent('%s-%s-tileoverlay', {key: \"%s\", x: %d, y: %d, zoom: %d});}",
|
||||
mapId, pluginId, urlKey, x, y, zoom);
|
||||
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
webView.loadUrl(js);
|
||||
}
|
||||
});
|
||||
try {
|
||||
semaphore.wait(10000); // Maximum wait 10sec
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
synchronized (tileUrlMap) {
|
||||
urlStr = tileUrlMap.remove(urlKey);
|
||||
}
|
||||
originalUrlStr = urlStr;
|
||||
|
||||
Tile tile = null;
|
||||
if (urlStr == null || "(null)".equals(urlStr)) {
|
||||
if (isDebug) {
|
||||
if (emptyBitmap == null) {
|
||||
emptyBitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888);
|
||||
}
|
||||
Bitmap dummyBitmap = emptyBitmap.copy(Config.ARGB_8888, true);
|
||||
drawDebugInfo(dummyBitmap, x, y, zoom, originalUrlStr);
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(dummyBitmap));
|
||||
dummyBitmap.recycle();
|
||||
return tile;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlStr.indexOf("data:image/") == 0 && urlStr.contains(";base64,")) {
|
||||
String[] tmp = urlStr.split(",");
|
||||
Bitmap image = PluginUtil.getBitmapFromBase64encodedImage(tmp[1]);
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(image));
|
||||
image.recycle();
|
||||
image = null;
|
||||
return tile;
|
||||
}
|
||||
|
||||
if (urlStr.startsWith("http://localhost") ||
|
||||
urlStr.startsWith("http://127.0.0.1")) {
|
||||
if (urlStr.contains("://")) {
|
||||
urlStr = urlStr.replaceAll("http://.+?/", "file:///android_asset/www/");
|
||||
} else {
|
||||
// Avoid WebViewLocalServer (because can not make a connection for some reason)
|
||||
urlStr = "file:///android_asset/www/".concat(urlStr);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream inputStream = null;
|
||||
if (urlStr.startsWith("http://") || urlStr.startsWith("https://")) {
|
||||
//-------------------------------
|
||||
// load image from the internet
|
||||
//-------------------------------
|
||||
|
||||
boolean redirect = true;
|
||||
URL url = new URL(urlStr);
|
||||
String cacheKey = url.hashCode() + "";
|
||||
Bitmap cachedImage = tileCache.get(cacheKey);
|
||||
if (cachedImage != null && !cachedImage.isRecycled()) {
|
||||
if (isDebug) {
|
||||
Bitmap copyImage = cachedImage.copy(Config.ARGB_8888, true);
|
||||
drawDebugInfo(copyImage, x, y, zoom, originalUrlStr);
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(copyImage));
|
||||
copyImage.recycle();
|
||||
} else {
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(cachedImage));
|
||||
}
|
||||
//cachedImage.recycle(); // Don't recycle it. Need to keep the bitmap instance
|
||||
return tile;
|
||||
}
|
||||
|
||||
HttpURLConnection http = null;
|
||||
String cookies = null;
|
||||
int redirectCnt = 0;
|
||||
while(redirect && redirectCnt < 10) {
|
||||
redirect = false;
|
||||
http = (HttpURLConnection)url.openConnection();
|
||||
http.setRequestMethod("GET");
|
||||
http.setReadTimeout(3000);
|
||||
http.setConnectTimeout(3000);
|
||||
if (cookies != null) {
|
||||
http.setRequestProperty("Cookie", cookies);
|
||||
}
|
||||
http.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
|
||||
http.addRequestProperty("User-Agent", userAgent);
|
||||
http.setInstanceFollowRedirects(true);
|
||||
HttpURLConnection.setFollowRedirects(true);
|
||||
|
||||
// normally, 3xx is redirect
|
||||
int status = http.getResponseCode();
|
||||
if (status != HttpURLConnection.HTTP_OK) {
|
||||
if (status == HttpURLConnection.HTTP_MOVED_TEMP
|
||||
|| status == HttpURLConnection.HTTP_MOVED_PERM
|
||||
|| status == HttpURLConnection.HTTP_SEE_OTHER)
|
||||
redirect = true;
|
||||
}
|
||||
if (redirect) {
|
||||
// get redirect url from "location" header field
|
||||
url = new URL(http.getHeaderField("Location"));
|
||||
|
||||
// get the cookie if need, for login
|
||||
cookies = http.getHeaderField("Set-Cookie");
|
||||
|
||||
// Disconnect the current connection
|
||||
http.disconnect();
|
||||
redirectCnt++;
|
||||
}
|
||||
}
|
||||
if (http != null) {
|
||||
inputStream = http.getInputStream();
|
||||
|
||||
Bitmap image = BitmapFactory.decodeStream(inputStream);
|
||||
if (image != null) {
|
||||
if (image.getWidth() != tileSize || image.getHeight() != tileSize) {
|
||||
Bitmap tileImage = this.resizeForTile(image);
|
||||
tileCache.put(cacheKey, tileImage.copy(Config.ARGB_8888, true));
|
||||
if (isDebug) {
|
||||
drawDebugInfo(tileImage, x, y, zoom, originalUrlStr);
|
||||
}
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(tileImage));
|
||||
tileImage.recycle();
|
||||
} else {
|
||||
tileCache.put(cacheKey, image.copy(Config.ARGB_8888, true));
|
||||
|
||||
if (isDebug) {
|
||||
drawDebugInfo(image, x, y, zoom, originalUrlStr);
|
||||
}
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(image));
|
||||
}
|
||||
cacheKeys.add(cacheKey);
|
||||
image.recycle();
|
||||
}
|
||||
http.disconnect();
|
||||
}
|
||||
inputStream.close();
|
||||
} else {
|
||||
//---------------------------------
|
||||
// load image from the local path
|
||||
//---------------------------------
|
||||
|
||||
if (!urlStr.contains("://") &&
|
||||
!urlStr.startsWith("/") &&
|
||||
!urlStr.startsWith("www/") &&
|
||||
!urlStr.startsWith("./") &&
|
||||
!urlStr.startsWith("../")) {
|
||||
urlStr = "./" + urlStr;
|
||||
}
|
||||
if (urlStr.startsWith("./") || urlStr.startsWith("../")) {
|
||||
urlStr = urlStr.replace("././", "./");
|
||||
String currentPage = webPageUrl;
|
||||
currentPage = currentPage.replaceAll("#.*$", "");
|
||||
currentPage = currentPage.replaceAll("\\?.*$", "");
|
||||
currentPage = currentPage.replaceAll("[^\\/]*$", "");
|
||||
urlStr = currentPage + "/" + urlStr;
|
||||
}
|
||||
String cacheKey = new File(urlStr).hashCode() + "";
|
||||
Bitmap cachedImage = tileCache.get(cacheKey);
|
||||
if (cachedImage != null && !cachedImage.isRecycled()) {
|
||||
if (isDebug) {
|
||||
Bitmap copyImage = cachedImage.copy(Config.ARGB_8888, true);
|
||||
drawDebugInfo(copyImage, x, y, zoom, originalUrlStr);
|
||||
cachedImage.recycle();
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(copyImage));
|
||||
copyImage.recycle();
|
||||
} else {
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(cachedImage));
|
||||
}
|
||||
//cachedImage.recycle(); // Don't recycle it. Need to keep the bitmap instance
|
||||
return tile;
|
||||
}
|
||||
|
||||
Bitmap image = null;
|
||||
if (urlStr.indexOf("file://") == 0 &&
|
||||
!urlStr.contains("file:///android_asset/")) {
|
||||
urlStr = urlStr.replace("file://", "");
|
||||
File tmp = new File(urlStr);
|
||||
if (tmp.exists()) {
|
||||
image = BitmapFactory.decodeFile(urlStr);
|
||||
} else {
|
||||
//Log.w("PluginTileProvider", "image is not found (" + urlStr + ")");
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (urlStr.indexOf("file:///android_asset/") == 0) {
|
||||
urlStr = urlStr.replace("file:///android_asset/", "");
|
||||
}
|
||||
if (urlStr.contains("./")) {
|
||||
try {
|
||||
boolean isAbsolutePath = urlStr.startsWith("/");
|
||||
File relativePath = new File(urlStr);
|
||||
urlStr = relativePath.getCanonicalPath();
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
if (!isAbsolutePath) {
|
||||
urlStr = urlStr.substring(1);
|
||||
}
|
||||
//Log.d(TAG, "imgUrl = " + imgUrl);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
//Log.d("PluginTileProvider", "urlStr = " + urlStr);
|
||||
try {
|
||||
inputStream = assetManager.open(urlStr);
|
||||
image = BitmapFactory.decodeStream(inputStream);
|
||||
Bitmap tmpImage = image.copy(Config.ARGB_8888, true);
|
||||
image.recycle();
|
||||
image = tmpImage;
|
||||
} catch (IOException e) {
|
||||
//e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (image != null) {
|
||||
if (image.getWidth() != tileSize || image.getHeight() != tileSize) {
|
||||
Bitmap tileImage = this.resizeForTile(image);
|
||||
tileCache.put(cacheKey, tileImage.copy(Config.ARGB_8888, true));
|
||||
if (isDebug) {
|
||||
drawDebugInfo(tileImage, x, y, zoom, originalUrlStr);
|
||||
}
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(tileImage));
|
||||
tileImage.recycle();
|
||||
} else {
|
||||
tileCache.put(cacheKey, image.copy(Config.ARGB_8888, true));
|
||||
if (isDebug) {
|
||||
drawDebugInfo(image, x, y, zoom, originalUrlStr);
|
||||
}
|
||||
tile = new Tile(tileSize, tileSize, bitmapToByteArray(image));
|
||||
}
|
||||
image.recycle();
|
||||
cacheKeys.add(cacheKey);
|
||||
}
|
||||
|
||||
}
|
||||
return tile;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] bitmapToByteArray(Bitmap bitmap) {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.WEBP, 99, outputStream);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
private void drawDebugInfo(Bitmap bitmap, int x, int y, int zoom, String url) {
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawLine(0, 0, tileSize, 0, debugPaint);
|
||||
canvas.drawLine(0, 0, 0, tileSize, debugPaint);
|
||||
canvas.drawText(String.format(Locale.US, "x = %d, y = %d, zoom = %d", x, y, zoom), 30, 30, debugPaint);
|
||||
if (url != null) {
|
||||
StaticLayout mTextLayout = new StaticLayout(url, debugTextPaint,
|
||||
tileSize * 4 / 5, Layout.Alignment.ALIGN_NORMAL,
|
||||
1.0f, 0.0f, false);
|
||||
canvas.save();
|
||||
canvas.translate(30, 60);
|
||||
mTextLayout.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap resizeForTile(Bitmap bitmap) {
|
||||
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636
|
||||
*/
|
||||
Bitmap scaledBitmap = Bitmap.createBitmap(tileSize, tileSize, Config.ARGB_8888);
|
||||
|
||||
float ratioX = tileSize / (float) bitmap.getWidth();
|
||||
float ratioY = tileSize / (float) bitmap.getHeight();
|
||||
float middleX = tileSize / 2.0f;
|
||||
float middleY = tileSize / 2.0f;
|
||||
|
||||
Matrix scaleMatrix = new Matrix();
|
||||
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
|
||||
|
||||
Canvas canvas = new Canvas(scaledBitmap);
|
||||
canvas.setMatrix(scaleMatrix);
|
||||
canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, tilePaint);
|
||||
bitmap.recycle();
|
||||
|
||||
return scaledBitmap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
package plugin.google.maps;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Bundle;
|
||||
import android.util.Base64;
|
||||
|
||||
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.LatLngBounds.Builder;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaResourceApi;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class PluginUtil {
|
||||
// Get resource id
|
||||
// http://stackoverflow.com/a/37840674
|
||||
public static int getAppResource(Activity activity, String name, String type) {
|
||||
return activity.getResources().getIdentifier(name, type, activity.getPackageName());
|
||||
}
|
||||
|
||||
public static abstract class MyCallbackContext extends CallbackContext {
|
||||
|
||||
public MyCallbackContext(String callbackId, CordovaWebView webView) {
|
||||
super(callbackId, webView);
|
||||
}
|
||||
@Override
|
||||
public void sendPluginResult(PluginResult pluginResult) {
|
||||
this.onResult(pluginResult);
|
||||
}
|
||||
|
||||
abstract public void onResult(PluginResult pluginResult);
|
||||
}
|
||||
|
||||
public static boolean isNumeric(String str)
|
||||
{
|
||||
for (char c : str.toCharArray()) {
|
||||
if (!Character.isDigit(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static LatLngBounds getBoundsFromCircle(LatLng center, double radius) {
|
||||
double d2r = Math.PI / 180; // degrees to radians
|
||||
double r2d = 180 / Math.PI; // radians to degrees
|
||||
double earthsradius = 3963.189; // 3963 is the radius of the earth in miles
|
||||
radius *= 0.000621371192; // convert to mile
|
||||
|
||||
// find the raidus in lat/lon
|
||||
double rlat = (radius / earthsradius) * r2d;
|
||||
double rlng = rlat / Math.cos(center.latitude * d2r);
|
||||
|
||||
LatLngBounds.Builder builder = new LatLngBounds.Builder();
|
||||
double ex, ey;
|
||||
for (int i = 0; i < 360; i+=90) {
|
||||
ey = center.longitude + (rlng * Math.cos(i * d2r)); // center a + radius x * cos(theta)
|
||||
ex = center.latitude + (rlat * Math.sin(i * d2r)); // center b + radius y * sin(theta)
|
||||
builder.include(new LatLng(ex, ey));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static LatLngBounds getBoundsFromPath(List<LatLng> path) {
|
||||
LatLngBounds.Builder builder = new LatLngBounds.Builder();
|
||||
|
||||
for (LatLng aPath : path) {
|
||||
builder.include(aPath);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static String getAbsolutePathFromCDVFilePath(CordovaResourceApi resourceApi, String cdvFilePath) {
|
||||
if (cdvFilePath.indexOf("cdvfile://") != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//CordovaResourceApi resourceApi = webView.getResourceApi();
|
||||
Uri fileURL = resourceApi.remapUri(Uri.parse(cdvFilePath));
|
||||
File file = resourceApi.mapUriToFile(fileURL);
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return file.getCanonicalPath();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public static JSONObject location2Json(Location location) throws JSONException {
|
||||
JSONObject latLng = new JSONObject();
|
||||
latLng.put("lat", location.getLatitude());
|
||||
latLng.put("lng", location.getLongitude());
|
||||
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("latLng", latLng);
|
||||
|
||||
if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
params.put("elapsedRealtimeNanos", location.getElapsedRealtimeNanos());
|
||||
} else {
|
||||
params.put("elapsedRealtimeNanos", 0);
|
||||
}
|
||||
params.put("time", location.getTime());
|
||||
/*
|
||||
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
|
||||
Date date = new Date(location.getTime());
|
||||
params.put("timeFormatted", format.format(date));
|
||||
*/
|
||||
if (location.hasAccuracy()) {
|
||||
params.put("accuracy", location.getAccuracy());
|
||||
}
|
||||
if (location.hasBearing()) {
|
||||
params.put("bearing", location.getBearing());
|
||||
}
|
||||
if (location.hasAltitude()) {
|
||||
params.put("altitude", location.getAltitude());
|
||||
}
|
||||
if (location.hasSpeed()) {
|
||||
params.put("speed", location.getSpeed());
|
||||
}
|
||||
params.put("provider", location.getProvider());
|
||||
params.put("hashCode", location.hashCode());
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* return color integer value
|
||||
* @param arrayRGBA
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static int parsePluginColor(JSONArray arrayRGBA) throws JSONException {
|
||||
return Color.argb(arrayRGBA.getInt(3), arrayRGBA.getInt(0), arrayRGBA.getInt(1), arrayRGBA.getInt(2));
|
||||
}
|
||||
|
||||
public static ArrayList<LatLng> JSONArray2LatLngList(JSONArray points) throws JSONException {
|
||||
ArrayList<LatLng> path = new ArrayList<LatLng>();
|
||||
JSONObject pointJSON;
|
||||
int i = 0;
|
||||
for (i = 0; i < points.length(); i++) {
|
||||
pointJSON = points.getJSONObject(i);
|
||||
path.add(new LatLng(pointJSON.getDouble("lat"), pointJSON.getDouble("lng")));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
/*
|
||||
public static Set<LatLng> JSONArray2LatLngHash(JSONArray points) throws JSONException {
|
||||
Set<LatLng> path = new HashSet<LatLng>();
|
||||
JSONObject pointJSON;
|
||||
int i = 0;
|
||||
for (i = 0; i < points.length(); i++) {
|
||||
pointJSON = points.getJSONObject(i);
|
||||
path.add(new LatLng(pointJSON.getDouble("lat"), pointJSON.getDouble("lng")));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
*/
|
||||
public static LatLngBounds JSONArray2LatLngBounds(JSONArray points) throws JSONException {
|
||||
List<LatLng> path = JSONArray2LatLngList(points);
|
||||
Builder builder = LatLngBounds.builder();
|
||||
int i = 0;
|
||||
for (i = 0; i < path.size(); i++) {
|
||||
builder.include(path.get(i));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Bundle Json2Bundle(JSONObject json) {
|
||||
Bundle mBundle = new Bundle();
|
||||
@SuppressWarnings("unchecked")
|
||||
Iterator<String> iter = json.keys();
|
||||
Object value;
|
||||
while (iter.hasNext()) {
|
||||
String key = iter.next();
|
||||
try {
|
||||
value = json.get(key);
|
||||
if (Boolean.class.isInstance(value)) {
|
||||
mBundle.putBoolean(key, (Boolean)value);
|
||||
} else if (Integer.class.isInstance(value)) {
|
||||
mBundle.putInt(key, (Integer)value);
|
||||
} else if (Long.class.isInstance(value)) {
|
||||
mBundle.putLong(key, (Long)value);
|
||||
} else if (Double.class.isInstance(value)) {
|
||||
mBundle.putDouble(key, (Double)value);
|
||||
} else if (JSONObject.class.isInstance(value)) {
|
||||
mBundle.putBundle(key, Json2Bundle((JSONObject)value));
|
||||
} else if (JSONArray.class.isInstance(value)) {
|
||||
JSONArray values = (JSONArray)value;
|
||||
ArrayList<String> strings = new ArrayList<String>();
|
||||
for (int i = 0; i < values.length(); i++) {
|
||||
strings.add(values.get(i) + "");
|
||||
}
|
||||
mBundle.putStringArrayList(key, strings);
|
||||
} else {
|
||||
mBundle.putString(key, json.getString(key));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return mBundle;
|
||||
}
|
||||
|
||||
public static Bitmap resizeBitmap(Bitmap bitmap, int newWidth, int newHeight) {
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636
|
||||
*/
|
||||
Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
|
||||
|
||||
float ratioX = newWidth / (float) bitmap.getWidth();
|
||||
float ratioY = newHeight / (float) bitmap.getHeight();
|
||||
float middleX = newWidth / 2.0f;
|
||||
float middleY = newHeight / 2.0f;
|
||||
|
||||
Matrix scaleMatrix = new Matrix();
|
||||
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
|
||||
|
||||
Canvas canvas = new Canvas(scaledBitmap);
|
||||
canvas.setMatrix(scaleMatrix);
|
||||
canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
bitmap.recycle();
|
||||
|
||||
return scaledBitmap;
|
||||
}
|
||||
|
||||
public static Bitmap scaleBitmapForDevice(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
float density = Resources.getSystem().getDisplayMetrics().density;
|
||||
int newWidth = (int)(bitmap.getWidth() * density);
|
||||
int newHeight = (int)(bitmap.getHeight() * density);
|
||||
/*
|
||||
Bitmap resizeBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
|
||||
*/
|
||||
/**
|
||||
* http://stackoverflow.com/questions/4821488/bad-image-quality-after-resizing-scaling-bitmap#7468636
|
||||
*/
|
||||
Bitmap scaledBitmap = Bitmap.createBitmap(newWidth, newHeight, Config.ARGB_8888);
|
||||
|
||||
float ratioX = newWidth / (float) bitmap.getWidth();
|
||||
float ratioY = newHeight / (float) bitmap.getHeight();
|
||||
float middleX = newWidth / 2.0f;
|
||||
float middleY = newHeight / 2.0f;
|
||||
|
||||
Matrix scaleMatrix = new Matrix();
|
||||
scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);
|
||||
|
||||
Canvas canvas = new Canvas(scaledBitmap);
|
||||
canvas.setMatrix(scaleMatrix);
|
||||
canvas.drawBitmap(bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));
|
||||
bitmap.recycle();
|
||||
|
||||
return scaledBitmap;
|
||||
}
|
||||
|
||||
public static Bitmap getBitmapFromBase64encodedImage(String base64EncodedImage) {
|
||||
byte[] byteArray= Base64.decode(base64EncodedImage, Base64.DEFAULT);
|
||||
Bitmap image= null;
|
||||
try {
|
||||
image = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
public static JSONObject Bundle2Json(Bundle bundle) {
|
||||
JSONObject json = new JSONObject();
|
||||
Set<String> keys = bundle.keySet();
|
||||
Iterator<String> iter = keys.iterator();
|
||||
while (iter.hasNext()) {
|
||||
String key = iter.next();
|
||||
try {
|
||||
Object value = bundle.get(key);
|
||||
if (Bundle.class.isInstance(value)) {
|
||||
value = Bundle2Json((Bundle)value);
|
||||
}
|
||||
if (value.getClass().isArray()) {
|
||||
JSONArray values = new JSONArray();
|
||||
Object[] objects = (Object[])value;
|
||||
int i = 0;
|
||||
for (i = 0; i < objects.length; i++) {
|
||||
if (Bundle.class.isInstance(objects[i])) {
|
||||
objects[i] = Bundle2Json((Bundle)objects[i]);
|
||||
}
|
||||
values.put(objects[i]);
|
||||
}
|
||||
json.put(key, values);
|
||||
} else if (value.getClass() == ArrayList.class) {
|
||||
JSONArray values = new JSONArray();
|
||||
Iterator<?> listIterator = ((ArrayList<?>)value).iterator();
|
||||
while(listIterator.hasNext()) {
|
||||
value = listIterator.next();
|
||||
if (Bundle.class.isInstance(value)) {
|
||||
value = Bundle2Json((Bundle)value);
|
||||
}
|
||||
values.put(value);
|
||||
}
|
||||
json.put(key, values);
|
||||
} else {
|
||||
json.put(key, value);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
public static LatLngBounds convertToLatLngBounds(List<LatLng> points) {
|
||||
LatLngBounds.Builder latLngBuilder = LatLngBounds.builder();
|
||||
Iterator<LatLng> iterator = points.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
latLngBuilder.include(iterator.next());
|
||||
}
|
||||
return latLngBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
public static JSONObject convertIndoorBuildingToJson(IndoorBuilding indoorBuilding) {
|
||||
if (indoorBuilding == null) {
|
||||
return null;
|
||||
}
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
JSONArray levels = new JSONArray();
|
||||
for(IndoorLevel level : indoorBuilding.getLevels()){
|
||||
JSONObject levelInfo = new JSONObject();
|
||||
levelInfo.put("name",level.getName());
|
||||
|
||||
// TODO Auto-generated catch block
|
||||
levelInfo.put("shortName",level.getShortName());
|
||||
levels.put(levelInfo);
|
||||
}
|
||||
result.put("activeLevelIndex",indoorBuilding.getActiveLevelIndex());
|
||||
result.put("defaultLevelIndex",indoorBuilding.getDefaultLevelIndex());
|
||||
result.put("levels",levels);
|
||||
result.put("underground",indoorBuilding.isUnderground());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ArrayList<File> unpackZipFromBytes(InputStream zipped, String dstPath)
|
||||
{
|
||||
ArrayList<File> files = new ArrayList<File>();
|
||||
try {
|
||||
String filename;
|
||||
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(zipped));
|
||||
ZipEntry zipEntry;
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
|
||||
while ((zipEntry = zipInputStream.getNextEntry()) != null)
|
||||
{
|
||||
filename = zipEntry.getName();
|
||||
|
||||
if (zipEntry.isDirectory()) {
|
||||
File directory = new File(dstPath + "/" + filename);
|
||||
files.add(directory);
|
||||
directory.mkdirs();
|
||||
continue;
|
||||
}
|
||||
|
||||
files.add(new File(dstPath + "/" + filename));
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(dstPath + "/" + filename);
|
||||
|
||||
while ((count = zipInputStream.read(buffer)) != -1) {
|
||||
fileOutputStream.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
fileOutputStream.close();
|
||||
zipInputStream.closeEntry();
|
||||
}
|
||||
|
||||
zipInputStream.close();
|
||||
zipped.close();
|
||||
|
||||
} catch(IOException e) {
|
||||
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
public static String getPgmStrings(Activity activity, String key) {
|
||||
int resId = PluginUtil.getAppResource(activity, key, "string");
|
||||
Resources res = activity.getResources();
|
||||
return res.getString(resId);
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<!-- Drop Shadow Stack -->
|
||||
<item>
|
||||
<shape>
|
||||
<padding
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<solid android:color="#00CCCCCC" />
|
||||
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<padding
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<solid android:color="#10CCCCCC" />
|
||||
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<padding
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<solid android:color="#20CCCCCC" />
|
||||
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<padding
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<solid android:color="#30CCCCCC" />
|
||||
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<padding
|
||||
android:bottom="1dp"
|
||||
android:left="1dp"
|
||||
android:right="1dp"
|
||||
android:top="1dp" />
|
||||
|
||||
<solid android:color="#50CCCCCC" />
|
||||
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Background -->
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="#FCFFFFFF" />
|
||||
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<View
|
||||
android:visibility="invisible"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="1dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">أغلق</string>
|
||||
<string name="pgm_api_key_error">مع مفتاح API الخاص بك \"GOOGLE_MAPS_ANDROID_API_KEY\" يرجى استبدال config.xml في منصات </string>
|
||||
<string name="pgm_google_play_error">غير متوفر لسبب ما على هذا الجهاز Google Maps Android API v2 </string>
|
||||
<string name="pgm_google_play_developer_error">تم تكوين التطبيق بشكل خاطئ. هذا الخطأ غير قابل للاسترداد وسيتم التعامل معه على أنه مميت. يجب على مطور البرامج النظر في السجلات بعد ذلك لتحديد مزيد من المعلومات القابلة للتنفيذ</string>
|
||||
<string name="pgm_google_play_internal_error">يرجى إعادة المحاولة ، ويجب أن تحل المشكلة ، Google Play حدث خطأ داخلي في خدمات </string>
|
||||
<string name="pgm_google_play_invalid_account">لقد حاولت الاتصال بالخدمة باسم حساب غير صالح</string>
|
||||
<string name="pgm_google_play_license_check_failed">.التطبيق غير مرخص للمستخدم. هذا الخطأ غير قابل للاسترداد وسيتم التعامل معه على أنه مميت</string>
|
||||
<string name="pgm_google_play_network_error">.حدث خطأ في الشبكة. الرجاء المحاولة مرة أخرى ، ويجب أن تحل المشكلة</string>
|
||||
<string name="pgm_google_play_service_disabled"> Google Play على هذا الجهاز. الرجاء تشغيل خدمات Google Play تم تعطيل الإصدار المثبت من خدمات</string>
|
||||
<string name="pgm_google_play_service_invalid">Google play من متجر Google Play المثبتة على هذا الجهاز غير أصلي. يرجى تحديث خدمات Google Play إصدار خدمات</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play مفقودة على هذا الجهاز. الرجاء تثبيت خدمات Google Play خدمات </string>
|
||||
<string name="pgm_google_play_service_update_required">Google Play من متجر Google Play قديم. يرجى تحديث خدمات Google Play الإصدار المثبت من خدمات </string>
|
||||
<string name="pgm_google_play_sign_in_required"> Google Play لقد حاولت الاتصال بالخدمة ولكنك لم تسجل الدخول. يرجى التحقق من تكوين خدمات </string>
|
||||
<string name="pgm_no_location_providers">. نظرًا لأن هذا الجهاز لا يحتوي على أي موفر للموقع ، لا يمكن لهذا التطبيق اكتشاف موقعك</string>
|
||||
<string name="pgm_no_location_service_is_disabled">تم تعطيل خدمة الموقع</string>
|
||||
<string name="pgm_location_rejected_by_user">خدمة الموقع مرفوضة من قبل المستخدم</string>
|
||||
<string name="pgm_can_not_get_location"> .لا يمكن الكشف عن موقعك. حاول ثانية </string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Luk</string>
|
||||
<string name="pgm_api_key_error">Vær sød at ændre \'GOOGLE_MAPS_ANDROID_API_KEY\' i config.xml med din egen API-nøgle!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 er, af en eller anden grund, ikke tilgængelig på denne enhed.</string>
|
||||
<string name="pgm_google_play_developer_error">Applikationen er fejlkonfigureret. Denne fejl er ikke genoprettelig og vil blive betragtet som fatal. Udvikleren bør kigge i logs efter dette for at finde mere handlingsrettet information.</string>
|
||||
<string name="pgm_google_play_internal_error">En intern fejl skete med Google Play Services. Vær sød at prøve igen, og så burde det løse problemet.</string>
|
||||
<string name="pgm_google_play_invalid_account">Du prøvede at tilslutte til servicen med et invalidt kontonavn specificeret</string>
|
||||
<string name="pgm_google_play_license_check_failed">Applikationen er ikke licenseret til brugeren. Denne fejl er ikke genoprettelig og vil blive betragtet som fatal.</string>
|
||||
<string name="pgm_google_play_network_error">Der skete en netværksfejl. Vær sød at prøve igen, og så burde det løse problemet.</string>
|
||||
<string name="pgm_google_play_service_disabled">Den installeret version af Google Play Services er blevet has been deaktiveret på denne enhed. Vær sød at tænde for Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">Versionen for Google Play Services installeret på denne enhed er ikke autentisk. Vær sød at opdatere Google Play Services fra Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play services mangler på denne enhed. Vær sød at installere Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">Den installeret version af Google Play services er forældet. Vær sød at opdatere Google Play Services fra Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Du prøvede at tilslutte til servicen, men du er ikke logget ind. Vær sød at tjekke Google Play Services konfigurationen</string>
|
||||
<string name="pgm_no_location_providers">Siden denne enhed ikke har en lokationsudbyder kan denne app ikke finde din lokation</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Lokationsservice er deaktiveret.</string>
|
||||
<string name="pgm_location_rejected_by_user">Lokationsservice blev afvist af bruger.</string>
|
||||
<string name="pgm_can_not_get_location">Kan ikke finde din lokation. Prøv igen.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Schließen</string>
|
||||
<string name="pgm_api_key_error">Bitte ersetzen Sie \'GOOGLE_MAPS_ANDROID_API_KEY\' unter config.xml durch Ihren API Key!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 ist auf diesem Gerät aus einem unbekannten Grund nicht verfügbar.</string>
|
||||
<string name="pgm_google_play_developer_error">Die Anwendung ist falsch konfiguriert und kann nicht fortgefahren werden. Der Entwickler muss die Protokolle inspezieren, um weitere Informationen zu erhalten.</string>
|
||||
<string name="pgm_google_play_internal_error">Ein interner Fehler der Google Play-Dienste ist aufgetreten. Bitte versuchen Sie es erneut.</string>
|
||||
<string name="pgm_google_play_invalid_account">Sie haben versucht, eine Verbindung zum Dienst mit einem ungültigen Kontonamen herzustellen.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Die Anwendung ist für den Benutzer nicht lizenziert. Dieser Fehler ist nicht wiederherstellbar und wird als fatal behandelt.</string>
|
||||
<string name="pgm_google_play_network_error">Ein Netzwerkfehler ist aufgetreten. Bitte stellen Sie eine Internetverbindung her und versuchen Sie es erneut.</string>
|
||||
<string name="pgm_google_play_service_disabled">Die installierte Version der Google Play-Dienste wurde auf diesem Gerät deaktiviert. Bitte schalten Sie die Google Play-Dienste ein.</string>
|
||||
<string name="pgm_google_play_service_invalid">Die Version der auf diesem Gerät installierten Google Play-Dienste konnte nicht authentifiziert werden. Bitte Aktualisieren Sie die Google Play-Dienste im Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play-Dienste fehlen auf diesem Gerät. Bitte installieren Sie die Google Play-Dienste über den Play Store.
|
||||
</string>
|
||||
<string name="pgm_google_play_service_update_required">Die installierte Version der Google Play-Dienste ist veraltet. Aktualisieren Sie die Google Play-Dienste im Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Sie haben versucht, eine Verbindung mit dem Dienst herzustellen, aber Sie sind nicht angemeldet. Überprüfen Sie die Konfiguration der Google Play-Dienste</string>
|
||||
<string name="pgm_no_location_providers">Da dieses Gerät keinen Standortanbieter hat, kann diese App Ihren Standort nicht orten.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Der Standortdienst ist deaktiviert.</string>
|
||||
<string name="pgm_location_rejected_by_user">Der Standortdienst wird vom Benutzer abgelehnt.</string>
|
||||
<string name="pgm_can_not_get_location">Sie konnten nicht lokalisiert werden. Versuchen Sie es bitte erneut.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Cerrar</string>
|
||||
<string name="pgm_api_key_error">Por favor reemplace \'GOOGLE_MAPS_ANDROID_API_KEY\' en el archivo config.xml por su API Key!</string>
|
||||
<string name="pgm_google_play_error">Por alguna razón Google Maps Android API v2 no está disponible en este dispositivo.</string>
|
||||
<string name="pgm_google_play_developer_error">La aplicación no está configurada correctamente. Ya que no es posible recuperarse de este error, será tratado como fatal. El desarrollador debe revisar los registros para determinar si existe alguna acción por realizar.</string>
|
||||
<string name="pgm_google_play_internal_error">Ocurrió un error interno en Google Play Services. Por favor inténtalo de nuevo, y el problema debería resolverse.</string>
|
||||
<string name="pgm_google_play_invalid_account">Intentaste conectarte al servicio especificando un nombre de cuenta invalido.</string>
|
||||
<string name="pgm_google_play_license_check_failed">La aplicación no tiene licencia para el usuario. Ya que no es posible recuperarse de este error, será tratado como fatal.</string>
|
||||
<string name="pgm_google_play_network_error">Ocurrió un error de red. Por favor inténtalo de nuevo, y el problema debería resolverse.</string>
|
||||
<string name="pgm_google_play_service_disabled"> La versión instalada de Google Play Services ha sido deshabilitada en este dispositivo. Por favor encienda Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">La versión instalada de Google Play Services no es autentica. Por favor actualice Google Play Services desde Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">No se encontró Google Play Services en el dispositivo. Por favor instale Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">La versión instalada de Google Play services está desactualizada. Por favor actualice Google Play Services desde Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Intentaste conectarte a los servicios pero no has iniciado sesión. Por favor revisa la configuración de Google Play Services</string>
|
||||
<string name="pgm_no_location_providers">En vista de que este dispositivo no tiene ningún proveedor de localización, la aplicación no puede detectar tu ubicación.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Servicio de localización desactivado.</string>
|
||||
<string name="pgm_location_rejected_by_user">El servicio de localización fue rechazado por el usuario.</string>
|
||||
<string name="pgm_can_not_get_location">No se pudo detectar tu ubicación. Inténtalo de nuevo.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Fermer</string>
|
||||
<string name="pgm_api_key_error">Veuillez remplacer \"GOOGLE_MAPS_ANDROID_API_KEY\" dans le fichier config.xml avec votre propre clé API!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 n\'est pas disponible sur cet appareil pour une raison non identifiée.</string>
|
||||
<string name="pgm_google_play_developer_error">L\'application a mal été configurée. Cette erreur n\'est pas récupérable et sera traitée comme étant fatale. Le développeur devrait inspecter le journal d\'erreur afin de trouver des informations plus précises.</string>
|
||||
<string name="pgm_google_play_internal_error">Une erreur interne est intervenue au niveau du Google Play Services. Veuillez réessayer, cela devrait résoudre le problème.</string>
|
||||
<string name="pgm_google_play_invalid_account">Vous avez tenté de vous connecter au service avec un nom de compte invalide.</string>
|
||||
<string name="pgm_google_play_license_check_failed">L\'application ne contient pas de licence pour cet utilisateur. Cette erreur n\'est pas récupérable et sera traitée comme étant fatale.</string>
|
||||
<string name="pgm_google_play_network_error">Un problème de réseau est intervenu. Veuillez réessayer, cela devrait résoudre le problème.</string>
|
||||
<string name="pgm_google_play_service_disabled">La version du Google Play Services installée sur cet appareil a été désactivée. Veuillez activer le Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">La version du Google Play Services installée sur cet appareil n\'est pas authentique. Merci de mettre à jour le Google Play Services à partir du Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play Services n\'est pas installé sur cet appareil. Merci d\'installer le Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">La version du Google Play Services installée sur cet appareil a expirée. Veuillez mettre à jour le Google Play Services à partir du Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Vous avez tenté de vous connecter au service, mais vous n\'est pas authentifié. Merci de vérifier la configuration du Google Play Services.</string>
|
||||
<string name="pgm_no_location_providers">Cet appareil n\'est pas équipé d\'un service de localisation, cette application ne peut donc pas récupérer votre position.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Le service de localisation est désactivé.</string>
|
||||
<string name="pgm_location_rejected_by_user">Le service de localisation est rejeté par l\'utilisateur.</string>
|
||||
<string name="pgm_can_not_get_location">Localisation introuvable. Veuillez réessayer.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">बंद</string>
|
||||
<string name="pgm_api_key_error">कृपया config.xml मे \'GOOGLE_MAPS_ANDROID_API_KEY\' को अपनी एपीआई कुंजी से बदलें</string>
|
||||
<string name="pgm_google_play_error">इस डिवाइस पर Google Maps Android API v2 उपलब्ध नहीं है</string>
|
||||
<string name="pgm_google_play_developer_error">यह एप्लिकेशन गलत तरीके से कॉन्फ़िगर किया गया है. इस त्रुटि को ठीक किया नहीं जासकता इसे घातक माना जायेगा. डेवलपर को चाहिए वे लोग में देखकर कार्रवाई योग्य प्राप्त करे.</string>
|
||||
<string name="pgm_google_play_internal_error">Google Play सेवाओं की एक आंतरिक त्रुटि हुई. कृप्या फिरसे प्रयास करें, इस से समस्या हल होनी चाहिए.</string>
|
||||
<string name="pgm_google_play_invalid_account">आप ने एक अमान्य खाता नाम के साथ सेवा से जुड़ने का प्रयास है.</string>
|
||||
<string name="pgm_google_play_license_check_failed">इस उपयोगकर्ता के पास इस एप्लीकेशन लाइसेंस नहीं है. इस त्रुटि को ठीक किया नहीं जासकता इसे घातक माना जायेग.</string>
|
||||
<string name="pgm_google_play_network_error">एक नेटवर्क त्रुटि हुई. कृप्या फिरसे प्रयास करें, इस से समस्या हल होनी चाहिए.</string>
|
||||
<string name="pgm_google_play_service_disabled">इस डिवाइस पर Google Play सेवाओं का स्थापित संस्करण अक्षम है. कृपया Google Play सेवाएं चालू करें.</string>
|
||||
<string name="pgm_google_play_service_invalid">इस डिवाइस पर स्थापित Google Play सेवाओं का संस्करण प्रामाणिक नहीं है. कृपया Google Play Store से Google Play सेवाएं अपडेट करें.</string>
|
||||
<string name="pgm_google_play_service_missing">इस डिवाइस पर Google Play सेवाएं मौजूद नहीं हैं. कृपया Google Play सेवाएं स्थापित करें.</string>
|
||||
<string name="pgm_google_play_service_update_required">Google Play सेवाओं का स्थापित संस्करण पुराना है. कृपया Google Play Store से Google Play सेवाएं अपडेट करें.</string>
|
||||
<string name="pgm_google_play_sign_in_required">आप ने साइन किये बिना इस सेवा से जुड़ने का प्रयास किया है. कृपया Google Play सेवाएं कॉन्फ़िगरेशन जांचें</string>
|
||||
<string name="pgm_no_location_providers">इस डिवाइस में कोई स्थान प्रदाता नहीं है, इसके कारण यह ऐप आपके स्थान का पता नहीं लगा सकता.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">स्थान सेवा अक्षम है.</string>
|
||||
<string name="pgm_location_rejected_by_user">उपयोगकर्ता ने स्थान सेवा अस्वीकार कर दिया है.</string>
|
||||
<string name="pgm_can_not_get_location">आप के स्थान का पता नही चल रहा है. कृप्या फिरसे प्रयास करें.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Tutup</string>
|
||||
<string name="pgm_api_key_error">Silahkan ganti \'GOOGLE_MAPS_ANDROID_API_KEY\' di config.xml dengan Key API anda!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 tidak dapat digunakan pada perangkat ini.</string>
|
||||
<string name="pgm_google_play_developer_error">Pengaturan applikasi ini salah. Kesalahan ini tidak dapat diperbaharui dan dianggap FATAL. Developer harus memeriksa informasi logs setelah ini untuk memastikan tidandakan.</string>
|
||||
<string name="pgm_google_play_internal_error">terdapat kesalahan pada Google Play Services. Silahkan Coba lagi, untuk memperbaiki masalah.</string>
|
||||
<string name="pgm_google_play_invalid_account">Anda mencoba menghubungi service dengan nama akun yang tidak sesuai.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Appilkasi ini tidak diberikan licensi kepada pengguna. Error ini tidak dapat diperbaharui dan dianggap FATAL.</string>
|
||||
<string name="pgm_google_play_network_error">Kesalahan pada Jaringan. Silahkan Coba lagi,untuk memperbaiki masalah.</string>
|
||||
<string name="pgm_google_play_service_disabled">versi Google Play yang di install telah di non aktifkan pada perangkat ini. Silahkan Aktifkan Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">Versi Google Play services yang di install pada perangkat ini tidak asli. Silahkan perbaharui Google Play Services melalui Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play services tidak terdapat pada perangkat ini. Silahkan install Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">Versi Google Play services sudha ketinggalan. Silahkan perbaharui Google Play Services melalui Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Anda mencoba terhubung pada service tetapi belum signed in. Silahkan periksa pengaturan Google Play Services </string>
|
||||
<string name="pgm_no_location_providers">Karena perangkat ini tidak tersedia provider lokasi, Applikasi tidak dapat mengetahui lokasi anda.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Service Lokasi tidak aktif.</string>
|
||||
<string name="pgm_location_rejected_by_user">Service Lokasi Ditolak pengguna.</string>
|
||||
<string name="pgm_can_not_get_location">Tidak dapat mengetahui lokasi anda. Silahkan Coba lagi.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">閉じる</string>
|
||||
<string name="pgm_api_key_error">config.xmlファイル内の\'GOOGLE_MAPS_ANDROID_API_KEY\'をあなたのAPIキーに置き換えてください</string>
|
||||
<string name="pgm_google_play_error">この端末ではGoogle Maps Android API v2が利用できません。</string>
|
||||
<string name="pgm_google_play_developer_error">アプリケーションの設定が間違えています。このエラーは修復不可能かつ重大な失敗なものです。開発者は端末ログを確認してください。</string>
|
||||
<string name="pgm_google_play_internal_error">Google Play Servicesの内部エラーが発生しました。おそらくもう一度試みていただければ解決します。</string>
|
||||
<string name="pgm_google_play_invalid_account">ログインしているGoogleアカウントが不正です。再度ログインして試してみてください。</string>
|
||||
<string name="pgm_google_play_license_check_failed">このアプリケーションの使用がユーザーに認められていません。このエラーは修復不可能かつ重大な失敗なものです。</string>
|
||||
<string name="pgm_google_play_network_error">ネットワークエラーが発生しました。おそらくもう一度試みていただければ解決します。</string>
|
||||
<string name="pgm_google_play_service_disabled">この端末にインストールされているGoogle Play Servicesが無効になっています。Google Play Servicesを有効にしてください。</string>
|
||||
<string name="pgm_google_play_service_invalid">この端末にインストールされているGoogle Play Servicesでは認証ができません。Google Play Storeから最新版のGoogle Play Servicesをインストールしてください。</string>
|
||||
<string name="pgm_google_play_service_missing">この端末にGoogle Play Servicesがインストールされていません。Google Play Storeから最新版のGoogle Play Servicesをインストールしてください。</string>
|
||||
<string name="pgm_google_play_service_update_required">この端末にインストールされているGoogle Play Servicesが古過ぎです。Google Play Storeから最新版のGoogle Play Servicesをインストールしてください。</string>
|
||||
<string name="pgm_google_play_sign_in_required">Google Maps Android API v2を使用しようと試みましたが、あなたがGoogleアカウントにログインしていません。Googleアカウントにログインしてから再度試してみてください。</string>
|
||||
<string name="pgm_no_location_providers">利用できる位置情報を取得できる方法(GPSなど)がないため、この端末の位置情報が取得できませんでした。</string>
|
||||
<string name="pgm_no_location_service_is_disabled">位置情報サービス(GPSなど)が無効になっています。</string>
|
||||
<string name="pgm_location_rejected_by_user">位置情報サービス(GPSなど)の利用がユーザーにより拒否されました。</string>
|
||||
<string name="pgm_can_not_get_location">この端末の位置情報が取得できませんでした。もう一度試してみてください。</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Lukk</string>
|
||||
<string name="pgm_api_key_error">Vennligst erstatt \'GOOGLE_MAPS_ANDROID_API_KEY\' i config.xml med din API-nøkkel!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 er ikke tilgjengelig av ukjent grunn på enheten.</string>
|
||||
<string name="pgm_google_play_developer_error">Applikasjon er feilkonfigurert. Denne feilen er ikke gjenvinnbart og skal behandles som fatal. Utvikleren bør se på loggen etter dette for å fastslå mer handlingsbar informasjon.</string>
|
||||
<string name="pgm_google_play_internal_error">Det har skjedd en intern feil av Google Play tjeneste. Vennligst prøv igjen og problemme skal løses ut.</string>
|
||||
<string name="pgm_google_play_invalid_account">Du prøver å koble til tjeneste med ugyldig brukkernavn.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Applikasjon har ikke licensing for dette brukkernavnet. Denne feilen er ikke gjenvinnbart og skal behandles som fatal.</string>
|
||||
<string name="pgm_google_play_network_error">Det har skjedd koblingsfailen. Vennligst prøv igjen og problemme skal løses ut.</string>
|
||||
<string name="pgm_google_play_service_disabled">Innstallert versjon av Google Play tjenesten har vært deaktivert på denne innhetten. Vennligst slå av Google Play tjenesten.</string>
|
||||
<string name="pgm_google_play_service_invalid">Innstallert versjon av Google Play tjenesten har ikke vært autentisk. Vennligst oppdater Google Play tjenesten fra Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play tjenesten er utilghengelig på denne enheten. Vennligst installer Google Play tjenesten.</string>
|
||||
<string name="pgm_google_play_service_update_required">Innstallert verjson av Google Play tjenesten er ikke oppdatert. Vennligst oppdater Google Play tjenesten fra Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Du prøver å koble til tjeneste, men du har ikke logget inn. Vennligst sjekk Google Play tjenesten innstellinger.</string>
|
||||
<string name="pgm_no_location_providers">Siden enheten har ingen stedleverandør, kan ikke din lokasjon oppdages av enheten.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Stedtjenester er deaktivert.</string>
|
||||
<string name="pgm_location_rejected_by_user">Stedtjenester er avvist av brukeren.</string>
|
||||
<string name="pgm_can_not_get_location">Kan ikke oppdage din lokasjon. Vannligst prøv igjen.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Sluiten</string>
|
||||
<string name="pgm_api_key_error">Vervang \"GOOGLE_MAPS_ANDROID_API_KEY\" in config.xml met je API Key!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 is niet beschikbaar op dit apparaat.</string>
|
||||
<string name="pgm_google_play_developer_error">De applicatie is onjuist geconfigureerd. Deze foutmelding kan niet hesteld worden. De ontwikkelaar moet de logboek informatie bekijken om dit probleem op te lossen.</string>
|
||||
<string name="pgm_google_play_internal_error">Er heeft een interne fout van Google Play Services plaatsgevonden. Probeer het opnieuw.</string>
|
||||
<string name="pgm_google_play_invalid_account">Je hebt geprobeerd om te verbinden met de service met een ongeldig gebruikersnaam.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Er is geen licentie voor de gebruiker. Deze foutmelding kan niet hersteld worden.</string>
|
||||
<string name="pgm_google_play_network_error">Er heeft een netwerkfout plaatsgevonden. Probeer het opnieuw.</string>
|
||||
<string name="pgm_google_play_service_disabled">De geinstalleerde versie van Google Play services staat uit op dit apparaat. Zet Google Play Services aan.</string>
|
||||
<string name="pgm_google_play_service_invalid">De geinstaleerde versie van Google Play services is niet autentiek. Update Google Play Services van de Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play services ontbreekt op dit apparaat. Installeer Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">De geinstalleerde Google Play services is niet langer geldig. Update Google Play Services van de Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Je probeert te verbinden met de service maar je bent niet ingelogd. Controleer de Google Play Services configuratie</string>
|
||||
<string name="pgm_no_location_providers">Omdat dit apparaat geen locatie provider heeft kan de app je locatie niet bepalen.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Locatie service staat uit.</string>
|
||||
<string name="pgm_location_rejected_by_user">Locatie service is afgewezen door de gebruiker.</string>
|
||||
<string name="pgm_can_not_get_location">Kan je locatie niet bepalen. Probeer opnieuw.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Zamknij</string>
|
||||
<string name="pgm_api_key_error">Proszę zastąpić \'GOOGLE_MAPS_ANDROID_API_KEY\' w pliku config.xml własnym kluczem API!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 .</string>
|
||||
<string name="pgm_google_play_developer_error">Aplikacja posiada niepoprawną konfigurację. Tego błędu nie można odtworzyć i będzie on traktowany jako krytyczny. W celu uzyskania bardziej szczegółowych informacji programista powinien przejrzeć pliki z logami.</string>
|
||||
<string name="pgm_google_play_internal_error">Wystąpił błąd wewnątrz usług Google Play. Ponowne uruchomienie powinno rozwiązać problem.</string>
|
||||
<string name="pgm_google_play_invalid_account">Próbę nawiązania połączenia z usługą nie powiodła się, z powodu nieprawidłowej nazwy konta.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Aplikacja nie jest licencjonowana dla użytkownika. Tego błędu nie można odtworzyć i będzie on traktowany jako krytyczny.</string>
|
||||
<string name="pgm_google_play_network_error">Wystąpił błąd połączenia internetowego. Proszę spróbować ponownie.</string>
|
||||
<string name="pgm_google_play_service_disabled">Zainstalowana wersja usług Google Play została wyłączona na urządzeniu. Proszę włączyć usługi Google Play.</string>
|
||||
<string name="pgm_google_play_service_invalid">Zainstalowana na urządzeniu wersja usług Google Play jest fałszywa. Proszę zaktualizować usługi Google Play ze sklepu Google Play.</string>
|
||||
<string name="pgm_google_play_service_missing">Nie odnaleziono zainstalowanej wersji usług Google Play. Proszę zainstalować usługi Google Play.</string>
|
||||
<string name="pgm_google_play_service_update_required">Zainstalowana wersja usług Google Play jest przestarzała. Proszę zaktualizować usługi Google Play Services w sklepie Google Play.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Próbowano połączyć się z usługami Google Play nie będąc zalogowanym. Proszę sprawdzić konfigurację usług Google Play.</string>
|
||||
<string name="pgm_no_location_providers">Na urządzeniu nie odnaleziono usług dostarczających położenie. Aplikacja nie będzie w stanie określić położenia.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Usługi lokalizacji są wyłączone.</string>
|
||||
<string name="pgm_location_rejected_by_user">Zgoda na usługi lokalizacji została odrzucona przez użytkownika.</string>
|
||||
<string name="pgm_can_not_get_location">Nie można określić położenia. Spróbuj ponownie.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Fechar</string>
|
||||
<string name="pgm_api_key_error">Por favor substitua \'GOOGLE_MAPS_ANDROID_API_KEY\' em config.xml com sua chave de API!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 não está disponível nesse dispositivo por alguma razão.</string>
|
||||
<string name="pgm_google_play_developer_error">O aplicativo está mal configurado. Este erro não é recuperável e será tratado como fatal. O desenvolvedor deve analisar os logs para determinar o que pode ser feito</string>
|
||||
<string name="pgm_google_play_internal_error">Ocorreu um erro interno no Google Play Services. Por favor tente novamente e o erro deverá estar resolvido.</string>
|
||||
<string name="pgm_google_play_invalid_account">Você tentou se conectar ao serviço com um nome de conta inválido.</string>
|
||||
<string name="pgm_google_play_license_check_failed">O aplicativo não está licenciado para o usuário. Esse erro não é recuperável e será tratado como fatal.</string>
|
||||
<string name="pgm_google_play_network_error">Ocorreu um erro de rede. Por favor tente novamente e o erro deverá estar resolvido.</string>
|
||||
<string name="pgm_google_play_service_disabled">A versão instalada do Google Play Services foi desativada nesse dispositivo. Por favor ative o Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">A versão do Google Play Services instalada nesse dispositivo não é autêntica. Atualize o Google Play Services na Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">O Google Play services não existe nesse dispositivo. Por favor instale o Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">A versão do Google Play Services instalada está desatualizada. Por favor atualize o Google Play Services na Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Você tentou se conectar ao serviço mas você não está logado. Por favor cheque a configuração do Google Play Services.</string>
|
||||
<string name="pgm_no_location_providers">Esse dispositivo não tem nenhum provedor de localização, logo, este aplicativo não pode detectar sua localização.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">O serviço de Localização está desativado.</string>
|
||||
<string name="pgm_location_rejected_by_user">O serviço de Localização foi rejeitado pelo usuário.</string>
|
||||
<string name="pgm_can_not_get_location">Não foi possível detectar sua localização. Tente novamente.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Закрыть</string>
|
||||
<string name="pgm_api_key_error">Пожалуйста, замените \'GOOGLE_MAPS_ANDROID_API_KEY\' в config.xml на Ваш API ключ!</string>
|
||||
<string name="pgm_google_play_error">По какой то причине Google Maps Android API v2 не доступен на этом устройстве.</string>
|
||||
<string name="pgm_google_play_developer_error">Приложение неправильно настроено. Произошла фатальная ошибка. Разработчик приложения должен посмотреть в логи, чтобы исправить ошибку.</string>
|
||||
<string name="pgm_google_play_internal_error">Произошла внутренняя ошибка Google Play Services. Пожалуйста, повторите попытку для решения проблемы.</string>
|
||||
<string name="pgm_google_play_invalid_account">Вы попытались подключиться к сервису с недопустимым именем учетной записи.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Произошла фатальная ошибка. Приложение не лицензировано для этого пользователя.</string>
|
||||
<string name="pgm_google_play_network_error">Произошла сетевая ошибка. Пожалуйста, повторите попытку для решения проблемы.</string>
|
||||
<string name="pgm_google_play_service_disabled">Установленная версия Google Play Services отключена на этом устройстве. Пожалуйста, включите Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">Версия Google Play Services установлена на этом уствройстве не подлинная. Пожалуйста, обновите Google Play Services из Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play Services отсутствует на этом устройстве. Пожалуйста, установите Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">Установленная версия Google Play Services устаревшая. Пожалуйста, обновите Google Play Services из Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Вы попытались подключиться к сервису, но вы не вошли в систему. Пожалуйста, проверьте конфигурацию Google Play Services.</string>
|
||||
<string name="pgm_no_location_providers">Поскольку это устройство не поддерживает поиск местоположения, это приложение не может определить Ваше расположение.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Сервис определения местоположения отключен.</string>
|
||||
<string name="pgm_location_rejected_by_user">Определение местоположения отклонено пользователем.</string>
|
||||
<string name="pgm_can_not_get_location">Не удается определить Ваше местоположение. Попробуй еще раз.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Закрити</string>
|
||||
<string name="pgm_api_key_error">Будь ласка, замініть \'GOOGLE_MAPS_ANDROID_API_KEY\' в config.xml на Ваш API ключ!</string>
|
||||
<string name="pgm_google_play_error">По якісь причині Google Maps Android API v2 не доступний на цьому пристрої.</string>
|
||||
<string name="pgm_google_play_developer_error">Програма невірно налаштована. Виникла фатальна помилка. Розробник додатку повинен подивитись в логи, щоб виправити помилку.</string>
|
||||
<string name="pgm_google_play_internal_error">Відбулася внутрішня помилка Google Play Services. Будь ласка, спробуйте ще раз для вирішення проблеми.</string>
|
||||
<string name="pgm_google_play_invalid_account">Ви спробували підключитися до сервісу з недійсним ім\'ям облікового запису.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Виникла фатальна помилка. Додаток не ліцензійований для користувача.</string>
|
||||
<string name="pgm_google_play_network_error">Виникла помилка мережі. Будь ласка, спробуйте ще раз для вирішення проблеми.</string>
|
||||
<string name="pgm_google_play_service_disabled">Встановлений Google Play Services на цьому пристрої - відключений. Будь ласка, увімкніть Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">Встановлена версія Google Play Services на цьому пристрої не є автентична. Будь ласка, обновіть Google Play Services із Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play Services відсутній на цьому пристрої. Будь ласка, встановіть Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">Встановлена версія Google Play Services застаріла. Будь ласка, обновіть Google Play Services із Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Ви спробували підключитися до сервісу, але ви не ввійшли в систему. Будь ласка, перевірте конфігурацію Google Play Services.</string>
|
||||
<string name="pgm_no_location_providers">Оскільки цей пристрій не підтримує пошук місцезнаходження, цей додаток не може визначити Ваше розташування.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Сервіс місцезнаходження вимкнений.</string>
|
||||
<string name="pgm_location_rejected_by_user">Сервіс пошуку місцезнаходження відхилений користувачем.</string>
|
||||
<string name="pgm_can_not_get_location">Не вдається виявити Ваше місцезнаходження. Спробуйте знову.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Đóng</string>
|
||||
<string name="pgm_api_key_error">Vui lòng thay \'GOOGLE_MAPS_ANDROID_API_KEY\' trong config.xml với API Key của bạn!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 vì một lý do nào đó không hoạt động trên thiết bị của bạn.</string>
|
||||
<string name="pgm_google_play_developer_error">Ứng dụng chưa được cấu hình. Lỗi này là nghiêm trọng và làm cho hệ thống ngừng hoạt động. Nhà phát triển cần kiểm tra hệ thống Logs để có hướng khắc phục cụ thể..</string>
|
||||
<string name="pgm_google_play_internal_error">Lỗi xảy ra với dịch vụ Google Play. Vui lòng khởi động lại để hệ thống hoạt động bình thường..</string>
|
||||
<string name="pgm_google_play_invalid_account">Bạn sử dụng tài khoản không hợp lệ để kết nối với dịch vụ.</string>
|
||||
<string name="pgm_google_play_license_check_failed">Người sử dụng không được cấp phép sử dụng ứng dụng. Lỗi này là nghiêm trọng và làm cho hệ thống ngừng hoạt động.</string>
|
||||
<string name="pgm_google_play_network_error">Đường truyền mạng có lỗi. Vui lòng thử lại, và hệ thống sẽ hoạt động bình thường.</string>
|
||||
<string name="pgm_google_play_service_disabled">Dịch vụ Google Play không hoạt động trên thiết bị của bạn. Vui lòng bật dịch vụ Google Play để hệ thống hoạt động.</string>
|
||||
<string name="pgm_google_play_service_invalid">Dịch vụ Google Play cài đặt trên máy của bạn không hợp lệ. Vui lòng cập nhật dịch vụ Google Play trên Kho ứng dụng Google Play.</string>
|
||||
<string name="pgm_google_play_service_missing">Dịch vụ Google PLay chưa được cài đặt trên thiết bị của bạn. Vui lòng cài đặt dịch vụ Google Play.</string>
|
||||
<string name="pgm_google_play_service_update_required">Dịch vụ Google Play của bạn đã lỗi thời. Vui lòng cập nhật dịch vụ Google Play trên Kho ứng dụng Google Play.</string>
|
||||
<string name="pgm_google_play_sign_in_required">Bạn chưa thực hiện đăng nhập để kết nối với dịch vụ. Vui lòng xem lại cấu hình dịch vụ Google Play.</string>
|
||||
<string name="pgm_no_location_providers">Thiết bị không hỗ trợ xác định vị trí vì vậy ứng dụng không thể xác định vị trí của bạn.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Dịch vụ xác định vị trí không được kích hoạt.</string>
|
||||
<string name="pgm_location_rejected_by_user">Người dùng không cho phép sử dụng dịch vụ xác định vị trí.</string>
|
||||
<string name="pgm_can_not_get_location">Không thể xác định vị trí của bạn. Vui lòng thử lại..</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="pgm_google_close_button">Close</string>
|
||||
<string name="pgm_api_key_error">Please replace \'GOOGLE_MAPS_ANDROID_API_KEY\' in the config.xml with your API Key!</string>
|
||||
<string name="pgm_google_play_error">Google Maps Android API v2 is not available for some reason on this device.</string>
|
||||
<string name="pgm_google_play_developer_error">The application is miss-configured. This error is not recoverable and will be treated as fatal. The developer should look at the logs after this to determine more actionable information.</string>
|
||||
<string name="pgm_google_play_internal_error">An internal error of Google Play Services occurred. Please retry, and it should resolve the problem.</string>
|
||||
<string name="pgm_google_play_invalid_account">You attempted to connect to the service with an invalid account name specified.</string>
|
||||
<string name="pgm_google_play_license_check_failed">The application is not licensed to the user. This error is not recoverable and will be treated as fatal.</string>
|
||||
<string name="pgm_google_play_network_error">A network error occurred. Please retry, and it should resolve the problem.</string>
|
||||
<string name="pgm_google_play_service_disabled">The installed version of Google Play services has been disabled on this device. Please turn on Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_invalid">The version of the Google Play services installed on this device is not authentic. Please update the Google Play Services from Google Play Store.</string>
|
||||
<string name="pgm_google_play_service_missing">Google Play services is missing on this device. Please install the Google Play Services.</string>
|
||||
<string name="pgm_google_play_service_update_required">The installed version of Google Play services is out of date. Please update the Google Play Services from Google Play Store.</string>
|
||||
<string name="pgm_google_play_sign_in_required">You attempted to connect to the service but you are not signed in. Please check the Google Play Services configuration</string>
|
||||
<string name="pgm_no_location_providers">Since this device does not have any location provider, this app can not detect your location.</string>
|
||||
<string name="pgm_no_location_service_is_disabled">Location service is disabled.</string>
|
||||
<string name="pgm_location_rejected_by_user">Location service is rejected by user.</string>
|
||||
<string name="pgm_can_not_get_location">Can not detect your location. Try again.</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,181 @@
|
||||
|
||||
|
||||
|
||||
var PluginMap = require('cordova-plugin-googlemaps.PluginMap'),
|
||||
PluginStreetViewPanorama = require('cordova-plugin-googlemaps.PluginStreetViewPanorama'),
|
||||
event = require('cordova-plugin-googlemaps.event'),
|
||||
Environment = require('cordova-plugin-googlemaps.PluginEnvironment');
|
||||
|
||||
var MAPS = {};
|
||||
|
||||
var API_LOADED_STATUS = 0; // 0: not loaded, 1: loading, 2: completed
|
||||
|
||||
document.addEventListener('load_googlemaps', function() {
|
||||
var envOptions = Environment._getEnv();
|
||||
var API_KEY_FOR_BROWSER;
|
||||
if (envOptions) {
|
||||
if (location.protocol === 'https:') {
|
||||
API_KEY_FOR_BROWSER = envOptions.API_KEY_FOR_BROWSER_RELEASE;
|
||||
} else {
|
||||
API_KEY_FOR_BROWSER = envOptions.API_KEY_FOR_BROWSER_DEBUG;
|
||||
}
|
||||
}
|
||||
API_LOADED_STATUS = 1;
|
||||
|
||||
var secureStripeScript = document.createElement('script');
|
||||
if (API_KEY_FOR_BROWSER && API_KEY_FOR_BROWSER.length > 35) {
|
||||
secureStripeScript.setAttribute('src','https://maps.googleapis.com/maps/api/js?key=' + API_KEY_FOR_BROWSER);
|
||||
} else {
|
||||
// for development only
|
||||
secureStripeScript.setAttribute('src','https://maps.googleapis.com/maps/api/js');
|
||||
}
|
||||
secureStripeScript.addEventListener('load', function() {
|
||||
API_LOADED_STATUS = 2;
|
||||
|
||||
var mKeys = Object.keys(MAPS);
|
||||
mKeys.forEach(function(mkey) {
|
||||
var map = MAPS[mkey];
|
||||
if (!map.get('isGoogleReady')) {
|
||||
map.trigger('googleready');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
secureStripeScript.addEventListener('error', function(error) {
|
||||
console.log('Can not load the Google Maps JavaScript API v3');
|
||||
console.log(error);
|
||||
|
||||
var mKeys = Object.keys(MAPS);
|
||||
mKeys.forEach(function(mkey) {
|
||||
var map = MAPS[mkey];
|
||||
if (map) {
|
||||
map.trigger('load_error');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementsByTagName('head')[0].appendChild(secureStripeScript);
|
||||
|
||||
}, {
|
||||
once: true
|
||||
});
|
||||
|
||||
var stub = function(onSuccess) {
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
var CordovaGoogleMaps = {
|
||||
resume: stub,
|
||||
pause: stub,
|
||||
getMap: function(onSuccess, onError, args) {
|
||||
// memory cleanup
|
||||
var mapIDs = Object.keys(MAPS);
|
||||
mapIDs.forEach(function(mapId) {
|
||||
var mapDivId = document.querySelector('[__pluginmapid=\'' + mapId + '\']');
|
||||
if (!mapDivId) {
|
||||
if (MAPS[mapDivId]) {
|
||||
MAPS[mapDivId].destroy();
|
||||
}
|
||||
MAPS[mapDivId] = undefined;
|
||||
delete MAPS[mapDivId];
|
||||
}
|
||||
});
|
||||
|
||||
var meta = args[0],
|
||||
mapId = meta.__pgmId;
|
||||
args[0] = mapId;
|
||||
args.unshift(this);
|
||||
|
||||
var pluginMap = new (PluginMap.bind.apply(PluginMap, args));
|
||||
MAPS[mapId] = pluginMap;
|
||||
var dummyObj = {};
|
||||
var keys = Object.getOwnPropertyNames(PluginMap.prototype).filter(function (p) {
|
||||
return typeof PluginMap.prototype[p] === 'function';
|
||||
});
|
||||
keys.forEach(function(key) {
|
||||
dummyObj[key] = pluginMap[key].bind(pluginMap);
|
||||
});
|
||||
require('cordova/exec/proxy').add(mapId, dummyObj);
|
||||
|
||||
pluginMap.one(event.MAP_READY, onSuccess);
|
||||
pluginMap.one('load_error', onError);
|
||||
|
||||
// Does this app already load the google maps library?
|
||||
API_LOADED_STATUS = (window.google && window.google.maps) ? 2 : API_LOADED_STATUS;
|
||||
|
||||
switch(API_LOADED_STATUS) {
|
||||
case 0:
|
||||
cordova.fireDocumentEvent('load_googlemaps', []);
|
||||
break;
|
||||
case 2:
|
||||
pluginMap.trigger('googleready');
|
||||
break;
|
||||
}
|
||||
},
|
||||
removeMap: function(onSuccess, onError, args) {
|
||||
var mapId = args[0];
|
||||
var pluginMap = MAPS[mapId];
|
||||
if (pluginMap) {
|
||||
var map = pluginMap.get('map');
|
||||
google.maps.event.clearInstanceListeners(map);
|
||||
var mapDiv = map.getDiv();
|
||||
if (mapDiv) {
|
||||
mapDiv.parentNode.removeChild(mapDiv);
|
||||
pluginMap.set('map', undefined);
|
||||
}
|
||||
}
|
||||
pluginMap.destroy();
|
||||
pluginMap = null;
|
||||
MAPS[mapId] = undefined;
|
||||
delete MAPS[mapId];
|
||||
onSuccess();
|
||||
},
|
||||
|
||||
getPanorama: function(onSuccess, onError, args) {
|
||||
// memory cleanup
|
||||
var mapIDs = Object.keys(MAPS);
|
||||
mapIDs.forEach(function(mapId) {
|
||||
var mapDivId = document.querySelector('[__pluginmapid=\'' + mapId + '\']');
|
||||
if (!mapDivId) {
|
||||
if (MAPS[mapDivId]) {
|
||||
MAPS[mapDivId].destroy();
|
||||
}
|
||||
MAPS[mapDivId] = undefined;
|
||||
delete MAPS[mapDivId];
|
||||
}
|
||||
});
|
||||
|
||||
var meta = args[0],
|
||||
mapId = meta.__pgmId;
|
||||
args[0] = mapId;
|
||||
args.unshift(this);
|
||||
|
||||
var pluginStreetView = new (PluginStreetViewPanorama.bind.apply(PluginStreetViewPanorama, args));
|
||||
MAPS[mapId] = pluginStreetView;
|
||||
var dummyObj = {};
|
||||
var keys = Object.getOwnPropertyNames(PluginStreetViewPanorama.prototype).filter(function (p) {
|
||||
return typeof PluginStreetViewPanorama.prototype[p] === 'function';
|
||||
});
|
||||
keys.forEach(function(key) {
|
||||
dummyObj[key] = pluginStreetView[key].bind(pluginStreetView);
|
||||
});
|
||||
require('cordova/exec/proxy').add(mapId, dummyObj);
|
||||
|
||||
pluginStreetView.one(event.PANORAMA_READY, onSuccess);
|
||||
pluginStreetView.one('load_error', onError);
|
||||
|
||||
// Does this app already load the google maps library?
|
||||
API_LOADED_STATUS = (window.google && window.google.maps) ? 2 : API_LOADED_STATUS;
|
||||
|
||||
switch(API_LOADED_STATUS) {
|
||||
case 0:
|
||||
cordova.fireDocumentEvent('load_googlemaps', []);
|
||||
break;
|
||||
case 2:
|
||||
pluginStreetView.trigger('googleready');
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
require('cordova/exec/proxy').add('CordovaGoogleMaps', CordovaGoogleMaps);
|
||||
@@ -0,0 +1,199 @@
|
||||
|
||||
|
||||
|
||||
var utils = require('cordova/utils'),
|
||||
event = require('cordova-plugin-googlemaps.event'),
|
||||
BaseClass = require('cordova-plugin-googlemaps.BaseClass');
|
||||
|
||||
function PluginCircle(pluginMap) {
|
||||
var self = this;
|
||||
BaseClass.apply(self);
|
||||
Object.defineProperty(self, 'pluginMap', {
|
||||
value: pluginMap,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
utils.extend(PluginCircle, BaseClass);
|
||||
|
||||
PluginCircle.prototype._create = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
map = self.pluginMap.get('map'),
|
||||
circleId = 'circle_' + args[2],
|
||||
pluginOptions = args[1];
|
||||
|
||||
var circleOpts = {
|
||||
'overlayId': circleId,
|
||||
'map': map
|
||||
};
|
||||
|
||||
circleOpts.center = pluginOptions.center;
|
||||
circleOpts.radius = pluginOptions.radius;
|
||||
|
||||
if (Array.isArray(pluginOptions.strokeColor)) {
|
||||
circleOpts.strokeColor = 'rgb(' + pluginOptions.strokeColor[0] + ',' + pluginOptions.strokeColor[1] + ',' + pluginOptions.strokeColor[2] + ')';
|
||||
circleOpts.strokeOpacity = pluginOptions.strokeColor[3] / 256;
|
||||
}
|
||||
if (Array.isArray(pluginOptions.fillColor)) {
|
||||
circleOpts.fillColor = 'rgb(' + pluginOptions.fillColor[0] + ',' + pluginOptions.fillColor[1] + ',' + pluginOptions.fillColor[2] + ')';
|
||||
circleOpts.fillOpacity = pluginOptions.fillColor[3] / 256;
|
||||
}
|
||||
if ('width' in pluginOptions) {
|
||||
circleOpts.strokeWeight = pluginOptions.width;
|
||||
}
|
||||
if ('zIndex' in pluginOptions) {
|
||||
circleOpts.zIndex = pluginOptions.zIndex;
|
||||
}
|
||||
if ('visible' in pluginOptions) {
|
||||
circleOpts.visible = pluginOptions.visible;
|
||||
}
|
||||
if ('clickable' in pluginOptions) {
|
||||
circleOpts.clickable = pluginOptions.clickable;
|
||||
}
|
||||
|
||||
var circle = new google.maps.Circle(circleOpts);
|
||||
circle.addListener('click', function(polyMouseEvt) {
|
||||
self._onCircleEvent.call(self, circle, polyMouseEvt);
|
||||
});
|
||||
|
||||
self.pluginMap.objects[circleId] = circle;
|
||||
|
||||
onSuccess({
|
||||
'__pgmId': circleId
|
||||
});
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setCenter = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var lat = args[1];
|
||||
var lng = args[2];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
circle.setCenter(new google.maps.LatLng(lat, lng));
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setFillColor = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var fillColor = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
|
||||
if (Array.isArray(fillColor)) {
|
||||
circle.setOptions({
|
||||
'fillColor': 'rgb(' + fillColor[0] + ',' + fillColor[1] + ',' + fillColor[2] + ')',
|
||||
'fillOpacity': fillColor[3] / 256
|
||||
});
|
||||
}
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setStrokeColor = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var strokeColor = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
|
||||
if (Array.isArray(strokeColor)) {
|
||||
circle.setOptions({
|
||||
'strokeColor': 'rgb(' + strokeColor[0] + ',' + strokeColor[1] + ',' + strokeColor[2] + ')',
|
||||
'strokeOpacity': strokeColor[3] / 256
|
||||
});
|
||||
}
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setStrokeWidth = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var width = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
circle.setOptions({
|
||||
'strokeWeight': width
|
||||
});
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setRadius = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var radius = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
circle.setRadius(radius);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setZIndex = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var zIndex = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
circle.setOptions({
|
||||
'zIndex': zIndex
|
||||
});
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setVisible = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var visible = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
circle.setVisible(visible === true);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.setClickable = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var clickable = args[1];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
circle.setOptions({
|
||||
'clickable': clickable === true
|
||||
});
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype.remove = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var circle = self.pluginMap.objects[overlayId];
|
||||
if (circle) {
|
||||
google.maps.event.clearInstanceListeners(circle);
|
||||
circle.setMap(null);
|
||||
circle = undefined;
|
||||
self.pluginMap.objects[overlayId] = undefined;
|
||||
delete self.pluginMap.objects[overlayId];
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginCircle.prototype._onCircleEvent = function(circle, polyMouseEvt) {
|
||||
var self = this,
|
||||
mapId = self.pluginMap.__pgmId;
|
||||
if (mapId in plugin.google.maps) {
|
||||
plugin.google.maps[mapId]({
|
||||
'evtName': event.CIRCLE_CLICK,
|
||||
'callback': '_onOverlayEvent',
|
||||
'args': [circle.overlayId, new plugin.google.maps.LatLng(polyMouseEvt.latLng.lat(), polyMouseEvt.latLng.lng())]
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
module.exports = PluginCircle;
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
var envOptions = {};
|
||||
|
||||
module.exports = {
|
||||
'isAvailable': function(onSuccess) {
|
||||
onSuccess();
|
||||
},
|
||||
'setBackGroundColor': function(onSuccess) {
|
||||
// stub
|
||||
onSuccess();
|
||||
},
|
||||
'getLicenseInfo': function(onSuccess) {
|
||||
// stub
|
||||
onSuccess('cordova-plugin-googlemaps for browser does not need to display any open source lincenses. But for iOS, you still need to display the lincense.');
|
||||
},
|
||||
'setEnv': function(onSuccess, onError, args) {
|
||||
var options = args[0];
|
||||
var keys = Object.keys(options);
|
||||
keys.forEach(function(key) {
|
||||
envOptions[key] = options[key];
|
||||
});
|
||||
onSuccess();
|
||||
},
|
||||
'_getEnv': function() {
|
||||
// internal method
|
||||
return envOptions;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
require('cordova/exec/proxy').add('PluginEnvironment', module.exports);
|
||||
@@ -0,0 +1,176 @@
|
||||
|
||||
|
||||
var BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass');
|
||||
|
||||
var geocoder = null;
|
||||
var lastRequestTime = 0;
|
||||
var QUEUE = new BaseArrayClass();
|
||||
QUEUE.on('insert_at', function() {
|
||||
if (QUEUE.getLength() === 1) {
|
||||
this.trigger('next');
|
||||
}
|
||||
});
|
||||
QUEUE.one('insert_at', function() {
|
||||
geocoder = new google.maps.Geocoder();
|
||||
this.trigger('next');
|
||||
});
|
||||
QUEUE.on('next', function() {
|
||||
var self = QUEUE;
|
||||
if (!geocoder || self._executing || QUEUE.getLength() === 0) {
|
||||
return;
|
||||
}
|
||||
if (Date.now() - lastRequestTime < 300) {
|
||||
setTimeout(function() {
|
||||
self.trigger('next');
|
||||
}, 300 + Math.floor(Math.random() * 200));
|
||||
return;
|
||||
}
|
||||
lastRequestTime = Date.now();
|
||||
self._executing = true;
|
||||
|
||||
var cmd = QUEUE.removeAt(0, true);
|
||||
geocoder.geocode(cmd.geocoderRequest, function(results, status) {
|
||||
switch(status) {
|
||||
case google.maps.GeocoderStatus.ERROR:
|
||||
cmd.onError('[geocoding] Cannot connect to Google servers');
|
||||
return;
|
||||
case google.maps.GeocoderStatus.INVALID_REQUEST:
|
||||
cmd.onError('[geocoding] Invalid request for geocoder');
|
||||
return;
|
||||
case google.maps.GeocoderStatus.OVER_QUERY_LIMIT:
|
||||
QUEUE.insertAt(0, cmd);
|
||||
console.warn('[geocoding] Due to the OVER_QUERY_LIMIT error, wait 3 sec, then try again.');
|
||||
setTimeout(function() {
|
||||
self._executing = false;
|
||||
self.trigger('next');
|
||||
}, 3000 + Math.floor(Math.random() * 200));
|
||||
return;
|
||||
case google.maps.GeocoderStatus.REQUEST_DENIED:
|
||||
cmd.onError('[geocoding] Google denited your geocoding request.');
|
||||
return;
|
||||
case google.maps.GeocoderStatus.UNKNOWN_ERROR:
|
||||
cmd.onError('[geocoding] There was an unknown error. Please try again.');
|
||||
return;
|
||||
}
|
||||
|
||||
var pluginResults = results.map(function(geocoderResult) {
|
||||
var result = {
|
||||
'position': {
|
||||
'lat': geocoderResult.geometry.location.lat(),
|
||||
'lng': geocoderResult.geometry.location.lng()
|
||||
},
|
||||
extra: {
|
||||
lines: []
|
||||
}
|
||||
};
|
||||
if (geocoderResult.place_id) {
|
||||
result.extra.place_id = geocoderResult.place_id;
|
||||
}
|
||||
if (geocoderResult.plus_code) {
|
||||
result.extra.plus_code = geocoderResult.plus_code;
|
||||
}
|
||||
if (geocoderResult.types) {
|
||||
result.extra.types = geocoderResult.types;
|
||||
}
|
||||
|
||||
var administrative_area = [];
|
||||
var sublocality_area = [];
|
||||
geocoderResult.address_components.forEach(function(addrComp) {
|
||||
result.extra.lines.push(addrComp.long_name);
|
||||
if (!result.locality && addrComp.types.indexOf('locality') > -1) {
|
||||
result.locality = addrComp.short_name;
|
||||
}
|
||||
if (addrComp.types.indexOf('administrative_area_level_1') > -1 ||
|
||||
addrComp.types.indexOf('administrative_area_level_2') > -1 ||
|
||||
addrComp.types.indexOf('administrative_area_level_3') > -1 ||
|
||||
addrComp.types.indexOf('administrative_area_level_4') > -1) {
|
||||
addrComp.types.forEach(function(type) {
|
||||
if (type.indexOf('administrative_area_level_') === 0) {
|
||||
var idx = parseInt(type.replace('administrative_area_level_', ''), 10);
|
||||
administrative_area[idx - 1] = addrComp.long_name;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (addrComp.types.indexOf('sublocality_level_1') > -1 ||
|
||||
addrComp.types.indexOf('sublocality_level_2') > -1 ||
|
||||
addrComp.types.indexOf('sublocality_level_3') > -1 ||
|
||||
addrComp.types.indexOf('sublocality_level_4') > -1 ||
|
||||
addrComp.types.indexOf('sublocality_level_5') > -1) {
|
||||
addrComp.types.forEach(function(type) {
|
||||
if (type.indexOf('sublocality_level_') === 0) {
|
||||
var idx = parseInt(type.replace('sublocality_level_', ''), 10);
|
||||
sublocality_area[idx - 1] = addrComp.long_name;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!result.country && addrComp.types.indexOf('country') > -1) {
|
||||
result.country = addrComp.long_name;
|
||||
result.countryCode = addrComp.short_name;
|
||||
}
|
||||
if (!result.postalCode && addrComp.types.indexOf('postal_code') > -1) {
|
||||
result.postalCode = addrComp.long_name;
|
||||
}
|
||||
if (!result.thoroughfare && addrComp.types.indexOf('street_address') > -1) {
|
||||
result.thoroughfare = addrComp.long_name;
|
||||
}
|
||||
});
|
||||
|
||||
if (administrative_area.length > 0) {
|
||||
result.adminArea = administrative_area.shift();
|
||||
result.subAdminArea = administrative_area.join(',');
|
||||
}
|
||||
if (sublocality_area.length > 0) {
|
||||
result.subLocality = sublocality_area.join(',');
|
||||
}
|
||||
//result.extra = geocoderResult.address_components; // for debug
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
self._executing = false;
|
||||
cmd.onSuccess({
|
||||
'idx': cmd.pluginRequest.idx,
|
||||
'results': pluginResults
|
||||
});
|
||||
|
||||
// Insert delay to prevent the OVER_QUERY_LIMIT error.
|
||||
var delay = 300 + Math.floor(Math.random() * 200);
|
||||
setTimeout(function() {
|
||||
self._executing = false;
|
||||
self.trigger('next');
|
||||
}, delay);
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
'geocode': function(onSuccess, onError, args) {
|
||||
var request = args[0];
|
||||
var geocoderRequest = {};
|
||||
|
||||
if (!request.position && request.address) {
|
||||
//---------------------
|
||||
// Geocoding
|
||||
//---------------------
|
||||
if (request.bounds && Array.isArray(request.bounds)) {
|
||||
var bounds = new google.maps.LatLngBounds();
|
||||
request.bounds.forEach(function(position) {
|
||||
bounds.extend(position);
|
||||
});
|
||||
geocoderRequest.bounds = bounds;
|
||||
}
|
||||
geocoderRequest.address = request.address;
|
||||
}
|
||||
if (request.position && !request.address) {
|
||||
geocoderRequest.location = request.position;
|
||||
}
|
||||
QUEUE.push({
|
||||
'geocoderRequest': geocoderRequest,
|
||||
'pluginRequest': request,
|
||||
'onSuccess': onSuccess,
|
||||
'onError': onError
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
require('cordova/exec/proxy').add('PluginGeocoder', module.exports);
|
||||
@@ -0,0 +1,412 @@
|
||||
|
||||
|
||||
|
||||
var utils = require('cordova/utils'),
|
||||
event = require('cordova-plugin-googlemaps.event'),
|
||||
BaseClass = require('cordova-plugin-googlemaps.BaseClass'),
|
||||
Spherical = require('cordova-plugin-googlemaps.spherical'),
|
||||
LatLng = require('cordova-plugin-googlemaps.LatLng');
|
||||
|
||||
function PluginGroundOverlay(pluginMap) {
|
||||
var self = this;
|
||||
BaseClass.apply(self);
|
||||
Object.defineProperty(self, 'pluginMap', {
|
||||
value: pluginMap,
|
||||
writable: false
|
||||
});
|
||||
}
|
||||
|
||||
utils.extend(PluginGroundOverlay, BaseClass);
|
||||
|
||||
PluginGroundOverlay.prototype._create = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
map = self.pluginMap.get('map'),
|
||||
groundoverlayId = 'groundoverlay_' + args[2],
|
||||
pluginOptions = args[1];
|
||||
|
||||
var groundoverlayOpts = {
|
||||
'overlayId': groundoverlayId,
|
||||
'map': map
|
||||
};
|
||||
|
||||
var bounds = new google.maps.LatLngBounds();
|
||||
pluginOptions.bounds.forEach(function(latLng) {
|
||||
bounds.extend(latLng);
|
||||
});
|
||||
|
||||
if ('bearing' in pluginOptions) {
|
||||
groundoverlayOpts.bearing = pluginOptions.bearing;
|
||||
}
|
||||
if ('zIndex' in pluginOptions) {
|
||||
groundoverlayOpts.zIndex = pluginOptions.zIndex;
|
||||
}
|
||||
if ('opacity' in pluginOptions) {
|
||||
groundoverlayOpts.opacity = pluginOptions.opacity;
|
||||
}
|
||||
if ('visible' in pluginOptions) {
|
||||
groundoverlayOpts.visible = pluginOptions.visible;
|
||||
}
|
||||
if ('clickable' in pluginOptions) {
|
||||
groundoverlayOpts.clickable = pluginOptions.clickable;
|
||||
}
|
||||
|
||||
var groundoverlay = newGroundOverlay(pluginOptions.url, bounds, groundoverlayOpts);
|
||||
groundoverlay.addListener('click', function(polyMouseEvt) {
|
||||
self._onGroundOverlayEvent.call(self, groundoverlay, polyMouseEvt);
|
||||
});
|
||||
|
||||
self.pluginMap.objects[groundoverlayId] = groundoverlay;
|
||||
self.pluginMap.objects['groundoverlay_property_' + groundoverlayId] = pluginOptions;
|
||||
|
||||
|
||||
onSuccess({
|
||||
'__pgmId': groundoverlayId
|
||||
});
|
||||
};
|
||||
PluginGroundOverlay.prototype.setImage = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
overlayId = args[0],
|
||||
url = args[1],
|
||||
groundoverlay = self.pluginMap.objects[overlayId];
|
||||
|
||||
if (groundoverlay) {
|
||||
groundoverlay.setImage(url);
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype.setBearing = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
groundOverlay.setBearing(args[1]);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype.setOpacity = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
groundOverlay.setOpacity(args[1]);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype.setBounds = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
|
||||
var points = args[1];
|
||||
var bounds = new google.maps.LatLngBounds();
|
||||
points.forEach(function(latLng) {
|
||||
bounds.extend(latLng);
|
||||
});
|
||||
groundOverlay.setBounds(bounds);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype.setZIndex = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var zIndex = args[1];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
groundOverlay.setZIndex(zIndex);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype.setClickable = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
groundOverlay.setClickable(args[1]);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginGroundOverlay.prototype.setVisible = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
groundOverlay.setVisible(args[1]);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype.remove = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var groundOverlay = self.pluginMap.objects[overlayId];
|
||||
if (groundOverlay) {
|
||||
google.maps.event.clearInstanceListeners(groundOverlay);
|
||||
groundOverlay.setMap(null);
|
||||
groundOverlay.remove();
|
||||
groundOverlay = undefined;
|
||||
self.pluginMap.objects[overlayId] = undefined;
|
||||
delete self.pluginMap.objects[overlayId];
|
||||
delete self.pluginMap.objects['groundoverlay_property_' + overlayId];
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginGroundOverlay.prototype._onGroundOverlayEvent = function(groundoverlay, mouseEvt) {
|
||||
var self = this,
|
||||
mapId = self.pluginMap.__pgmId;
|
||||
if (mapId in plugin.google.maps) {
|
||||
plugin.google.maps[mapId]({
|
||||
'evtName': event.GROUND_OVERLAY_CLICK,
|
||||
'callback': '_onOverlayEvent',
|
||||
'args': [groundoverlay.overlayId, new LatLng(mouseEvt.latLng.lat(), mouseEvt.latLng.lng())]
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
module.exports = PluginGroundOverlay;
|
||||
|
||||
function newGroundOverlay(url, bounds, options) {
|
||||
|
||||
// https://github.com/apache/cordova-js/blob/c75e8059114255d1dbae1ede398e6626708ee9f3/src/common/utils.js#L167
|
||||
if (CustomGroundOverlay.__super__ !== google.maps.OverlayView.prototype) {
|
||||
var key, defined = {};
|
||||
for (key in CustomGroundOverlay.prototype) {
|
||||
defined[key] = CustomGroundOverlay.prototype[key];
|
||||
}
|
||||
utils.extend(CustomGroundOverlay, google.maps.OverlayView);
|
||||
for (key in defined) {
|
||||
CustomGroundOverlay.prototype[key] = defined[key];
|
||||
}
|
||||
}
|
||||
|
||||
return new CustomGroundOverlay(url, bounds, options);
|
||||
}
|
||||
|
||||
function CustomGroundOverlay(url, bounds, options) {
|
||||
google.maps.OverlayView.apply(this);
|
||||
var self = this;
|
||||
|
||||
//------------------------------------------------------------
|
||||
// Create an img element, then put it on map as GroundOverlay
|
||||
//------------------------------------------------------------
|
||||
var img = new Image();
|
||||
img.src = url;
|
||||
img.style.position = 'absolute';
|
||||
img.style.WebkitTransformOrigin = '50% 50%';
|
||||
img.style.MozTransformOrigin = '50% 50%';
|
||||
img.style.transformOrigin = '50% 50%';
|
||||
self.set('img', img);
|
||||
|
||||
self.set('url', url);
|
||||
self.addListener('url_changed', function() {
|
||||
img.src = self.get('url');
|
||||
});
|
||||
|
||||
var path = new google.maps.MVCArray([
|
||||
new google.maps.LatLng(0, 0),
|
||||
new google.maps.LatLng(0, 0),
|
||||
new google.maps.LatLng(0, 0),
|
||||
new google.maps.LatLng(0, 0)
|
||||
]);
|
||||
self.set('path', path);
|
||||
|
||||
self.addListener('bearing_changed', self.updateVertixes);
|
||||
self.addListener('bounds_changed', self.updateVertixes);
|
||||
self.addListener('opacity_changed', function() {
|
||||
img.style.opacity = self.get('opacity');
|
||||
});
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Create a transparent rectangle above the groundOverlay,
|
||||
// then catch click event
|
||||
//---------------------------------------------------------
|
||||
var touchPolygon = new google.maps.Polygon({
|
||||
'map': options.map,
|
||||
'paths': path,
|
||||
'clickable': true,
|
||||
'fillColor': 'red',
|
||||
'fillOpacity': 0,
|
||||
'strokeOpacity': 0,
|
||||
'zIndex': options.zIndex || 0
|
||||
});
|
||||
self.addListener('visible_changed', function() {
|
||||
img.style.visibility = self.get('visible') ? 'visible' : 'hidden';
|
||||
touchPolygon.setVisible(self.get('visible'));
|
||||
});
|
||||
|
||||
self.addListener('zIndex_changed', function() {
|
||||
touchPolygon.setOptions({
|
||||
'zIndex': self.get('zIndex')
|
||||
});
|
||||
img.style.zIndex = self.get('zIndex');
|
||||
});
|
||||
|
||||
touchPolygon.addListener('click', function(evt) {
|
||||
if (self.get('clickable')) {
|
||||
google.maps.event.trigger(self, 'click', {
|
||||
'latLng': evt. latLng
|
||||
});
|
||||
}
|
||||
});
|
||||
self.set('touchPolygon', touchPolygon);
|
||||
|
||||
var markers = [];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
markers.push(new google.maps.Marker({
|
||||
'position': {'lat': 0, 'lng': 0},
|
||||
'map': options.map
|
||||
}));
|
||||
}
|
||||
self.set('markers', markers);
|
||||
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Apply option values
|
||||
//---------------------------------------------------------
|
||||
for (var key in options) {
|
||||
self.set(key, options[key]);
|
||||
}
|
||||
self.set('bounds', bounds);
|
||||
}
|
||||
CustomGroundOverlay.prototype.updateVertixes = function() {
|
||||
var self = this;
|
||||
var degree = self.get('bearing') || 0;
|
||||
|
||||
// Get center
|
||||
var bounds = self.get('bounds');
|
||||
if (!bounds) {
|
||||
return;
|
||||
}
|
||||
var center = bounds.getCenter(),
|
||||
centerPos = {
|
||||
'lat': center.lat(),
|
||||
'lng': center.lng()
|
||||
};
|
||||
|
||||
// original NorthEast
|
||||
var orgNE = bounds.getNorthEast(),
|
||||
orgSW = bounds.getSouthWest(),
|
||||
orgNEpos = {
|
||||
'lat': orgNE.lat(),
|
||||
'lng': orgNE.lng()
|
||||
};
|
||||
var orgDrgreeNE = Spherical.computeHeading(centerPos, orgNEpos);
|
||||
var orgDrgreeSE = Spherical.computeHeading(centerPos, {
|
||||
'lat': orgSW.lat(),
|
||||
'lng': orgNE.lng()
|
||||
});
|
||||
|
||||
var distanceCenter2NE = Spherical.computeDistanceBetween(orgNEpos, centerPos);
|
||||
// new NorthEast
|
||||
var newNE = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeNE);
|
||||
|
||||
// new SourthEast
|
||||
var newSE = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeSE);
|
||||
// new SourthWest
|
||||
var newSW = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeNE + 180);
|
||||
// new SouthEast
|
||||
var newNW = Spherical.computeOffsetOrigin(centerPos, distanceCenter2NE, degree + orgDrgreeSE + 180);
|
||||
|
||||
// Caclulate bounds
|
||||
var path = self.get('path');
|
||||
path.setAt(0, new google.maps.LatLng(newNE.lat, newNE.lng));
|
||||
path.setAt(1, new google.maps.LatLng(newSE.lat, newSE.lng));
|
||||
path.setAt(2, new google.maps.LatLng(newSW.lat, newSW.lng));
|
||||
path.setAt(3, new google.maps.LatLng(newNW.lat, newNW.lng));
|
||||
|
||||
self.draw();
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.remove = function() {
|
||||
var self = this;
|
||||
self.set('img', undefined);
|
||||
google.maps.event.clearInstanceListeners(self);
|
||||
google.maps.event.clearInstanceListeners(self.get('touchPolygon'));
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setClickable = function(clickable) {
|
||||
var self = this;
|
||||
self.set('clickable', clickable);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setOpacity = function(opacity) {
|
||||
var self = this;
|
||||
self.get('img').style.opacity = opacity;
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setBearing = function(bearing) {
|
||||
var self = this;
|
||||
self.set('bearing', bearing);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setBounds = function(bounds) {
|
||||
var self = this;
|
||||
self.set('bounds', bounds);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setOpacity = function(opacity) {
|
||||
var self = this;
|
||||
self.set('opacity', opacity);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setZIndex = function(zIndex) {
|
||||
var self = this;
|
||||
self.set('zIndex', zIndex);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setVisible = function(visible) {
|
||||
var self = this;
|
||||
self.set('visible', visible);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.setImage = function(url) {
|
||||
var self = this;
|
||||
self.set('url', url);
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.getBounds = function() {
|
||||
var self = this;
|
||||
return new google.maps.LatLngBounds(
|
||||
self.get('sw'), self.get('ne')
|
||||
);
|
||||
};
|
||||
CustomGroundOverlay.prototype.draw = function() {
|
||||
var self = this,
|
||||
projection = self.getProjection();
|
||||
if (!projection) {
|
||||
return;
|
||||
}
|
||||
var bounds = self.get('bounds'),
|
||||
img = self.get('img');
|
||||
|
||||
// Calculate positions
|
||||
var swPx = projection.fromLatLngToDivPixel(bounds.getSouthWest()),
|
||||
nePx = projection.fromLatLngToDivPixel(bounds.getNorthEast());
|
||||
|
||||
img.style.left = swPx.x + 'px';
|
||||
img.style.top = nePx.y + 'px';
|
||||
img.style.width = (nePx.x - swPx.x) + 'px';
|
||||
img.style.height = (swPx.y - nePx.y) + 'px';
|
||||
img.style.transform = 'rotate(' + self.get('bearing') + 'deg)';
|
||||
img.style.WebkitTransform = 'rotate(' + self.get('bearing') + 'deg)';
|
||||
img.style.MozTransform = 'rotate(' + self.get('bearing') + 'deg)';
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.onAdd = function() {
|
||||
var self = this;
|
||||
self.set('mapPane', self.getPanes().mapPane);
|
||||
self.getPanes().mapPane.appendChild(self.get('img'));
|
||||
};
|
||||
|
||||
CustomGroundOverlay.prototype.onRemove = function() {
|
||||
var self = this;
|
||||
self.get('mapPane').removeChild(self.get('img'));
|
||||
google.maps.event.clearInstanceListeners(self.get('img'));
|
||||
};
|
||||
@@ -0,0 +1,545 @@
|
||||
|
||||
/* eslint no-useless-escape: off */
|
||||
|
||||
var InlineWorker = require('cordova-plugin-googlemaps.InlineWorker');
|
||||
|
||||
function PluginKmlOverlay() {
|
||||
// stub
|
||||
}
|
||||
|
||||
PluginKmlOverlay.prototype._create = function(onSuccess, onError, args) {
|
||||
var pluginOptions = args[1];
|
||||
|
||||
if (!/^https?:/.test(location.protocol)) {
|
||||
return onError('KmlOverlay is only available on http: or https: protocols.');
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
// Parse the xml file using WebWorker
|
||||
//-------------------------------------
|
||||
var worker = new InlineWorker(loadKml);
|
||||
worker.onmessage = function(evt) {
|
||||
//console.log('host message', evt.data);
|
||||
worker.terminate();
|
||||
onSuccess(evt.data);
|
||||
};
|
||||
worker.onerror = onError;
|
||||
var link = document.createElement('a');
|
||||
link.href = pluginOptions.url;
|
||||
var url = link.protocol+'//'+link.host+link.pathname+link.search;
|
||||
worker.postMessage({
|
||||
'url': url
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = PluginKmlOverlay;
|
||||
|
||||
function loadKml(self) {
|
||||
|
||||
// code: https://stackoverflow.com/q/32912732/697856
|
||||
var createCORSRequest = function(method, url, asynch) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
if ('withCredentials' in xhr) {
|
||||
// XHR for Chrome/Firefox/Opera/Safari.
|
||||
xhr.open(method, url, asynch);
|
||||
// xhr.setRequestHeader('MEDIBOX', 'login');
|
||||
xhr.setRequestHeader('Content-Type', 'application/xml; charset=UTF-8');
|
||||
} else if (typeof window.XDomainRequest != 'undefined') {
|
||||
// XDomainRequest for IE.
|
||||
xhr = new window.XDomainRequest();
|
||||
xhr.open(method, url, asynch);
|
||||
} else {
|
||||
// CORS not supported.
|
||||
xhr = null;
|
||||
}
|
||||
return xhr;
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------
|
||||
// modified fromXML (xml parser)
|
||||
//---------------------------------------
|
||||
var fromXML = (function() {
|
||||
// fromXML
|
||||
// https://github.com/kawanet/from-xml
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2016 Yusuke Kawasaki
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
/**
|
||||
* The fromXML() method parses an XML string, constructing the JavaScript
|
||||
* value or object described by the string.
|
||||
*
|
||||
* @function fromXML
|
||||
* @param text {String} The string to parse as XML
|
||||
* @param [reviver] {Function} If a function, prescribes how the value
|
||||
* originally produced by parsing is transformed, before being returned.
|
||||
* @returns {Object}
|
||||
*/
|
||||
|
||||
var UNESCAPE = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
''': '\'',
|
||||
'"': '"'
|
||||
};
|
||||
|
||||
var CHILD_NODE_KEY = '#';
|
||||
|
||||
function parseXML(text) {
|
||||
var list = String.prototype.split.call(text, /<([^!<>?](?:'[\S\s]*?'|"[\S\s]*?"|[^'"<>])*|!(?:--[\S\s]*?--|\[[^\[\]'"<>]+\[[\S\s]*?]]|DOCTYPE[^\[<>]*?\[[\S\s]*?]|(?:ENTITY[^"<>]*?"[\S\s]*?")?[\S\s]*?)|\?[\S\s]*?\?)>/);
|
||||
var length = list.length;
|
||||
|
||||
// root element
|
||||
var root = {f: []};
|
||||
var elem = root;
|
||||
|
||||
// dom tree stack
|
||||
var stack = [];
|
||||
|
||||
for (var i = 0; i < length;) {
|
||||
// text node
|
||||
var str = list[i++];
|
||||
if (str) appendText(str);
|
||||
|
||||
// child node
|
||||
var tag = list[i++];
|
||||
if (tag) parseNode(tag);
|
||||
}
|
||||
|
||||
return root;
|
||||
|
||||
function parseNode(tag) {
|
||||
var tagLength = tag.length;
|
||||
var firstChar = tag[0];
|
||||
if (firstChar === '/') {
|
||||
// close tag
|
||||
var closed = tag.replace(/^\/|[\s\/].*$/g, '').toLowerCase();
|
||||
while (stack.length) {
|
||||
var tagName = elem.n && elem.n.toLowerCase();
|
||||
elem = stack.pop();
|
||||
if (tagName === closed) break;
|
||||
}
|
||||
// } else if (firstChar === "?") {
|
||||
// // XML declaration
|
||||
// appendChild({n: "?", r: tag.substr(1, tagLength - 2)});
|
||||
} else if (firstChar === '!') {
|
||||
if (tag.substr(1, 7) === '[CDATA[' && tag.substr(-2) === ']]') {
|
||||
// CDATA section
|
||||
appendText(tag.substr(8, tagLength - 10));
|
||||
} else {
|
||||
// comment
|
||||
appendChild({n: '!', r: tag.substr(1)});
|
||||
}
|
||||
} else {
|
||||
var child = openTag(tag);
|
||||
appendChild(child);
|
||||
if (tag[tagLength - 1] === '/') {
|
||||
child.c = 1; // emptyTag
|
||||
} else {
|
||||
stack.push(elem); // openTag
|
||||
elem = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function appendChild(child) {
|
||||
elem.f.push(child);
|
||||
}
|
||||
|
||||
function appendText(str) {
|
||||
str = removeSpaces(str);
|
||||
if (str) appendChild(unescapeXML(str));
|
||||
}
|
||||
}
|
||||
|
||||
function openTag(tag) {
|
||||
var elem = {f: []};
|
||||
tag = tag.replace(/\s*\/?$/, '');
|
||||
var pos = tag.search(/[\s='"\/]/);
|
||||
if (pos < 0) {
|
||||
elem.n = tag;
|
||||
} else {
|
||||
elem.n = tag.substr(0, pos);
|
||||
elem.t = tag.substr(pos);
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
function parseAttribute(elem, reviver) {
|
||||
if (!elem.t) return;
|
||||
var list = elem.t.split(/([^\s='"]+(?:\s*=\s*(?:'[\S\s]*?'|"[\S\s]*?"|[^\s'"]*))?)/);
|
||||
var attributes = {};
|
||||
|
||||
list.forEach(function(str) {
|
||||
var val;
|
||||
str = removeSpaces(str);
|
||||
if (!str) return;
|
||||
|
||||
var pos = str.indexOf('=');
|
||||
if (pos < 0) {
|
||||
// bare attribute
|
||||
val = null;
|
||||
} else {
|
||||
// attribute key/value pair
|
||||
val = str.substr(pos + 1).replace(/^\s+/, '');
|
||||
str = str.substr(0, pos).replace(/\s+$/, '');
|
||||
|
||||
// quote: foo="FOO" bar='BAR'
|
||||
var firstChar = val[0];
|
||||
var lastChar = val[val.length - 1];
|
||||
if (firstChar === lastChar && (firstChar === '\'' || firstChar === '"')) {
|
||||
val = val.substr(1, val.length - 2);
|
||||
}
|
||||
|
||||
val = unescapeXML(val);
|
||||
}
|
||||
if (reviver) {
|
||||
val = reviver(str, val);
|
||||
}
|
||||
addAttribute(attributes, str, val);
|
||||
});
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
function removeSpaces(str) {
|
||||
return str && str.replace(/^\s+|\s+$/g, '');
|
||||
}
|
||||
|
||||
function unescapeXML(str) {
|
||||
return str.replace(/(&(?:lt|gt|amp|apos|quot|#(?:\d{1,6}|x[0-9a-fA-F]{1,5}));)/g, function(str) {
|
||||
if (str[1] === '#') {
|
||||
var code = (str[2] === 'x') ? parseInt(str.substr(3), 16) : parseInt(str.substr(2), 10);
|
||||
if (code > -1) return String.fromCharCode(code);
|
||||
}
|
||||
return UNESCAPE[str] || str;
|
||||
});
|
||||
}
|
||||
|
||||
function toObject(elem, reviver) {
|
||||
//
|
||||
// var raw = elem.r;
|
||||
// if (raw) return raw;
|
||||
|
||||
var attributes = parseAttribute(elem, reviver);
|
||||
var object;
|
||||
var childList = elem.f;
|
||||
var childLength = childList && childList.length || 0;
|
||||
|
||||
if (attributes || childLength > 1) {
|
||||
// merge attributes and child nodes
|
||||
if (typeof attributes === 'object') {
|
||||
object = attributes;
|
||||
object.line=198;
|
||||
} else {
|
||||
object = {};
|
||||
}
|
||||
object.tagName = elem.n;
|
||||
childList.forEach(function(child) {
|
||||
if ('string' === typeof child) {
|
||||
addObject(object, CHILD_NODE_KEY, child);
|
||||
} else {
|
||||
addObject(object, child.n, toObject(child, reviver));
|
||||
}
|
||||
});
|
||||
} else if (childLength) {
|
||||
// the node has single child node but no attribute
|
||||
var child = childList[0];
|
||||
if ('string' === typeof child) {
|
||||
object = {
|
||||
'tagName': elem.n,
|
||||
'value': child,
|
||||
'line': 215
|
||||
};
|
||||
} else {
|
||||
object = toObject(child, reviver);
|
||||
if (child.n) {
|
||||
object = {
|
||||
'tagName': elem.n,
|
||||
'value': {
|
||||
'children': [object]
|
||||
},
|
||||
'line': 227
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// the node has no attribute nor child node
|
||||
object = {
|
||||
'tagName': elem.n,
|
||||
'value': '',
|
||||
'line': 233
|
||||
};
|
||||
}
|
||||
|
||||
if (reviver) {
|
||||
object = reviver(elem.n || '', object);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
function addAttribute(object, key, val) {
|
||||
if ('undefined' === typeof val) return;
|
||||
object.attributes = object.attributes || {};
|
||||
object.attributes[key] = val;
|
||||
}
|
||||
function addObject(object, key, val) {
|
||||
if ('undefined' === typeof val) return;
|
||||
object.value = object.value || {};
|
||||
object.value.children = object.value.children || [];
|
||||
if (typeof val === 'object' && val.tagName) {
|
||||
object.value.children.push(val);
|
||||
} else {
|
||||
object.value.children.push({
|
||||
'tagName': key,
|
||||
'value': val,
|
||||
'line': 258
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return function(text, reviver) {
|
||||
text = text.replace(/<\?xml[^>]+>/i, '');
|
||||
var xmlTree = parseXML(text);
|
||||
var result = toObject(xmlTree, reviver);
|
||||
result.tagName = 'document';
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
|
||||
//---------------------------------------
|
||||
// KmlParserClass
|
||||
//---------------------------------------
|
||||
function KmlParserClass() {
|
||||
var _parser = this;
|
||||
_parser.styleHolder = {};
|
||||
_parser.schemaHolder = {};
|
||||
|
||||
_parser.tagSwitchTables = {
|
||||
'styleurl': _parser._styleurl,
|
||||
'stylemap': _parser._style,
|
||||
'style': _parser._style,
|
||||
'schema': _parser._schema,
|
||||
'coordinates': _parser._coordinates
|
||||
};
|
||||
}
|
||||
|
||||
KmlParserClass.prototype.parseXml = function(rootElement) {
|
||||
var _parser = this,
|
||||
tagName = rootElement.tagName.toLowerCase();
|
||||
|
||||
|
||||
|
||||
var _proc = _parser.tagSwitchTables[tagName] || _parser._default;
|
||||
//console.log("--->tagName = " + tagName, tagName in _parser.tagSwitchTables ? tagName : '_default');
|
||||
var result = _proc.call(_parser, rootElement);
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
result.tagName = tagName;
|
||||
if (rootElement.attributes) {
|
||||
var attrNames = Object.keys(rootElement.attributes);
|
||||
attrNames.forEach(function(attrName) {
|
||||
result[attrName] = rootElement.attributes[attrName];
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
KmlParserClass.prototype._styleurl = function(rootElement) {
|
||||
return {
|
||||
'styleId': rootElement.value
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
KmlParserClass.prototype._style = function(rootElement) {
|
||||
var _parser = this;
|
||||
|
||||
// Generate a style id for the tag
|
||||
var styleId = rootElement.attributes ? rootElement.attributes.id : null;
|
||||
if (!styleId) {
|
||||
styleId = '__' + Math.floor(Date.now() * Math.random()) + '__';
|
||||
}
|
||||
var result = {
|
||||
'styleId': styleId
|
||||
};
|
||||
|
||||
// Store style information into the styleHolder
|
||||
var styles = {};
|
||||
var children = [];
|
||||
if (rootElement.value.children) {
|
||||
rootElement.value.children.forEach(function(childNode) {
|
||||
var node = _parser.parseXml(childNode);
|
||||
if (node.value) {
|
||||
styles[node.tagName] = node.value;
|
||||
} else {
|
||||
children.push(node);
|
||||
}
|
||||
});
|
||||
if (children.length > 0) {
|
||||
styles.children = children;
|
||||
}
|
||||
}
|
||||
_parser.styleHolder[styleId] = styles;
|
||||
return result;
|
||||
};
|
||||
|
||||
KmlParserClass.prototype._schema = function(rootElement) {
|
||||
var _parser = this;
|
||||
var result = {};
|
||||
|
||||
// Generate a schema id for the tag
|
||||
var schemaId = rootElement.attributes ? rootElement.attributes.id : null;
|
||||
if (!schemaId) {
|
||||
schemaId = '__' + Math.floor(Date.now() * Math.random()) + '__';
|
||||
}
|
||||
|
||||
// Store schema information into the schemaHolder.
|
||||
var schema = {};
|
||||
schema.name = rootElement.attributes ? rootElement.attributes.id : '__' + Math.floor(Date.now() * Math.random()) + '__';
|
||||
if (rootElement.value.children) {
|
||||
var children = [];
|
||||
rootElement.value.children.forEach(function(childNode) {
|
||||
var node = _parser.parseXml(childNode);
|
||||
if (node) {
|
||||
children.push(node);
|
||||
}
|
||||
});
|
||||
if (children.length > 0) {
|
||||
schema.children = children;
|
||||
}
|
||||
}
|
||||
_parser.schemaHolder[schemaId] = schema;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
KmlParserClass.prototype._coordinates = function(rootElement) {
|
||||
var result = {};
|
||||
var latLngList = [];
|
||||
|
||||
var txt = rootElement.value;
|
||||
txt = txt.replace(/\s+/g, '\n');
|
||||
txt = txt.replace(/\n+/g, '\n');
|
||||
var lines = txt.split(/\n/);
|
||||
|
||||
lines.forEach(function(line) {
|
||||
line = line.replace(/[^0-9,.\\-]/g, '');
|
||||
if (line !== '') {
|
||||
var tmpArry = line.split(',');
|
||||
latLngList.push({
|
||||
'lat': parseFloat(tmpArry[1]),
|
||||
'lng': parseFloat(tmpArry[0])
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
result.coordinates = latLngList;
|
||||
return result;
|
||||
};
|
||||
|
||||
KmlParserClass.prototype._default = function(rootElement) {
|
||||
var _parser = this,
|
||||
result = {};
|
||||
if (!rootElement.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rootElement.value.children) {
|
||||
var children = [];
|
||||
rootElement.value.children.forEach(function(childNode) {
|
||||
|
||||
var node = _parser.parseXml.call(_parser, childNode);
|
||||
if (node) {
|
||||
if ('styleId' in node) {
|
||||
result.styleIDs = result.styleIDs || [];
|
||||
result.styleIDs.push(node.styleId);
|
||||
} else if (node.tagName !== 'schema') {
|
||||
children.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
result.children = children;
|
||||
} else {
|
||||
var value = rootElement.value;
|
||||
if (/^-?[0-9]+$/.test(value)) {
|
||||
result.value = parseInt(value, 10);
|
||||
} else if (/^-?[0-9\.]+$/.test(value)) {
|
||||
result.value = parseFloat(value, 10);
|
||||
} else {
|
||||
result.value = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
self.onmessage = function(evt) {
|
||||
var params = evt.data;
|
||||
//------------------------------------------
|
||||
// Load & parse kml file in WebWorker
|
||||
//------------------------------------------
|
||||
(new Promise(function(resolve, reject) {
|
||||
//-----------------
|
||||
// Read XML file
|
||||
//-----------------
|
||||
var xhr = createCORSRequest('GET', params.url, true);
|
||||
if (xhr) {
|
||||
xhr.onreadystatechange = function() {
|
||||
try {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
resolve(xhr.responseText);
|
||||
} else {
|
||||
reject(xhr);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
reject(e.description);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
}))
|
||||
.then(function(xmlTxt) {
|
||||
//-----------------
|
||||
// Parse it
|
||||
//-----------------
|
||||
var doc = fromXML(xmlTxt);
|
||||
var parser = new KmlParserClass();
|
||||
var root = parser.parseXml(doc);
|
||||
|
||||
var result = {
|
||||
'schemas': parser.schemaHolder,
|
||||
'styles': parser.styleHolder,
|
||||
'root': root
|
||||
};
|
||||
postMessage(result);
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
|
||||
var LOCATION_ERROR = {
|
||||
'1': 'service_denied',
|
||||
'2': 'not_available',
|
||||
'3': 'timeout'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
'hasPermission': function(onSuccess, onError) {
|
||||
if (navigator.permissions) {
|
||||
navigator.permissions.query({'name': 'geolocation'})
|
||||
.then(function(permission) {
|
||||
onSuccess(permission.state === 'granted' ? 1 : 0);
|
||||
})
|
||||
.catch(onError);
|
||||
} else {
|
||||
onError('Browser does not support this feature.');
|
||||
}
|
||||
},
|
||||
'getMyLocation': function(onSuccess, onError) {
|
||||
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(function(position) {
|
||||
onSuccess({
|
||||
'latLng': {
|
||||
'lat': position.coords.latitude,
|
||||
'lng': position.coords.longitude
|
||||
},
|
||||
'elapsedRealtimeNanos': 0,
|
||||
'time': position.timestamp,
|
||||
'accuracy': position.coords.accuracy,
|
||||
'altitude': position.coords.altitude,
|
||||
'speed': position.coords.speed,
|
||||
'bearing': position.coords.heading,
|
||||
'provider': 'geolocationapi',
|
||||
'hashCode': 'dummy',
|
||||
'status': true
|
||||
});
|
||||
}, function(error) {
|
||||
onError({
|
||||
'status': false,
|
||||
'error_code': LOCATION_ERROR[error.code],
|
||||
'error_message': error.message
|
||||
});
|
||||
}, {
|
||||
'enableHighAccuracy': true
|
||||
});
|
||||
} else {
|
||||
onError({
|
||||
'status': false,
|
||||
'error_code': 'not_available',
|
||||
'error_message': 'Since this device does not have any location provider, this app can not detect your location.'
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
require('cordova/exec/proxy').add('PluginLocationService', module.exports);
|
||||
@@ -0,0 +1,726 @@
|
||||
|
||||
|
||||
|
||||
var utils = require('cordova/utils'),
|
||||
event = require('cordova-plugin-googlemaps.event'),
|
||||
BaseClass = require('cordova-plugin-googlemaps.BaseClass'),
|
||||
LatLng = require('cordova-plugin-googlemaps.LatLng'),
|
||||
MapTypeId = require('cordova-plugin-googlemaps.MapTypeId');
|
||||
|
||||
var MAP_TYPES = {};
|
||||
MAP_TYPES[MapTypeId.NORMAL] = 'roadmap';
|
||||
MAP_TYPES[MapTypeId.ROADMAP] = 'roadmap';
|
||||
MAP_TYPES[MapTypeId.SATELLITE] = 'satellite';
|
||||
MAP_TYPES[MapTypeId.HYBRID] = 'hybrid';
|
||||
MAP_TYPES[MapTypeId.TERRAIN] = 'terrain';
|
||||
MAP_TYPES[MapTypeId.NONE] = 'none';
|
||||
|
||||
var LOCATION_ERROR = {};
|
||||
LOCATION_ERROR[1] = 'service_denied';
|
||||
LOCATION_ERROR[2] = 'not_available';
|
||||
LOCATION_ERROR[3] = 'cannot_detect';
|
||||
var LOCATION_ERROR_MSG = {};
|
||||
LOCATION_ERROR_MSG[1] = 'Location service is rejected by user.';
|
||||
LOCATION_ERROR_MSG[2] = 'Since this device does not have any location provider, this app can not detect your location.';
|
||||
LOCATION_ERROR_MSG[3] = 'Can not detect your location. Try again.';
|
||||
|
||||
function displayGrayMap(container) {
|
||||
var gmErrorContent = document.querySelector('.gm-err-container');
|
||||
var gmnoprint = document.querySelector('.gmnoprint');
|
||||
if (!gmErrorContent && !gmnoprint) {
|
||||
container.innerHTML = [
|
||||
'<div style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color:rgb(229, 227, 223)">',
|
||||
'<div style="text-align: center; position: absolute; left: 0; top: 0; bottom: 0; right: 0; width: 80%; height: 100px; margin: auto; color: #616161">',
|
||||
'<img src="https://maps.gstatic.com/mapfiles/api-3/images/icon_error.png"><br>',
|
||||
'<h3>Can not display map.<br>Check the developer console.</h3>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
function PluginMap(mapId, options) {
|
||||
var self = this;
|
||||
BaseClass.apply(this);
|
||||
var mapDiv = document.querySelector('[__pluginMapId=\'' + mapId + '\']');
|
||||
mapDiv.style.backgroundColor = 'rgb(229, 227, 223)';
|
||||
|
||||
var container = document.createElement('div');
|
||||
container.style.userSelect='none';
|
||||
container.style['-webkit-user-select']='none';
|
||||
container.style['-moz-user-select']='none';
|
||||
container.style['-ms-user-select']='none';
|
||||
mapDiv.style.position = 'relative';
|
||||
container.style.position = 'absolute';
|
||||
container.style.top = 0;
|
||||
container.style.bottom = 0;
|
||||
container.style.right = 0;
|
||||
container.style.left = 0;
|
||||
mapDiv.insertBefore(container, mapDiv.firstElementChild);
|
||||
|
||||
self.set('isGoogleReady', false);
|
||||
self.set('container', container);
|
||||
self.PLUGINS = {};
|
||||
|
||||
Object.defineProperty(self, '__pgmId', {
|
||||
value: mapId,
|
||||
writable: false
|
||||
});
|
||||
Object.defineProperty(self, 'objects', {
|
||||
value: {},
|
||||
writable: false
|
||||
});
|
||||
Object.defineProperty(self, 'activeMarker', {
|
||||
value: null,
|
||||
writable: true
|
||||
});
|
||||
self.set('clickable', true);
|
||||
|
||||
|
||||
self.one('googleready', function() {
|
||||
self.set('isGoogleReady', true);
|
||||
|
||||
var mapTypeReg = new google.maps.MapTypeRegistry();
|
||||
mapTypeReg.set('none', new google.maps.ImageMapType({
|
||||
'getTileUrl': function() { return null; },
|
||||
'name': 'none_type',
|
||||
'tileSize': new google.maps.Size(256, 256),
|
||||
'minZoom': 0,
|
||||
'maxZoom': 25
|
||||
}));
|
||||
|
||||
var mapInitOptions = {
|
||||
mapTypes: mapTypeReg,
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP,
|
||||
noClear: true,
|
||||
zoom: 2,
|
||||
minZoom: 2,
|
||||
disableDefaultUI: true,
|
||||
zoomControl: true,
|
||||
center: {lat: 0, lng: 0}
|
||||
};
|
||||
|
||||
if (options) {
|
||||
if (options.mapType) {
|
||||
mapInitOptions.mapTypeId = MAP_TYPES[options.mapType];
|
||||
}
|
||||
if (options.styles) {
|
||||
mapInitOptions.styles = JSON.parse(options.styles);
|
||||
}
|
||||
|
||||
if (options.controls) {
|
||||
if (options.controls.zoom !== undefined) {
|
||||
mapInitOptions.zoomControl = options.controls.zoom == true;
|
||||
}
|
||||
}
|
||||
if (options.preferences) {
|
||||
if (options.preferences.zoom) {
|
||||
mapInitOptions.minZoom = options.preferences.zoom.minZoom;
|
||||
if (options.preferences.zoom.maxZoom) {
|
||||
mapInitOptions.maxZoom = options.preferences.zoom.maxZoom;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var map = new google.maps.Map(container, mapInitOptions);
|
||||
map.mapTypes = mapTypeReg;
|
||||
self.set('map', map);
|
||||
|
||||
var boundsLimit = null;
|
||||
if (options.preferences && options.preferences.gestureBounds &&
|
||||
options.preferences.gestureBounds.length > 0) {
|
||||
boundsLimit = new google.maps.LatLngBounds();
|
||||
options.preferences.gestureBounds.forEach(function(pos) {
|
||||
boundsLimit.extend(pos);
|
||||
});
|
||||
}
|
||||
map.set('boundsLimit', boundsLimit);
|
||||
|
||||
var timeoutError = setTimeout(function() {
|
||||
self.trigger('load_error');
|
||||
displayGrayMap(mapDiv);
|
||||
}, 3000);
|
||||
|
||||
map.addListener('bounds_changed', function() {
|
||||
var boundsLimit = map.get('boundsLimit');
|
||||
if (!boundsLimit) {
|
||||
return;
|
||||
}
|
||||
var visibleBounds = map.getBounds();
|
||||
if (boundsLimit.intersects(visibleBounds) ||
|
||||
visibleBounds.contains(boundsLimit.getNorthEast()) && visibleBounds.contains(boundsLimit.getSouthWest()) ||
|
||||
boundsLimit.contains(visibleBounds.getNorthEast()) && boundsLimit.contains(visibleBounds.getSouthWest())) {
|
||||
return;
|
||||
}
|
||||
var center = map.getCenter();
|
||||
var dummyLat = center.lat(),
|
||||
dummyLng = center.lng();
|
||||
var ne = boundsLimit.getNorthEast(),
|
||||
sw = boundsLimit.getSouthWest();
|
||||
if (dummyLat < sw.lat() ) {
|
||||
dummyLat = sw.lat();
|
||||
} else if (dummyLat > ne.lat()) {
|
||||
dummyLat = ne.lat();
|
||||
}
|
||||
if (dummyLng < 0) {
|
||||
// the Western Hemisphere
|
||||
if (dummyLng > ne.lng()) {
|
||||
dummyLng = ne.lng();
|
||||
} else if (dummyLng < sw.lng()) {
|
||||
dummyLng = sw.lng();
|
||||
}
|
||||
} else {
|
||||
// the Eastern Hemisphere
|
||||
if (dummyLng > ne.lng()) {
|
||||
dummyLng = ne.lng();
|
||||
} else if (dummyLng < sw.lng()) {
|
||||
dummyLng = sw.lng();
|
||||
}
|
||||
}
|
||||
var dummyLatLng = new google.maps.LatLng(dummyLat, dummyLng);
|
||||
map.panTo(dummyLatLng);
|
||||
});
|
||||
|
||||
google.maps.event.addListenerOnce(map, 'projection_changed', function() {
|
||||
clearTimeout(timeoutError);
|
||||
|
||||
self.trigger(event.MAP_READY);
|
||||
map.addListener('idle', self._onCameraEvent.bind(self, 'camera_move_end'));
|
||||
//map.addListener("bounce_changed", self._onCameraEvent.bind(self, 'camera_move'));
|
||||
map.addListener('drag', self._onCameraEvent.bind(self, event.CAMERA_MOVE));
|
||||
map.addListener('dragend', self._onCameraEvent.bind(self, event.CAMERA_MOVE_END));
|
||||
map.addListener('dragstart', self._onCameraEvent.bind(self, event.CAMERA_MOVE_START));
|
||||
|
||||
map.addListener('click', function(evt) {
|
||||
self._onMapEvent.call(self, event.MAP_CLICK, evt);
|
||||
});
|
||||
map.addListener('mousedown', function() {
|
||||
map.set('mousedown_time', Date.now());
|
||||
});
|
||||
map.addListener('mouseup', function(evt) {
|
||||
if (Date.now() - (map.get('mousedown_time') || Date.now()) > 500) {
|
||||
self._onMapEvent.call(self, event.MAP_LONG_CLICK, evt);
|
||||
}
|
||||
});
|
||||
map.addListener('drag', function(evt) {
|
||||
self._onMapEvent.call(self, event.MAP_DRAG, evt);
|
||||
});
|
||||
map.addListener('dragend', function(evt) {
|
||||
self._onMapEvent.call(self, event.MAP_DRAG_END, evt);
|
||||
});
|
||||
map.addListener('dragstart', function(evt) {
|
||||
map.set('mousedown_time', undefined);
|
||||
self._onMapEvent.call(self, event.MAP_DRAG_START, evt);
|
||||
});
|
||||
});
|
||||
|
||||
if (options) {
|
||||
if (options.camera) {
|
||||
if (options.camera.target) {
|
||||
|
||||
if (Array.isArray(options.camera.target)) {
|
||||
var bounds = new google.maps.LatLngBounds();
|
||||
options.camera.target.forEach(function(pos) {
|
||||
bounds.extend(pos);
|
||||
});
|
||||
map.fitBounds(bounds, 5);
|
||||
} else {
|
||||
map.setCenter(options.camera.target);
|
||||
}
|
||||
if (typeof options.camera.tilt === 'number') {
|
||||
map.setTilt(options.camera.tilt);
|
||||
}
|
||||
if (typeof options.camera.bearing === 'number') {
|
||||
map.setHeading(options.camera.bearing);
|
||||
}
|
||||
if (typeof options.camera.zoom === 'number') {
|
||||
map.setZoom(options.camera.zoom);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map.setCenter({lat: 0, lng: 0});
|
||||
}
|
||||
} else {
|
||||
map.setCenter({lat: 0, lng: 0});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
utils.extend(PluginMap, BaseClass);
|
||||
|
||||
PluginMap.prototype.setOptions = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map'),
|
||||
options = args[0];
|
||||
|
||||
var mapInitOptions = {};
|
||||
|
||||
if (options) {
|
||||
if (options.mapType) {
|
||||
mapInitOptions.mapTypeId = MAP_TYPES[options.mapType];
|
||||
}
|
||||
if (options.styles) {
|
||||
mapInitOptions.styles = JSON.parse(options.styles);
|
||||
}
|
||||
|
||||
if (options.controls) {
|
||||
if (options.controls.zoom !== undefined) {
|
||||
mapInitOptions.zoomControl = options.controls.zoom == true;
|
||||
}
|
||||
}
|
||||
if (options.preferences) {
|
||||
if (options.preferences.zoom) {
|
||||
mapInitOptions.minZoom = Math.max(options.preferences.zoom || 2, 2);
|
||||
if (options.preferences.zoom.maxZoom) {
|
||||
mapInitOptions.maxZoom = options.preferences.zoom.maxZoom;
|
||||
}
|
||||
}
|
||||
|
||||
if ('gestureBounds' in options.preferences) {
|
||||
var boundsLimit = null;
|
||||
if (options.preferences.gestureBounds && options.preferences.gestureBounds.length > 0) {
|
||||
boundsLimit = new google.maps.LatLngBounds();
|
||||
options.preferences.gestureBounds.forEach(function(pos) {
|
||||
boundsLimit.extend(pos);
|
||||
});
|
||||
}
|
||||
map.set('boundsLimit', boundsLimit);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
map.setOptions(mapInitOptions);
|
||||
|
||||
if (options) {
|
||||
if (options.camera) {
|
||||
if (options.camera.target) {
|
||||
|
||||
if (Array.isArray(options.camera.target)) {
|
||||
var bounds = new google.maps.LatLngBounds();
|
||||
options.camera.target.forEach(function(pos) {
|
||||
bounds.extend(pos);
|
||||
});
|
||||
map.fitBounds(bounds, 5);
|
||||
} else {
|
||||
map.setCenter(options.camera.target);
|
||||
}
|
||||
if (typeof options.camera.tilt === 'number') {
|
||||
map.setTilt(options.camera.tilt);
|
||||
}
|
||||
if (typeof options.camera.bearing === 'number') {
|
||||
map.setHeading(options.camera.bearing);
|
||||
}
|
||||
if (typeof options.camera.zoom === 'number') {
|
||||
map.setZoom(options.camera.zoom);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map.setCenter({lat: 0, lng: 0});
|
||||
}
|
||||
} else {
|
||||
map.setCenter({lat: 0, lng: 0});
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.setActiveMarkerId = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
markerId = args[0];
|
||||
self.activeMarker = self.objects[markerId];
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.clear = function(onSuccess) {
|
||||
this.activeMarker = null;
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.getFocusedBuilding = function(onSuccess) {
|
||||
// stub
|
||||
onSuccess(-1);
|
||||
};
|
||||
|
||||
PluginMap.prototype.setDiv = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
map = self.get('map'),
|
||||
container = self.get('container');
|
||||
|
||||
if (args.length === 0) {
|
||||
if (container && container.parentNode) {
|
||||
container.parentNode.removeAttribute('__pluginMapId');
|
||||
container.parentNode.removeChild(container);
|
||||
}
|
||||
} else {
|
||||
var domId = args[0];
|
||||
var mapDiv = document.querySelector('[__pluginDomId=\'' + domId + '\']');
|
||||
mapDiv.style.position = 'relative';
|
||||
mapDiv.insertBefore(container, mapDiv.firstElementChild);
|
||||
mapDiv.setAttribute('__pluginMapId', self.__pgmId);
|
||||
}
|
||||
|
||||
google.maps.event.trigger(map, 'resize');
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.resizeMap = function(onSuccess) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
|
||||
google.maps.event.trigger(map, 'resize');
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.panBy = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
map.panBy.apply(map, args);
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.setCameraBearing = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var heading = args[0];
|
||||
|
||||
map.setHeading(heading);
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.setCameraZoom = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var zoom = args[0];
|
||||
|
||||
map.setZoom(zoom);
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.setCameraTarget = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var lat = args[0],
|
||||
lng = args[1];
|
||||
|
||||
map.setCenter(new google.maps.LatLng(lat, lng));
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.setCameraTilt = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var tilt = args[0];
|
||||
|
||||
map.setTilt(tilt);
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.setMyLocationEnabled = function(onSuccess) {
|
||||
// stub
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.stopAnimation = function(onSuccess) {
|
||||
// stub
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.animateCamera = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
|
||||
var options = args[0];
|
||||
var padding = 'padding' in options ? options.padding : 5;
|
||||
var bounds;
|
||||
if (Array.isArray(options.target)) {
|
||||
bounds = new google.maps.LatLngBounds();
|
||||
options.target.forEach(function(pos) {
|
||||
bounds.extend(pos);
|
||||
});
|
||||
map.fitBounds(bounds, padding);
|
||||
} else {
|
||||
var zoomFlag = typeof options.zoom === 'number';
|
||||
var targetFlag = !!options.target;
|
||||
|
||||
if (zoomFlag && targetFlag) {
|
||||
var projection = map.getProjection();
|
||||
var centerLatLng = new google.maps.LatLng(options.target.lat, options.target.lng, true);
|
||||
var centerPoint = projection.fromLatLngToPoint(centerLatLng);
|
||||
|
||||
var scale = Math.pow(2, options.zoom);
|
||||
|
||||
var div = map.getDiv();
|
||||
var harfWidth = div.offsetWidth / 2;
|
||||
var harfHeight = div.offsetHeight / 2;
|
||||
var swPoint = new google.maps.Point((centerPoint.x * scale - harfWidth) / scale, (centerPoint.y * scale + harfHeight) / scale );
|
||||
var nePoint = new google.maps.Point((centerPoint.x * scale + harfWidth) / scale, (centerPoint.y * scale - harfHeight) / scale);
|
||||
var sw = projection.fromPointToLatLng(swPoint);
|
||||
var ne = projection.fromPointToLatLng(nePoint);
|
||||
bounds = new google.maps.LatLngBounds(sw, ne);
|
||||
map.fitBounds(bounds, padding);
|
||||
|
||||
} else if (zoomFlag) {
|
||||
map.setZoom(options.zoom);
|
||||
} else if (targetFlag) {
|
||||
map.panTo(options.target);
|
||||
}
|
||||
}
|
||||
if (typeof options.tilt === 'number') {
|
||||
map.setTilt(options.tilt);
|
||||
}
|
||||
if (typeof options.bearing === 'number') {
|
||||
map.setHeading(options.bearing);
|
||||
}
|
||||
onSuccess();
|
||||
|
||||
};
|
||||
|
||||
PluginMap.prototype.moveCamera = function(onSuccess, onError, args) {
|
||||
this.animateCamera.call(this, onSuccess, onError, args);
|
||||
};
|
||||
|
||||
PluginMap.prototype.setMapTypeId = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var mapTypeId = args[0];
|
||||
map.setMapTypeId(MAP_TYPES[mapTypeId]);
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.setClickable = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var clickable = args[0];
|
||||
self.set('clickable', clickable);
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.setVisible = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var visibility = args[0];
|
||||
var mapDiv = map.getDiv();
|
||||
if (mapDiv) {
|
||||
mapDiv.style.visibility = visibility === true ? 'visible' : 'hidden';
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.setPadding = function(onSuccess) {
|
||||
// stub
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.setAllGesturesEnabled = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var enabled = args[0];
|
||||
map.setOptions({
|
||||
gestureHandling: enabled === true ? 'auto': 'none'
|
||||
});
|
||||
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.setCompassEnabled = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var enabled = args[0];
|
||||
map.setOptions({
|
||||
rotateControl: enabled === true
|
||||
});
|
||||
var mapTypeId = map.getMapTypeId();
|
||||
if (mapTypeId !== google.maps.MapTypeId.SATELLITE &&
|
||||
mapTypeId !== google.maps.MapTypeId.HYBRID) {
|
||||
console.warn('map.setCompassEnabled() works only HYBRID or SATELLITE for this platform.');
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMap.prototype.setTrafficEnabled = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var enabled = args[0];
|
||||
|
||||
var trafficLayer = map.get('trafficLayer');
|
||||
if (!trafficLayer) {
|
||||
trafficLayer = new google.maps.TrafficLayer();
|
||||
map.set('trafficLayer', trafficLayer);
|
||||
}
|
||||
if (enabled) {
|
||||
trafficLayer.setMap(map);
|
||||
} else {
|
||||
trafficLayer.setMap(null);
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
PluginMap.prototype.fromLatLngToPoint = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var lat = args[0],
|
||||
lng = args[1];
|
||||
|
||||
var projection = map.getProjection(),
|
||||
bounds = map.getBounds(),
|
||||
ne = bounds.getNorthEast(),
|
||||
sw = bounds.getSouthWest(),
|
||||
zoom = map.getZoom(),
|
||||
north = ne.lat(),
|
||||
west = sw.lng();
|
||||
|
||||
var nowrapFlag = !bounds.contains(new google.maps.LatLng(north, 179));
|
||||
|
||||
var scale = Math.pow(2, zoom),
|
||||
topLeft = projection.fromLatLngToPoint(new google.maps.LatLng(north, west + 360, nowrapFlag)),
|
||||
worldPoint = projection.fromLatLngToPoint(new google.maps.LatLng(lat, lng + 360, true));
|
||||
onSuccess([(worldPoint.x - topLeft.x) * scale, (worldPoint.y - topLeft.y) * scale]);
|
||||
};
|
||||
|
||||
PluginMap.prototype.fromPointToLatLng = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var map = self.get('map');
|
||||
var x = args[0],
|
||||
y = args[1];
|
||||
|
||||
var projection = map.getProjection(),
|
||||
bounds = map.getBounds(),
|
||||
ne = bounds.getNorthEast(),
|
||||
sw = bounds.getSouthWest(),
|
||||
zoom = map.getZoom();
|
||||
|
||||
var topRight = projection.fromLatLngToPoint(ne);
|
||||
var bottomLeft = projection.fromLatLngToPoint(sw);
|
||||
var scale = Math.pow(2, zoom);
|
||||
var worldPoint = new google.maps.Point(x / scale + bottomLeft.x, y / scale + topRight.y);
|
||||
var latLng = map.getProjection().fromPointToLatLng(worldPoint);
|
||||
onSuccess([latLng.lat(), latLng.lng()]);
|
||||
|
||||
};
|
||||
|
||||
PluginMap.prototype.setIndoorEnabled = function(onSuccess) {
|
||||
// stub
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype.toDataURL = function(onSuccess) {
|
||||
// stub
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMap.prototype._syncInfoWndPosition = function() {
|
||||
var self = this;
|
||||
|
||||
if (!self.activeMarker) {
|
||||
return;
|
||||
}
|
||||
|
||||
var latLng = self.activeMarker.getPosition();
|
||||
self.fromLatLngToPoint(function(point) {
|
||||
|
||||
plugin.google.maps[self.__pgmId]({
|
||||
'evtName': 'syncPosition',
|
||||
'callback': '_onSyncInfoWndPosition',
|
||||
'args': [{'x': point[0], 'y': point[1]}]
|
||||
});
|
||||
|
||||
}, null, [latLng.lat(), latLng.lng()]);
|
||||
|
||||
};
|
||||
|
||||
PluginMap.prototype._onMapEvent = function(evtName, evt) {
|
||||
var self = this;
|
||||
|
||||
if (self.get('clickable') === false &&
|
||||
(evtName === event.MAP_CLICK || evtName === event.MAP_LONG_CLICK)) {
|
||||
evt.stop();
|
||||
return;
|
||||
}
|
||||
if (self.__pgmId in plugin.google.maps) {
|
||||
if (evt) {
|
||||
if (evtName === event.MAP_CLICK) {
|
||||
if (evt.placeId) {
|
||||
evt.stop();
|
||||
plugin.google.maps[self.__pgmId]({
|
||||
'evtName': event.POI_CLICK,
|
||||
'callback': '_onMapEvent',
|
||||
'args': [evt.placeId, undefined, new LatLng(evt.latLng.lat(), evt.latLng.lng())]
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
plugin.google.maps[self.__pgmId]({
|
||||
'evtName': evtName,
|
||||
'callback': '_onMapEvent',
|
||||
'args': [new LatLng(evt.latLng.lat(), evt.latLng.lng())]
|
||||
});
|
||||
} else {
|
||||
plugin.google.maps[self.__pgmId]({
|
||||
'evtName': evtName,
|
||||
'callback': '_onMapEvent',
|
||||
'args': []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
PluginMap.prototype._onCameraEvent = function(evtName) {
|
||||
var self = this,
|
||||
map = self.get('map'),
|
||||
center = map.getCenter(),
|
||||
bounds = map.getBounds(),
|
||||
ne = bounds.getNorthEast(),
|
||||
sw = bounds.getSouthWest();
|
||||
|
||||
self._syncInfoWndPosition();
|
||||
|
||||
var cameraInfo = {
|
||||
'target': {'lat': center.lat(), 'lng': center.lng()},
|
||||
'zoom': map.getZoom(),
|
||||
'tilt': map.getTilt() || 0,
|
||||
'bearing': map.getHeading() || 0,
|
||||
'northeast': {'lat': ne.lat(), 'lng': ne.lng()},
|
||||
'southwest': {'lat': sw.lat(), 'lng': sw.lng()},
|
||||
'farLeft': {'lat': ne.lat(), 'lng': sw.lng()},
|
||||
'farRight': {'lat': ne.lat(), 'lng': ne.lng()}, // = northEast
|
||||
'nearLeft': {'lat': sw.lat(), 'lng': sw.lng()}, // = southWest
|
||||
'nearRight': {'lat': sw.lat(), 'lng': ne.lng()}
|
||||
};
|
||||
if (self.__pgmId in plugin.google.maps) {
|
||||
plugin.google.maps[self.__pgmId]({
|
||||
'evtName': evtName,
|
||||
'callback': '_onCameraEvent',
|
||||
'args': [cameraInfo]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
PluginMap.prototype.loadPlugin = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var className = args[0];
|
||||
|
||||
var plugin;
|
||||
if (className in self.PLUGINS) {
|
||||
plugin = self.PLUGINS[className];
|
||||
} else {
|
||||
var OverlayClass = require('cordova-plugin-googlemaps.Plugin' + className);
|
||||
plugin = new OverlayClass(this);
|
||||
self.PLUGINS[className] = plugin;
|
||||
|
||||
// Since Cordova involes methods as Window,
|
||||
// the `this` keyword of involved method is Window, not overlay itself.
|
||||
// In order to keep indicate the `this` keyword as overlay itself,
|
||||
// wrap the method.
|
||||
var dummyObj = {};
|
||||
for (var key in OverlayClass.prototype) {
|
||||
if (typeof OverlayClass.prototype[key] === 'function') {
|
||||
dummyObj[key] = plugin[key].bind(plugin);
|
||||
} else {
|
||||
dummyObj[key] = plugin[key];
|
||||
}
|
||||
}
|
||||
require('cordova/exec/proxy').add(self.__pgmId + '-' + className.toLowerCase(), dummyObj);
|
||||
}
|
||||
|
||||
plugin._create.call(plugin, onSuccess, onError, args);
|
||||
};
|
||||
|
||||
module.exports = PluginMap;
|
||||
@@ -0,0 +1,489 @@
|
||||
|
||||
var utils = require('cordova/utils'),
|
||||
event = require('cordova-plugin-googlemaps.event'),
|
||||
BaseClass = require('cordova-plugin-googlemaps.BaseClass'),
|
||||
LatLng = require('cordova-plugin-googlemaps.LatLng');
|
||||
|
||||
function PluginMarker(pluginMap) {
|
||||
var self = this;
|
||||
BaseClass.apply(self);
|
||||
Object.defineProperty(self, 'pluginMap', {
|
||||
value: pluginMap,
|
||||
writable: false
|
||||
});
|
||||
Object.defineProperty(self, 'infoWnd', {
|
||||
value: null,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
|
||||
utils.extend(PluginMarker, BaseClass);
|
||||
|
||||
PluginMarker.prototype._create = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
markerId = 'marker_' + args[2],
|
||||
pluginOptions = args[1];
|
||||
self.__create.call(self, markerId, pluginOptions, function(marker, properties) {
|
||||
onSuccess(properties);
|
||||
}, onError);
|
||||
};
|
||||
|
||||
/*eslint-disable no-unused-vars*/
|
||||
PluginMarker.prototype.__create = function(markerId, pluginOptions, onSuccess, onError) {
|
||||
/*eslint-enable no-unused-vars*/
|
||||
var self = this,
|
||||
map = self.pluginMap.get('map');
|
||||
var markerOpts = {
|
||||
'overlayId': markerId,
|
||||
'position': pluginOptions.position,
|
||||
'disableAutoPan': pluginOptions.disableAutoPan,
|
||||
'draggable': pluginOptions.draggable,
|
||||
'visible': pluginOptions.visible
|
||||
};
|
||||
|
||||
var iconSize = null;
|
||||
if (pluginOptions.animation) {
|
||||
markerOpts.animation = google.maps.Animation[pluginOptions.animation.toUpperCase()];
|
||||
}
|
||||
if (pluginOptions.icon) {
|
||||
var icon = pluginOptions.icon;
|
||||
markerOpts.icon = {};
|
||||
if (Array.isArray(pluginOptions.icon) && pluginOptions.icon.length === 4) {
|
||||
pluginOptions.icon = {
|
||||
'url': pluginOptions.icon
|
||||
};
|
||||
}
|
||||
if (typeof pluginOptions.icon === 'string') {
|
||||
// Specifies path or url to icon image
|
||||
markerOpts.icon.url = pluginOptions.icon;
|
||||
} else if (typeof pluginOptions.icon === 'object') {
|
||||
if (Array.isArray(pluginOptions.icon.url)) {
|
||||
// Specifies color name or rule
|
||||
markerOpts.icon = {
|
||||
'path': 'm12 0c-4.4183 2.3685e-15 -8 3.5817-8 8 0 1.421 0.3816 2.75 1.0312 3.906 0.1079 0.192 0.221 0.381 0.3438 0.563l6.625 11.531 6.625-11.531c0.102-0.151 0.19-0.311 0.281-0.469l0.063-0.094c0.649-1.156 1.031-2.485 1.031-3.906 0-4.4183-3.582-8-8-8zm0 4c2.209 0 4 1.7909 4 4 0 2.209-1.791 4-4 4-2.2091 0-4-1.791-4-4 0-2.2091 1.7909-4 4-4z',
|
||||
'fillColor': 'rgb(' + pluginOptions.icon.url[0] + ',' + pluginOptions.icon.url[1] + ',' + pluginOptions.icon.url[2] + ')',
|
||||
'fillOpacity': pluginOptions.icon.url[3] / 255,
|
||||
'scale': 1.3,
|
||||
'strokeWeight': 1,
|
||||
'strokeColor': 'rgb(255, 255, 255)',
|
||||
'strokeOpacity': 0.65,
|
||||
'anchor': new google.maps.Point(12, 27)
|
||||
};
|
||||
iconSize = {
|
||||
'width': 22,
|
||||
'height': 28
|
||||
};
|
||||
} else {
|
||||
markerOpts.icon.url = pluginOptions.icon.url;
|
||||
if (pluginOptions.icon.size) {
|
||||
markerOpts.icon.scaledSize = new google.maps.Size(icon.size.width, icon.size.height);
|
||||
iconSize = icon.size;
|
||||
}
|
||||
}
|
||||
if ('anchor' in icon && Array.isArray(icon.anchor)) {
|
||||
markerOpts.icon.anchor = new google.maps.Point(icon.anchor[0], icon.anchor[1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!markerOpts.icon ||
|
||||
!markerOpts.icon.url && !markerOpts.icon.path) {
|
||||
// default marker
|
||||
markerOpts.icon = {
|
||||
'path': 'm12 0c-4.4183 2.3685e-15 -8 3.5817-8 8 0 1.421 0.3816 2.75 1.0312 3.906 0.1079 0.192 0.221 0.381 0.3438 0.563l6.625 11.531 6.625-11.531c0.102-0.151 0.19-0.311 0.281-0.469l0.063-0.094c0.649-1.156 1.031-2.485 1.031-3.906 0-4.4183-3.582-8-8-8zm0 4c2.209 0 4 1.7909 4 4 0 2.209-1.791 4-4 4-2.2091 0-4-1.791-4-4 0-2.2091 1.7909-4 4-4z',
|
||||
'fillColor': 'rgb(255, 0, 0)',
|
||||
'fillOpacity': 1,
|
||||
'scale': 1.3,
|
||||
'strokeWeight': 1,
|
||||
'strokeColor': 'rgb(255, 255, 255)',
|
||||
'strokeOpacity': 0.65,
|
||||
'anchor': new google.maps.Point(12, 27)
|
||||
};
|
||||
iconSize = {
|
||||
'width': 22,
|
||||
'height': 28
|
||||
};
|
||||
}
|
||||
|
||||
if ('zIndex' in pluginOptions) {
|
||||
markerOpts.zIndex = pluginOptions.zIndex;
|
||||
}
|
||||
markerOpts.map = map;
|
||||
var marker = new google.maps.Marker(markerOpts);
|
||||
marker.addListener('click', self.onMarkerClickEvent.bind(self, event.MARKER_CLICK, marker), {passive: true});
|
||||
marker.addListener('dragstart', self.onMarkerEvent.bind(self, event.MARKER_DRAG_START, marker));
|
||||
marker.addListener('drag', self.onMarkerEvent.bind(self, event.MARKER_DRAG, marker));
|
||||
marker.addListener('dragend', self.onMarkerEvent.bind(self, event.MARKER_DRAG_END, marker));
|
||||
|
||||
if (pluginOptions.title) {
|
||||
marker.set('title', pluginOptions.title);
|
||||
}
|
||||
if (pluginOptions.snippet) {
|
||||
marker.set('snippet', pluginOptions.snippet);
|
||||
}
|
||||
|
||||
|
||||
self.pluginMap.objects[markerId] = marker;
|
||||
self.pluginMap.objects['marker_property_' + markerId] = markerOpts;
|
||||
|
||||
if (iconSize) {
|
||||
onSuccess(marker, {
|
||||
'__pgmId': markerId,
|
||||
'width': iconSize.width,
|
||||
'height': iconSize.height
|
||||
});
|
||||
} else {
|
||||
var markerIcon = marker.getIcon();
|
||||
if (markerIcon && markerIcon.size) {
|
||||
onSuccess({
|
||||
'__pgmId': markerId,
|
||||
'width': markerIcon.size.width,
|
||||
'height': markerIcon.size.height
|
||||
});
|
||||
} else {
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
onSuccess(marker, {
|
||||
'__pgmId': markerId,
|
||||
'width': img.width,
|
||||
'height': img.height
|
||||
});
|
||||
};
|
||||
img.onerror = function(error) {
|
||||
console.warn(error.getMessage());
|
||||
onSuccess(marker, {
|
||||
'__pgmId': markerId,
|
||||
'width': 20,
|
||||
'height': 42
|
||||
});
|
||||
};
|
||||
if (typeof markerOpts.icon === 'string') {
|
||||
img.src = markerOpts.icon;
|
||||
} else {
|
||||
img.src = markerOpts.icon.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
marker.setAnimation(null);
|
||||
}, 500);
|
||||
};
|
||||
PluginMarker.prototype._removeMarker = function(marker) {
|
||||
marker.setMap(null);
|
||||
marker = undefined;
|
||||
};
|
||||
|
||||
PluginMarker.prototype.setDisableAutoPan = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var disableAutoPan = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.set('disableAutoPan', disableAutoPan);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setFlat = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var flat = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.set('flat', flat);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setVisible = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var visible = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.setVisible(visible);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setAnimation = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var animation = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.setAnimation(google.maps.Animation[animation]);
|
||||
setTimeout(function() {
|
||||
marker.setAnimation(null);
|
||||
}, 500);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setRotation = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var rotation = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
var icon = marker.getIcon();
|
||||
if (icon && icon.path) {
|
||||
icon.rotation = rotation;
|
||||
marker.setIcon(icon);
|
||||
}
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setDraggable = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.setDraggable(args[1]);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setInfoWindowAnchor = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
var icon = marker.getIcon();
|
||||
var anchorX = args[1];
|
||||
anchorX = anchorX - icon.size.width / 2;
|
||||
var anchorY = args[2];
|
||||
anchorY = anchorY - icon.size.height / 2;
|
||||
marker.setOptions({
|
||||
'anchorPoint': new google.maps.Point(anchorX, anchorY)
|
||||
});
|
||||
if (self.infoWnd) {
|
||||
self._showInfoWindow.call(self, marker);
|
||||
}
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setTitle = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var title = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.set('title', title);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMarker.prototype.setSnippet = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var snippet = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.set('snippet', snippet);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMarker.prototype.setOpacity = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var opacity = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.setOpacity(opacity);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMarker.prototype.setIconAnchor = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var anchorX = args[1],
|
||||
anchorY = args[2];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
var icon = marker.getIcon();
|
||||
if (typeof icon === 'string') {
|
||||
icon = {
|
||||
'url': icon
|
||||
};
|
||||
}
|
||||
icon.anchor = new google.maps.Point(anchorX, anchorY);
|
||||
marker.setIcon(icon);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setZIndex = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var zIndex = args[1];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.setZIndex(zIndex);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.showInfoWindow = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
if (self.pluginMap.activeMarker && self.pluginMap.activeMarker !== marker) {
|
||||
self.onMarkerClickEvent(event.INFO_CLICK, self.pluginMap.activeMarker);
|
||||
}
|
||||
self.pluginMap.activeMarker = marker;
|
||||
self._showInfoWindow.call(self, marker);
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.hideInfoWindow = function(onSuccess) {
|
||||
var self = this;
|
||||
if (self.infoWnd) {
|
||||
google.maps.event.trigger(self.infoWnd, 'closeclick');
|
||||
self.infoWnd.close();
|
||||
self.infoWnd = null;
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
PluginMarker.prototype.setIcon = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
|
||||
self.setIcon_(marker, args[1])
|
||||
.then(onSuccess)
|
||||
.catch(onError);
|
||||
};
|
||||
PluginMarker.prototype.setIcon_ = function(marker, iconOpts) {
|
||||
return new Promise(function(resolve) {
|
||||
if (marker) {
|
||||
if (Array.isArray(iconOpts)) {
|
||||
// Specifies color name or rule
|
||||
iconOpts = {
|
||||
'path': 'm12 0c-4.4183 2.3685e-15 -8 3.5817-8 8 0 1.421 0.3816 2.75 1.0312 3.906 0.1079 0.192 0.221 0.381 0.3438 0.563l6.625 11.531 6.625-11.531c0.102-0.151 0.19-0.311 0.281-0.469l0.063-0.094c0.649-1.156 1.031-2.485 1.031-3.906 0-4.4183-3.582-8-8-8zm0 4c2.209 0 4 1.7909 4 4 0 2.209-1.791 4-4 4-2.2091 0-4-1.791-4-4 0-2.2091 1.7909-4 4-4z',
|
||||
'fillColor': 'rgb(' + iconOpts[0] + ',' + iconOpts[1] + ',' + iconOpts[2] + ')',
|
||||
'fillOpacity': iconOpts[3] / 256,
|
||||
'scale': 1.3,
|
||||
'strokeWeight': 0,
|
||||
'anchor': new google.maps.Point(12, 27)
|
||||
};
|
||||
} else if (typeof iconOpts === 'object') {
|
||||
|
||||
if (typeof iconOpts.size === 'object') {
|
||||
iconOpts.size = new google.maps.Size(iconOpts.size.width, iconOpts.size.height, 'px', 'px');
|
||||
iconOpts.scaledSize = iconOpts.size;
|
||||
}
|
||||
if (Array.isArray(iconOpts.anchor)) {
|
||||
iconOpts.anchor = new google.maps.Point(iconOpts.anchor[0], iconOpts.anchor[1]);
|
||||
}
|
||||
}
|
||||
|
||||
marker.setIcon(iconOpts);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
PluginMarker.prototype.setPosition = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
marker.setPosition({'lat': args[1], 'lng': args[2]});
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMarker.prototype.remove = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
var overlayId = args[0];
|
||||
var marker = self.pluginMap.objects[overlayId];
|
||||
if (marker) {
|
||||
google.maps.event.clearInstanceListeners(marker);
|
||||
marker.setMap(null);
|
||||
marker = undefined;
|
||||
self.pluginMap.objects[overlayId] = undefined;
|
||||
delete self.pluginMap.objects[overlayId];
|
||||
delete self.pluginMap.objects['marker_property_' + overlayId];
|
||||
}
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
PluginMarker.prototype.onMarkerEvent = function(evtName, marker) {
|
||||
var self = this,
|
||||
mapId = self.pluginMap.__pgmId;
|
||||
|
||||
if (mapId in plugin.google.maps) {
|
||||
var latLng = marker.getPosition();
|
||||
plugin.google.maps[mapId]({
|
||||
'evtName': evtName,
|
||||
'callback': '_onMarkerEvent',
|
||||
'args': [marker.overlayId, new LatLng(latLng.lat(), latLng.lng())]
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
PluginMarker.prototype._showInfoWindow = function(marker) {
|
||||
var self = this;
|
||||
if (!self.infoWnd) {
|
||||
self.infoWnd = new google.maps.InfoWindow({
|
||||
'pixelOffset': new google.maps.Size(0, 0)
|
||||
});
|
||||
}
|
||||
var container = document.createElement('div');
|
||||
if (self.pluginMap.activeMarker && self.pluginMap.activeMarker !== marker) {
|
||||
self.onMarkerClickEvent(event.INFO_CLICK, self.pluginMap.activeMarker);
|
||||
}
|
||||
self.pluginMap.activeMarker = marker;
|
||||
self.pluginMap._syncInfoWndPosition.call(self);
|
||||
var maxWidth = marker.getMap().getDiv().offsetWidth * 0.7;
|
||||
var html = [];
|
||||
if (marker.get('title')) {
|
||||
html.push(marker.get('title'));
|
||||
}
|
||||
if (marker.get('snippet')) {
|
||||
html.push('<small>' + marker.get('snippet') + '</small>');
|
||||
}
|
||||
if (html.length > 0) {
|
||||
container.innerHTML = html.join('<br>');
|
||||
google.maps.event.addListenerOnce(self.infoWnd, 'domready', function() {
|
||||
self.onMarkerClickEvent(event.INFO_OPEN, marker);
|
||||
|
||||
if (container.parentNode) {
|
||||
google.maps.event.addDomListener(container.parentNode.parentNode.parentNode, 'click', function() {
|
||||
self.onMarkerClickEvent(event.INFO_CLICK, marker);
|
||||
}, true);
|
||||
}
|
||||
|
||||
});
|
||||
self.infoWnd.setOptions({
|
||||
content: container,
|
||||
disableAutoPan: marker.disableAutoPan,
|
||||
maxWidth: maxWidth
|
||||
});
|
||||
google.maps.event.addListener(self.infoWnd, 'closeclick', function() {
|
||||
google.maps.event.clearInstanceListeners(self.infoWnd);
|
||||
self.onMarkerClickEvent(event.INFO_CLOSE, marker);
|
||||
});
|
||||
self.infoWnd.open(marker.getMap(), marker);
|
||||
}
|
||||
};
|
||||
|
||||
PluginMarker.prototype.onMarkerClickEvent = function(evtName, marker) {
|
||||
var self = this;
|
||||
|
||||
var overlayId = marker.get('overlayId');
|
||||
|
||||
if (self.pluginMap.activeMarker && self.pluginMap.activeMarker !== marker) {
|
||||
self.onMarkerEvent(event.INFO_CLOSE, self.pluginMap.activeMarker);
|
||||
}
|
||||
self.pluginMap.activeMarker = marker;
|
||||
if (marker.get('disableAutoPan') === false) {
|
||||
self.pluginMap.get('map').panTo(marker.getPosition());
|
||||
}
|
||||
if (overlayId.indexOf('markercluster_') > -1) {
|
||||
self.onClusterEvent(evtName, marker);
|
||||
} else {
|
||||
self.onMarkerEvent(evtName, marker);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = PluginMarker;
|
||||
@@ -0,0 +1,714 @@
|
||||
|
||||
var utils = require('cordova/utils'),
|
||||
event = require('cordova-plugin-googlemaps.event'),
|
||||
PluginMarker = require('cordova-plugin-googlemaps.PluginMarker'),
|
||||
Thread = require('cordova-plugin-googlemaps.Thread'),
|
||||
LatLng = require('cordova-plugin-googlemaps.LatLng'),
|
||||
BaseArrayClass = require('cordova-plugin-googlemaps.BaseArrayClass');
|
||||
|
||||
var STATUS = {
|
||||
'WORKING': 0,
|
||||
'CREATED': 1,
|
||||
'DELETED': 2
|
||||
};
|
||||
|
||||
function PluginMarkerCluster(pluginMap) {
|
||||
var self = this;
|
||||
PluginMarker.call(self, pluginMap);
|
||||
|
||||
Object.defineProperty(self, 'pluginMarkers', {
|
||||
value: {},
|
||||
writable: false
|
||||
});
|
||||
Object.defineProperty(self, 'debugFlags', {
|
||||
value: {},
|
||||
writable: false
|
||||
});
|
||||
|
||||
var deleteMarkers = new BaseArrayClass();
|
||||
Object.defineProperty(self, 'deleteMarkers', {
|
||||
value: deleteMarkers,
|
||||
writable: false
|
||||
});
|
||||
|
||||
deleteMarkers.on('insert_at', function() {
|
||||
var key = deleteMarkers.removeAt(0);
|
||||
var marker = self.pluginMap.objects[key];
|
||||
if (marker) {
|
||||
self._removeMarker(marker);
|
||||
}
|
||||
|
||||
self.pluginMap.objects[key] = undefined;
|
||||
self.pluginMap.objects['marker_property_' + key] = undefined;
|
||||
self.pluginMarkers[key] = undefined;
|
||||
|
||||
delete self.pluginMap.objects[key];
|
||||
delete self.pluginMap.objects['marker_property_' + key];
|
||||
delete self.pluginMarkers[key];
|
||||
self.pluginMarkers[key] = STATUS.DELETED;
|
||||
});
|
||||
}
|
||||
|
||||
utils.extend(PluginMarkerCluster, PluginMarker);
|
||||
|
||||
PluginMarkerCluster.prototype._create = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
params = args[1],
|
||||
hashCode = args[2],
|
||||
positionList = params.positionList;
|
||||
|
||||
//---------------------------------------------
|
||||
// Calculate geocell hashCode on multi thread
|
||||
//---------------------------------------------
|
||||
var tasks = [];
|
||||
while(positionList.length > 0) {
|
||||
var list = positionList.splice(0, 10);
|
||||
|
||||
tasks.push(new Promise(function(resolve, reject) {
|
||||
|
||||
var thread = new Thread(function(params) {
|
||||
|
||||
|
||||
|
||||
var GEOCELL_GRID_SIZE = 4;
|
||||
var GEOCELL_ALPHABET = '0123456789abcdef';
|
||||
|
||||
function getGeocell(lat, lng, resolution) {
|
||||
var north = 90.0,
|
||||
south = -90.0,
|
||||
east = 180.0,
|
||||
west = -180.0,
|
||||
subcell_lng_span, subcell_lat_span,
|
||||
x, y, cell = [];
|
||||
|
||||
while(cell.length < resolution + 1) {
|
||||
subcell_lng_span = (east - west) / GEOCELL_GRID_SIZE;
|
||||
subcell_lat_span = (north - south) / GEOCELL_GRID_SIZE;
|
||||
|
||||
x = Math.min(Math.floor(GEOCELL_GRID_SIZE * (lng - west) / (east - west)), GEOCELL_GRID_SIZE - 1);
|
||||
y = Math.min(Math.floor(GEOCELL_GRID_SIZE * (lat - south) / (north - south)), GEOCELL_GRID_SIZE - 1);
|
||||
cell.push(_subdiv_char(x, y));
|
||||
|
||||
south += subcell_lat_span * y;
|
||||
north = south + subcell_lat_span;
|
||||
|
||||
west += subcell_lng_span * x;
|
||||
east = west + subcell_lng_span;
|
||||
}
|
||||
return cell.join('');
|
||||
}
|
||||
function _subdiv_char(posX, posY) {
|
||||
return GEOCELL_ALPHABET.charAt(
|
||||
(posY & 2) << 2 |
|
||||
(posX & 2) << 1 |
|
||||
(posY & 1) << 1 |
|
||||
(posX & 1) << 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return params.positionList.map(function(position) {
|
||||
return getGeocell(position.lat, position.lng, params.resolution);
|
||||
});
|
||||
});
|
||||
|
||||
thread
|
||||
.once({
|
||||
'positionList': list,
|
||||
'resolution': 12
|
||||
})
|
||||
.done(resolve)
|
||||
.fail(reject);
|
||||
}));
|
||||
}
|
||||
|
||||
Promise.all(tasks)
|
||||
.then(function(results) {
|
||||
var id = 'markerclister_' + hashCode;
|
||||
self.debugFlags[id] = params.debug;
|
||||
|
||||
var result = {
|
||||
'geocellList': Array.prototype.concat.apply([], results),
|
||||
'hashCode': hashCode,
|
||||
'__pgmId': id
|
||||
};
|
||||
|
||||
onSuccess(result);
|
||||
})
|
||||
.catch(onError);
|
||||
|
||||
};
|
||||
|
||||
|
||||
PluginMarkerCluster.prototype.redrawClusters = function(onSuccess, onError, args) {
|
||||
var self = this;
|
||||
|
||||
var updateClusterIDs = [],
|
||||
changeProperties = {},
|
||||
clusterId = args[0],
|
||||
isDebug = self.debugFlags[clusterId],
|
||||
params = args[1],
|
||||
map = self.pluginMap.get('map');
|
||||
|
||||
if ('new_or_update' in params) {
|
||||
|
||||
//---------------------------
|
||||
// Determine new or update
|
||||
//---------------------------
|
||||
params.new_or_update.forEach(function(clusterData) {
|
||||
var positionJSON = clusterData.position,
|
||||
markerId = clusterData.__pgmId,
|
||||
clusterId_markerId = clusterId + '-' + markerId;
|
||||
|
||||
// Save the marker properties
|
||||
self.pluginMap.objects['marker_property_' + clusterId_markerId] = clusterData;
|
||||
|
||||
// Set the WORKING status flag
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.WORKING;
|
||||
updateClusterIDs.push(clusterId_markerId);
|
||||
|
||||
// Prepare the marker properties for addMarker()
|
||||
var properties = {
|
||||
'lat': positionJSON.lat,
|
||||
'lng': positionJSON.lng,
|
||||
'__pgmId': clusterId_markerId
|
||||
};
|
||||
if ('title' in clusterData) {
|
||||
properties.title = clusterData.title;
|
||||
}
|
||||
|
||||
if ('icon' in clusterData) {
|
||||
var iconObj = clusterData.icon,
|
||||
iconProperties = {},
|
||||
label;
|
||||
if (typeof iconObj === 'string') {
|
||||
iconProperties.url = iconObj;
|
||||
properties.icon = iconProperties;
|
||||
} else if (typeof iconObj === 'object') {
|
||||
iconProperties = iconObj;
|
||||
if (clusterData.isClusterIcon) {
|
||||
if (iconObj.label) {
|
||||
label = iconObj.label;
|
||||
if (isDebug) {
|
||||
label.text = markerId;
|
||||
} else {
|
||||
label.text = clusterData.count;
|
||||
}
|
||||
} else {
|
||||
label = {};
|
||||
if (isDebug) {
|
||||
label.text = markerId;
|
||||
} else {
|
||||
label.fontSize = 15;
|
||||
label.bold = true;
|
||||
label.text = clusterData.count;
|
||||
}
|
||||
}
|
||||
iconProperties.label = label;
|
||||
}
|
||||
if ('anchor' in iconObj) {
|
||||
iconProperties.anchor = iconObj.anchor;
|
||||
}
|
||||
if ('infoWindowAnchor' in iconObj) {
|
||||
iconProperties.anchor = iconObj.infoWindowAnchor;
|
||||
}
|
||||
properties.icon = iconProperties;
|
||||
}
|
||||
}
|
||||
changeProperties[clusterId_markerId] = JSON.parse(JSON.stringify(properties));
|
||||
});
|
||||
|
||||
if (updateClusterIDs.length === 0) {
|
||||
self.deleteProcess(clusterId, params);
|
||||
onSuccess({});
|
||||
return;
|
||||
}
|
||||
|
||||
//---------------------------
|
||||
// mapping markers on the map
|
||||
//---------------------------
|
||||
var allResults = {};
|
||||
|
||||
//---------------
|
||||
// new or update
|
||||
//---------------
|
||||
var tasks = [];
|
||||
updateClusterIDs.forEach(function(clusterId_markerId) {
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.WORKING;
|
||||
var isNew = !(clusterId_markerId in self.pluginMap.objects);
|
||||
|
||||
// Get the marker properties
|
||||
var markerProperties = changeProperties[clusterId_markerId],
|
||||
properties, marker;
|
||||
|
||||
if (clusterId_markerId.indexOf('-marker_') > -1) {
|
||||
//-------------------
|
||||
// regular marker
|
||||
//-------------------
|
||||
if (isNew) {
|
||||
properties = self.pluginMap.objects['marker_property_' + clusterId_markerId];
|
||||
|
||||
tasks.push(new Promise(function(resolve) {
|
||||
|
||||
self.__create.call(self, clusterId_markerId, {
|
||||
'position': properties.position,
|
||||
'icon': properties.icon,
|
||||
'disableAutoPan': properties.disableAutoPan
|
||||
}, function(marker, properties) {
|
||||
if (markerProperties.title) {
|
||||
marker.set('title', markerProperties.title);
|
||||
}
|
||||
if (markerProperties.snippet) {
|
||||
marker.set('snippet', markerProperties.snippet);
|
||||
}
|
||||
if (self.pluginMarkers[clusterId_markerId] === STATUS.DELETED) {
|
||||
self._removeMarker.call(self, marker);
|
||||
delete self.pluginMarkers[clusterId_markerId];
|
||||
resolve();
|
||||
} else {
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.CREATED;
|
||||
allResults[clusterId_markerId.split('-')[1]] = {
|
||||
'width': properties.width,
|
||||
'height': properties.height
|
||||
};
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
} else {
|
||||
marker = self.pluginMap.objects[clusterId_markerId];
|
||||
//----------------------------------------
|
||||
// Set the title and snippet properties
|
||||
//----------------------------------------
|
||||
if (markerProperties.title) {
|
||||
marker.set('title', markerProperties.title);
|
||||
}
|
||||
if (markerProperties.snippet) {
|
||||
marker.set('snippet', markerProperties.snippet);
|
||||
}
|
||||
if (self.pluginMarkers[clusterId_markerId] === STATUS.DELETED) {
|
||||
self._removeMarker.call(self, marker);
|
||||
delete self.pluginMarkers[clusterId_markerId];
|
||||
} else {
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.CREATED;
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
//--------------------------
|
||||
// cluster icon
|
||||
//--------------------------
|
||||
if (isNew) {
|
||||
// If the requested id is new location, create a marker
|
||||
marker = newClusterIcon({
|
||||
'map': map,
|
||||
'position': {
|
||||
'lat': markerProperties.lat,
|
||||
'lng': markerProperties.lng
|
||||
},
|
||||
'overlayId': clusterId_markerId,
|
||||
'opacity': 0
|
||||
});
|
||||
marker.addListener('click', self.onClusterEvent.bind(self, 'cluster_click', marker));
|
||||
|
||||
// Store the marker instance with markerId
|
||||
self.pluginMap.objects[clusterId_markerId] = marker;
|
||||
|
||||
} else {
|
||||
marker = self.pluginMap.objects[clusterId_markerId];
|
||||
}
|
||||
//----------------------------------------
|
||||
// Set the title and snippet properties
|
||||
//----------------------------------------
|
||||
if (markerProperties.title) {
|
||||
marker.set('title', markerProperties.title);
|
||||
}
|
||||
if (markerProperties.snippet) {
|
||||
marker.set('snippet', markerProperties.snippet);
|
||||
}
|
||||
if (markerProperties.icon) {
|
||||
var icon = markerProperties.icon;
|
||||
|
||||
tasks.push(new Promise(function(resolve) {
|
||||
|
||||
self.setIconToClusterMarker.call(self, clusterId_markerId, marker, icon)
|
||||
.then(function() {
|
||||
//--------------------------------------
|
||||
// Marker was updated
|
||||
//--------------------------------------
|
||||
marker.setVisible(true);
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.CREATED;
|
||||
resolve();
|
||||
})
|
||||
.catch(function(error) {
|
||||
//--------------------------------------
|
||||
// Could not read icon for some reason
|
||||
//--------------------------------------
|
||||
if (marker.get('overlayId')) {
|
||||
self._removeMarker.call(self, marker);
|
||||
}
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.DELETED;
|
||||
|
||||
console.warn(error.getMessage());
|
||||
self.deleteMarkers.push(clusterId_markerId);
|
||||
resolve();
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
} else {
|
||||
//--------------------
|
||||
// No icon for marker
|
||||
//--------------------
|
||||
self.pluginMarkers[clusterId_markerId] = STATUS.CREATED;
|
||||
}
|
||||
}
|
||||
});
|
||||
Promise.all(tasks).then(function() {
|
||||
self.deleteProcess(clusterId, params);
|
||||
onSuccess(allResults);
|
||||
}).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
PluginMarkerCluster.prototype.deleteProcess = function(clusterId, params) {
|
||||
var self = this;
|
||||
if (!params.delete || params.delete.length === 0) {
|
||||
return;
|
||||
}
|
||||
params.delete.forEach(function(key) {
|
||||
self.deleteMarkers.push(clusterId + '-' + key);
|
||||
});
|
||||
};
|
||||
|
||||
PluginMarkerCluster.prototype.setIconToClusterMarker = function(markerId, marker, iconProperty) {
|
||||
var self = this;
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
||||
if (self.pluginMarkers[markerId] === STATUS.DELETED) {
|
||||
self._removeMarker.call(self, marker);
|
||||
delete self.pluginMap.objects[markerId];
|
||||
delete self.pluginMap.objects['marker_property_' + markerId];
|
||||
|
||||
delete self.pluginMarkers[markerId];
|
||||
reject('marker has been removed');
|
||||
return;
|
||||
}
|
||||
self.setIcon_.call(self, marker, iconProperty)
|
||||
.then(function() {
|
||||
if (self.pluginMarkers[markerId] === STATUS.DELETED) {
|
||||
self._removeMarker.call(self, marker);
|
||||
delete self.pluginMap.objects[markerId];
|
||||
delete self.pluginMap.objects['marker_property_' + markerId];
|
||||
self.pluginMarkers.remove(markerId);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
marker.setVisible(true);
|
||||
self.pluginMarkers[markerId] = STATUS.CREATED;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
PluginMarkerCluster.prototype.remove = function(onSuccess, onError, args) {
|
||||
var self = this,
|
||||
clusterId = args[0],
|
||||
keys = Object.keys(self.pluginMarkers);
|
||||
|
||||
keys.forEach(function(key) {
|
||||
if (key.indexOf(clusterId) === 0) {
|
||||
if (self.pluginMap.objects[key]) {
|
||||
self.pluginMap.objects[key].setMap(null);
|
||||
}
|
||||
self.pluginMarkers[key] = STATUS.DELETED;
|
||||
delete self.pluginMap.objects[key];
|
||||
delete self.pluginMap.objects['marker_property_' + key];
|
||||
}
|
||||
});
|
||||
onSuccess();
|
||||
};
|
||||
|
||||
|
||||
PluginMarkerCluster.prototype.onClusterEvent = function(evtName, marker) {
|
||||
var self = this,
|
||||
mapId = self.pluginMap.__pgmId;
|
||||
var overlayId = marker.get('overlayId');
|
||||
var tmp = overlayId.split('-');
|
||||
var clusterId = tmp[0];
|
||||
var markerId = tmp[1];
|
||||
var latLng = marker.getPosition();
|
||||
if (mapId in plugin.google.maps) {
|
||||
if (self.pluginMap.activeMarker) {
|
||||
self.onMarkerEvent(event.INFO_CLOSE, self.pluginMap.activeMarker);
|
||||
}
|
||||
plugin.google.maps[mapId]({
|
||||
'evtName': evtName,
|
||||
'callback': '_onClusterEvent',
|
||||
'args': [clusterId, markerId, new LatLng(latLng.lat(), latLng.lng())]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = PluginMarkerCluster;
|
||||
|
||||
function newClusterIcon(options) {
|
||||
|
||||
// https://github.com/apache/cordova-js/blob/c75e8059114255d1dbae1ede398e6626708ee9f3/src/common/utils.js#L167
|
||||
if (ClusterIconClass.__super__ !== google.maps.OverlayView.prototype) {
|
||||
var key, defined = {};
|
||||
for (key in ClusterIconClass.prototype) {
|
||||
defined[key] = ClusterIconClass.prototype[key];
|
||||
}
|
||||
utils.extend(ClusterIconClass, google.maps.OverlayView);
|
||||
for (key in defined) {
|
||||
ClusterIconClass.prototype[key] = defined[key];
|
||||
}
|
||||
}
|
||||
|
||||
return new ClusterIconClass(options);
|
||||
}
|
||||
function ClusterIconClass(options) {
|
||||
google.maps.OverlayView.apply(this);
|
||||
var self = this;
|
||||
//-----------------------------------------
|
||||
// Create a canvas to draw label
|
||||
//-----------------------------------------
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = 50;
|
||||
canvas.height = 50;
|
||||
canvas.style.visibility = 'hidden';
|
||||
canvas.style.position = 'absolute';
|
||||
canvas.style.zIndex = -100;
|
||||
canvas.setAttribute('id', 'canvas_' + options.overlayId);
|
||||
self.set('canvas', canvas);
|
||||
|
||||
//-----------------------------------------
|
||||
// Create two markers for icon and label
|
||||
//-----------------------------------------
|
||||
var iconMarker = new google.maps.Marker({
|
||||
'clickable': false,
|
||||
'icon': options.icon,
|
||||
'zIndex': 0,
|
||||
'opacity': 0
|
||||
});
|
||||
var labelMarker = new google.maps.Marker({
|
||||
'clickable': true,
|
||||
'zIndex': 1,
|
||||
'icon': self.get('label'),
|
||||
'opacity': 0
|
||||
});
|
||||
labelMarker.addListener('click', function() {
|
||||
google.maps.event.trigger(self, 'click');
|
||||
});
|
||||
self.set('iconMarker', iconMarker);
|
||||
self.set('labelMarker', labelMarker);
|
||||
self.set('opacity', 0);
|
||||
|
||||
iconMarker.bindTo('opacity', labelMarker);
|
||||
iconMarker.bindTo('visible', labelMarker);
|
||||
iconMarker.bindTo('position', labelMarker);
|
||||
iconMarker.bindTo('map', labelMarker);
|
||||
self.bindTo('opacity', iconMarker);
|
||||
self.bindTo('visible', iconMarker);
|
||||
self.bindTo('map', iconMarker);
|
||||
self.bindTo('position', iconMarker);
|
||||
self.set('labelMarkerAnchor', new google.maps.Point(canvas.width / 2, canvas.height / 2));
|
||||
|
||||
self.addListener('icon_changed', function() {
|
||||
var icon = self.get('icon');
|
||||
if (typeof icon === 'string') {
|
||||
icon = {
|
||||
'url': icon
|
||||
};
|
||||
}
|
||||
|
||||
var iconUrl = icon.url;
|
||||
if (typeof icon === 'object') {
|
||||
if (typeof icon.size === 'object' &&
|
||||
icon.size.width && icon.size.height) {
|
||||
icon.anchor = new google.maps.Point(icon.size.width / 2, icon.size.height / 2);
|
||||
iconMarker.setIcon(icon);
|
||||
return;
|
||||
}
|
||||
}
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
icon.size = new google.maps.Size(img.width, img.height);
|
||||
icon.scaledSize = new google.maps.Size(img.width, img.height);
|
||||
icon.anchor = new google.maps.Point(img.width / 2, img.height / 2);
|
||||
self.set('labelMarkerAnchor', new google.maps.Point(img.width / 2, img.height / 2));
|
||||
iconMarker.setIcon(icon);
|
||||
};
|
||||
img.onerror = function(e) {
|
||||
console.error(e);
|
||||
};
|
||||
img.src = iconUrl;
|
||||
});
|
||||
|
||||
//debug
|
||||
//var positionConfirmMarker = new google.maps.Marker();
|
||||
//labelMarker.bindTo('position', positionConfirmMarker);
|
||||
//labelMarker.bindTo('map', positionConfirmMarker);
|
||||
|
||||
for (var key in options) {
|
||||
self.set(key, options[key]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ClusterIconClass.prototype.onAdd = function() {
|
||||
var self = this;
|
||||
var canvas = self.get('canvas');
|
||||
self.get('map').getDiv().appendChild(canvas);
|
||||
};
|
||||
ClusterIconClass.prototype.onRemove = function() {
|
||||
var self = this;
|
||||
var canvas = self.get('canvas');
|
||||
self.set('map', null);
|
||||
google.maps.event.clearInstanceListeners(self.get('iconMarker'));
|
||||
google.maps.event.clearInstanceListeners(self.get('labelMarker'));
|
||||
var parent = canvas.parentNode;
|
||||
if (parent) {
|
||||
parent.removeChild(canvas);
|
||||
}
|
||||
canvas = undefined;
|
||||
};
|
||||
ClusterIconClass.prototype.draw = function() {
|
||||
var self = this,
|
||||
icon = self.get('icon');
|
||||
if (typeof icon === 'string') {
|
||||
icon = {
|
||||
'url': icon
|
||||
};
|
||||
}
|
||||
icon.label = icon.label || {};
|
||||
if (self.get('prevText') === icon.label.text) {
|
||||
return;
|
||||
}
|
||||
self.set('prevText', icon.label.text);
|
||||
self.get('labelMarker').set('opacity', 0);
|
||||
|
||||
(new Promise(function(resolve) {
|
||||
var iconUrl = icon.url;
|
||||
if (typeof icon === 'object') {
|
||||
if (typeof icon.size === 'object' &&
|
||||
icon.size.width && icon.size.height) {
|
||||
return resolve(icon.size);
|
||||
}
|
||||
}
|
||||
var img = new Image();
|
||||
img.onload = function() {
|
||||
var newIconInfo = {
|
||||
width: img.width,
|
||||
height: img.height
|
||||
};
|
||||
icon.size = newIconInfo;
|
||||
self.set('labelMarkerAnchor', new google.maps.Point(newIconInfo.width / 2, newIconInfo.height / 2));
|
||||
self.set('icon', icon, true);
|
||||
resolve(newIconInfo);
|
||||
};
|
||||
img.onerror = function() {
|
||||
var newIconInfo = {
|
||||
width: img.width,
|
||||
height: img.height
|
||||
};
|
||||
icon.size = newIconInfo;
|
||||
self.set('icon', icon, true);
|
||||
resolve(newIconInfo);
|
||||
};
|
||||
img.src = iconUrl;
|
||||
}))
|
||||
.then(function(iconSize) {
|
||||
var canvas = self.get('canvas'),
|
||||
ctx = canvas.getContext('2d');
|
||||
canvas.width = iconSize.width;
|
||||
canvas.height = iconSize.height;
|
||||
|
||||
var labelOptions = icon.label || {
|
||||
fontSize: 10,
|
||||
bold: false,
|
||||
italic: false
|
||||
};
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
// debug
|
||||
//ctx.fillStyle="#FF000077";
|
||||
//ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
if (labelOptions.text) {
|
||||
var fontStyles = [];
|
||||
|
||||
if ('color' in labelOptions) {
|
||||
ctx.fillStyle = [
|
||||
'rgba(',
|
||||
labelOptions.color[0],
|
||||
',',
|
||||
labelOptions.color[1],
|
||||
',',
|
||||
labelOptions.color[2],
|
||||
',',
|
||||
labelOptions.color[3] / 255,
|
||||
')'].join('');
|
||||
} else {
|
||||
ctx.fillStyle = 'black';
|
||||
}
|
||||
|
||||
if (labelOptions.italic === true) {
|
||||
fontStyles.push('italic');
|
||||
}
|
||||
if (labelOptions.bold === true) {
|
||||
fontStyles.push('bold');
|
||||
}
|
||||
|
||||
fontStyles.push(parseInt(labelOptions.fontSize || '10', 10) + 'px');
|
||||
|
||||
fontStyles.push('Arial');
|
||||
|
||||
ctx.font = fontStyles.join(' ');
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.fillText(labelOptions.text, iconSize.width / 2, iconSize.height / 2);
|
||||
// debug
|
||||
//ctx.fillText(selfId.split("-")[1], iconSize.width / 2, iconSize.height / 2);
|
||||
|
||||
}
|
||||
|
||||
self.get('labelMarker').set('icon', {
|
||||
'url': canvas.toDataURL(),
|
||||
'anchor': self.get('labelMarkerAnchor')
|
||||
});
|
||||
setTimeout(function() {
|
||||
self.set('opacity', 1);
|
||||
}, 10);
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
ClusterIconClass.prototype.setIcon = function(icon) {
|
||||
var self = this;
|
||||
self.set('icon', icon);
|
||||
};
|
||||
ClusterIconClass.prototype.setVisible = function(visible) {
|
||||
var self = this;
|
||||
self.set('visible', visible);
|
||||
};
|
||||
ClusterIconClass.prototype.setPosition = function(position) {
|
||||
var self = this;
|
||||
self.set('position', position);
|
||||
};
|
||||
|
||||
ClusterIconClass.prototype.getPosition = function() {
|
||||
var self = this;
|
||||
return self.get('position');
|
||||
};
|
||||
|
||||
ClusterIconClass.prototype.setMap = function(map) {
|
||||
var self = this;
|
||||
self.set('map', map);
|
||||
};
|
||||