Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d04dbc60f0 | ||
|
|
59326e6688 | ||
|
|
b07488751b | ||
|
|
77440828bd | ||
|
|
8aa353c048 | ||
|
|
c9ed60d2de | ||
|
|
b9905ff3db | ||
|
|
9b0c13ecd4 | ||
|
|
9da68a54ba | ||
|
|
a827eb929b | ||
|
|
8e37cce864 | ||
|
|
e56b983ee4 | ||
|
|
0a59ccdde5 | ||
|
|
1db166181c | ||
|
|
667a688e54 | ||
|
|
b3bdc27034 | ||
|
|
8c3084e7ed | ||
|
|
8aef10fe8b | ||
|
|
70937e1632 | ||
|
|
7e0dd02fba | ||
|
|
7ab6fbe856 | ||
|
|
c569bd95d9 | ||
|
|
8c393f5eee | ||
|
|
7508046ad9 | ||
|
|
4ad8a12b2d | ||
|
|
938198fae0 | ||
|
|
f0998f6dc6 | ||
|
|
a92e2b6c74 | ||
|
|
ed3dfe0014 | ||
|
|
e96f07196f | ||
|
|
c09ce5769f | ||
|
|
6b83d1eff8 | ||
|
|
6c13507e0d | ||
|
|
c0c43946c9 | ||
|
|
7713742770 |
No files matched your search
@@ -1,12 +0,0 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/*
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
apikeys.xml
|
||||
/app/release/app-release.aab
|
||||
/_img/connectors/*.ai
|
||||
@@ -1,35 +0,0 @@
|
||||
language: android
|
||||
dist: trusty
|
||||
android:
|
||||
components:
|
||||
- build-tools-29.0.3
|
||||
- android-29
|
||||
env:
|
||||
global:
|
||||
- secure: KYdFlMarsyXw+OHht1Atp+Kirbw9O09Ck14EjFuKb1eNtknurZ/tGEXuD+8xWh1W8W21kgHEG7s3rzru53t29buz+FW9f+ZmhEWXFP3OydyvXLw4BAVVOjm6xG2uHX/8MOGLJNM7cfaF25EPQ+kznHe84R29KaLH90mNRr2lPa4VnfbcnvDStiVaez/vJ72UoYSP5HICAzoF70yC3ZvvCK1hZv71UIysCbFE2IkxvMhG9OOGebdnRmFssaRCrvfRLjitobcLzkPWzZZIqdjNASf8/iAxX8VgGBYfVj8ID06AfMrtgXNJRCvcD0LICraQ+WPUbikMunRieGO8PNHSB5vKdPoC50aLUa0RoRb4G3QM1pR2A8xAFlIJFX2R7iY+2t24L9hRFqB98+QoQzutfkAI1T0rzem/wtpZpuan+bDawDJEHGCeYbE0aPDAl6lytgrEE9fRgV3c1jJLQzu0xIWG8YLl3iMg0hL+c0wCKXoeqrfCFS6kYmmG7W+rQp4tCZifvRbWfAXwIPQieffKxqdEuUwiUsYxdzCu9v9uU3nflEOLLuRgeMP3gV8mpur9b5GztpkfgfzcAqsF+NiY01kYgGtrgCYlMy0TxASE+UuALrtkQtU01wwhs9RH7Az0Ib3C+MT5DTjxHQCYETIViocmNEG2vfAbgHazCpGAhcY=
|
||||
- secure: Hoko50vP+Mwm/O4CWvPvjMxd1gGhi+Bultjyy1WpludSmZFCfKz45Mj1EqzeYk6MeMLvGOEkSLB5wjdXdgJ9j5gEOF5K34k4vESJA7+DqDO3I7Xw9cnWgOXdFqB0qGHar0TVP3Dfg7ZcRgtmeX6t2uoELFLiS9GvTnbZXk4PCUybd979Xi8XHjEQV7+3EZbSjtsI4GFeIK1rrjd0I+UM88zYrWnz1KhdCjWvQb4iZjo+ib6NmGGEMqR7jJPRZz3KB01Y+n5h21qq9/Nv31zQIqt2B5nRxy3vBvvqKapgprIk+hVOpNnBU8w89uWUU6tYUeFk0t7z1TYWjgaBrMmGCM+aKkQ2q5F/ygNzDwB+KkpJx709Yhf0ZX8g2yvkdz+Ok7moYuvmrOPOf4E/U3BlfZxbGtRD2bGYbDgHLFYlTn6v5J2kJHDJAz31yvF5jvJejDaPp2IBVfoMRy7ZJFmGUNHGd9Se6bRwxS++AoobP5WDrBiUXNe2KKDMs3e7vbaO+hLbZ9XHpjeWIJGUfvtTee8EHZqF/8A3ju53V4/R0ehlOv2UZbpYNqcmwrsy9/R4pgMfDkG3Q054LmYrxD8DIC9b8excVMwWRP5aQ1TqZnxO2B1/vJU87RcnGl3jekeHTdHXbRq9BMV4dAdPfB9X3nGIi2GgV0iTBk+24xccOc0=
|
||||
- secure: RsN/2nBVv0byMzwchuAhDti1AWKECg3Uzqk6Ahgaqg02zx8GHj60j0qNax7KuMUg70X3G4b3m8ZzndAU0wcJ98UyIku7ofUYgXHm0XYKTJwiyyhrKJ8pZ4qoeCoRkitIGIitlb84fSufVamcoLyLNbLUsO5OzL+Uscyhq/BwAhyOhj5FB9DM+GE9ntanQ4m3k4guMyMctR9CGS6Tk4LKJ1WCm0AnjTPalc+we+7yORY2J/9d6VHLKfaFXbpuWmrdnFfDZqxqcUODsxPVE0LuUtXyKQDTjjfQc8106Z/z19AJS+oLm/2UND84PD3MqsjX6EX0/9k3fY0OsoCuQAivDRmtevQ0bDQrTAyeBLcfnPCw/MYiJWyBcaYBAYK42EAfsFTDBRIduFB/Dpvg+8GuLZSdm4xVYpTlQ2pMtlGNWIGIRQsjk9LZ8swq8QBMiiF/bpMGKdfmnyQj8jkEOCWaAzkR9O+4E4Y7PuBENef9XuV0hLMryCrML2YXigxAKkEUOPhfnG+AbEY+g2kAMp+2EtXaG3tsGxoMhEYA+uRd3o2cacQMMwRpnePH5DYg6F05mrvdHPpt+P9UR1iHaHmjBPAYeksmrdP8bS088zcnePgiL6+6N0m3+l8Krihmxg5RyWjnH18IwX4RO+xg4x3cW+zaScCDbbotDMEYtChF+Hs=
|
||||
before_install:
|
||||
- openssl aes-256-cbc -K $encrypted_53968681344a_key -iv $encrypted_53968681344a_iv -in _ci/keystore.jks.enc -out _ci/keystore.jks -d
|
||||
script:
|
||||
- "./gradlew lintFossDebug testFossDebugUnitTest lintGoogleDebug testGoogleDebugUnitTest"
|
||||
- "./gradlew assembleRelease"
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
cache:
|
||||
directories:
|
||||
- "$HOME/.gradle/caches/"
|
||||
- "$HOME/.gradle/wrapper/"
|
||||
- "$HOME/.android/build-cache"
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: B+V5Fz8k9HbpecyMjpJuLr8aVBrdwtDBDkQh4YQ8nu+Da4AiYwEJZseWXhOWs+oms0gNen9bBxsakQQKu7GKYDs8gIXZZtANWsc0gse8xo+cYT7NqEM3jP6mM3ytAv7VNRX3N2cdL7xazELK3/5+mghfORAAdXXYKUFGG5eTKoML8zgdPVN8E9QFqiusLXqoKhxOMCSE4NS+Di7CGlUmnidRTWg6yxhE085zljmYv2owS0NRbr5a4/zW6Z9xZPALGAqsOvIvpZHuOC2s0eMJWMmYGkK/Ws/LAVxfj4U+YkFp9hlZC0zEg/JoS19Gf57QmEu+vsoQ3uOBYBFv9NPI/R9kVH6o0hcOxId3J0u+ewSGWuceGLRpizXuMxKIvLTS5j6GWkxdSieWjwh/OuVB+ciAHNM31B7GP4FWnfz0ZaEVxI/tPenNipZdl9oXdyyBQQ00vPlYp0jT80XhaMh5rDwWMUPaEjRafvymcNyqZ0iVOr0rq1CbdT92STMSmA1U3/rmhtCMD5IGD0b+gQl+VpPKe1QXViYftVxCGL+s4ke4DUZD7HR20fGs8zu61Elnwci1HufbetKFL5TmxoKSLkWFSkzrtBaJnEruZIxhNUMkUL2UPynaOcPNzLoumjHXrUb3m3s0yE4OFelmJ6mJfXswP38sS8kj3wB7R/gC4rw=
|
||||
file:
|
||||
- app/build/outputs/apk/foss/release/app-foss-release.apk
|
||||
- app/build/outputs/apk/google/release/app-google-release.apk
|
||||
on:
|
||||
repo: johan12345/EVMap
|
||||
tags: true
|
||||
skip_cleanup: 'true'
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Johan von Forstner
|
||||
|
||||
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.
|
||||
@@ -1,48 +0,0 @@
|
||||
EVMap [](https://travis-ci.org/johan12345/EVMap)
|
||||
=====
|
||||
|
||||
<img src="https://raw.githubusercontent.com/johan12345/EVMap/master/_img/appicon_cropped.svg?sanitize=true" width=80 alt="Logo"/>
|
||||
|
||||
Android app to access the goingelectric.de electric vehicle charging station directory.
|
||||
|
||||
<a href="https://play.google.com/store/apps/details?id=net.vonforst.evmap" target="_blank">
|
||||
<img src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png" alt="Get it on Google Play" height="100"/></a>
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- [Material Design](https://material.io/)
|
||||
- Shows all charging stations from the community-maintained [GoingElectric.de](https://www.goingelectric.de/stromtankstellen/) directory
|
||||
- Realtime availability information (beta)
|
||||
- Search places
|
||||
- Favorites list, also with availability information
|
||||
- No ads, fully open source
|
||||
- Compatible with Android 5.0 and above
|
||||
|
||||
Screenshots
|
||||
-----------
|
||||
|
||||
<img src="https://raw.githubusercontent.com/johan12345/EVMap/master/_img/screenshots/phone/01_main.png" width=250 alt="Screenshot 1"/><img src="https://raw.githubusercontent.com/johan12345/EVMap/master/_img/screenshots/phone/02_detail.png" width=250 alt="Screenshot 2"/>
|
||||
|
||||
Development setup
|
||||
-----------------
|
||||
|
||||
The App is developed using Android Studio.
|
||||
|
||||
For testing the app, you need to obtain API Keys for the
|
||||
[GoingElectric API](https://www.goingelectric.de/stromtankstellen/api/)
|
||||
as well as for [Google APIs](https://console.developers.google.com/)
|
||||
("Maps SDK for Android" and "Places API" need to be activated). These APIs need to be put into the
|
||||
app in the form of a resource file called `apikeys.xml` under `app/src/main/res/values`, with the
|
||||
following content:
|
||||
|
||||
```xml
|
||||
<resources>
|
||||
<string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">
|
||||
insert your Google Maps key here
|
||||
</string>
|
||||
<string name="goingelectric_key" translatable="false">
|
||||
insert your GoingElectric key here
|
||||
</string>
|
||||
</resources>
|
||||
```
|
||||
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 192 192"
|
||||
style="enable-background:new 0 0 192 192;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{display:none;}
|
||||
.st1{display:inline;}
|
||||
|
||||
.st2{display:inline;opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#212121;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
|
||||
.st3{display:inline;opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#FFFFFF;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
|
||||
.st4{opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#212121;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
|
||||
.st5{opacity:0.2;clip-path:url(#SVGID_2_);fill:none;stroke:#FFFFFF;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
|
||||
.st6{display:none;fill:#802C27;}
|
||||
.st7{fill:#FFB300;}
|
||||
.st8{fill:#90A4AE;}
|
||||
.st9{fill:#546E7A;}
|
||||
.st10{fill:#00E676;}
|
||||
.st11{fill:#FFFFFF;fill-opacity:0.2;}
|
||||
.st12{fill:#3E2723;fill-opacity:0.2;}
|
||||
.st13{opacity:0.45;}
|
||||
</style>
|
||||
<g class="st0">
|
||||
<defs>
|
||||
<path id="SVGID_1_" d="M122.5,57.1c-5.8-7.7-15.2-12.4-24.7-12.9H94c-8.1,0.5-15.9,3.9-21.7,9.6c-4.3,4.3-7.4,9.7-8.6,15.7
|
||||
c-1.5,7.5-0.1,15.4,3.5,22.1c2.3,4.5,5.2,8.6,8.1,12.8c4.9,6.8,9.8,13.7,13.2,21.3c3.1,6.5,4.9,13.6,5.6,20.8
|
||||
c0.4,0.6,1.1,1,1.7,1.5c2.6-1,2-4,2.5-6.2c1.9-12.8,8.7-24.2,16.1-34.6c6.6-9.2,14.1-19,14.4-30.8V74
|
||||
C128.5,67.9,126.3,61.9,122.5,57.1z M106.2,74.3l-12.2,21V79.5h-5.2V60.3h17.5l-7,14H106.2z" />
|
||||
</defs>
|
||||
<clipPath id="SVGID_2_" class="st1">
|
||||
<use xlink:href="#SVGID_1_" style="overflow:visible;" />
|
||||
</clipPath>
|
||||
<path class="st2" d="M106.2,74.3h-7" />
|
||||
<path class="st2" d="M106.2,60.3c0,0-17.5,0-17.5,0" />
|
||||
<line class="st3" x1="93.9" y1="79.5" x2="88.7" y2="79.5" />
|
||||
<path class="st4" d="M94,79v16.2" />
|
||||
<line class="st5" x1="106.2" y1="60.3" x2="99.2" y2="74.3" />
|
||||
<line class="st5" x1="106.2" y1="74.3" x2="93.9" y2="95.2" />
|
||||
</g>
|
||||
<path class="st6" d="M255-187.1L255-187.1L234.2-208l-5.9,5.9l11.8,11.8c-5.3,2-9,7.1-9,13.1c0,7.7,6.3,14,14,14
|
||||
c2,0,3.9-0.4,5.6-1.2v40.4c0,3.1-2.5,5.6-5.6,5.6s-5.6-2.5-5.6-5.6v-25.2c0-6.2-5-11.2-11.2-11.2h-5.6v-39.2c0-6.2-5-11.2-11.2-11.2
|
||||
h-33.7c-6.2,0-11.2,5-11.2,11.2v89.7h56.1V-152h8.4v28c0,7.7,6.3,14,14,14s14-6.3,14-14v-53.2C259.1-181.1,257.6-184.6,255-187.1
|
||||
M245.1-171.6c-3.1,0-5.6-2.5-5.6-5.6c0-3.1,2.5-5.6,5.6-5.6s5.6,2.5,5.6,5.6C250.7-174.1,248.2-171.6,245.1-171.6 M189.1-126.8
|
||||
V-152h-11.3l22.4-42.1v28h11.2L189.1-126.8z" />
|
||||
<g>
|
||||
<g>
|
||||
<path class="st7"
|
||||
d="M67.6,120.6L65.7,104l-2.9,0.3l1.9,16.6L67.6,120.6z M77.9,119.4l-1.9-16.6l-2.9,0.3l1.9,16.6L77.9,119.4z" />
|
||||
<path class="st8" d="M83.3,142c-0.9,1.1-1.6,1.8-1.7,1.9c-2.6,2.1-4.7,2.7-6.4,1.9c-3-1.5-2.8-7.1-2.7-7.7l2.1,0.1
|
||||
c-0.1,1.6,0.2,5,1.6,5.7c0.8,0.4,2.2-0.1,4-1.6l0,0c0,0,5.8-5.8,4.6-10.4c-1.4-5.5,5-13.4,7.1-16.1l0.3-0.3l1.7,1.3l-0.3,0.4
|
||||
c-6.5,8-7.2,12.1-6.7,14.2C87.9,135.4,85.2,139.7,83.3,142z" />
|
||||
<path class="st8" d="M61.2,120.4l0.8,6.8l6.3,4.2l8.5-0.9l5.2-5.5l-0.8-6.8L61.2,120.4z" />
|
||||
<g>
|
||||
<path class="st9" d="M76.7,130.5l-8.5,0.9l1.8,7.5l6.7-0.8L76.7,130.5L76.7,130.5z M82.8,112.5l0.7,6.2l-24.4,2.8l-0.7-6.2
|
||||
L82.8,112.5z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st10" d="M101.9,44.1c-17.5,0-31.7,14.2-31.7,31.7c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7
|
||||
s1.8-0.7,1.9-1.7c3.2-34.1,29.9-46.6,29.9-70.5C133.6,58.2,119.4,44.1,101.9,44.1z" />
|
||||
<path class="st11" d="M101.9,44.8c17.4,0,31.5,14,31.7,31.3c0-0.1,0-0.2,0-0.3c0-17.5-14.2-31.7-31.7-31.7S70.2,58.2,70.2,75.8
|
||||
c0,0.1,0,0.2,0,0.3C70.4,58.8,84.5,44.8,101.9,44.8L101.9,44.8z" />
|
||||
<path class="st12" d="M103.8,145.5c-0.1,1-0.9,1.7-1.9,1.7s-1.8-0.7-1.9-1.7c-3.1-34-29.6-46.5-29.8-70.1c0,0.2,0,0.3,0,0.5
|
||||
c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7s1.8-0.7,1.9-1.7c3.2-34.1,29.9-46.6,29.9-70.5c0-0.2,0-0.3,0-0.5
|
||||
C133.4,99,106.9,111.5,103.8,145.5L103.8,145.5z" />
|
||||
</g>
|
||||
<path class="st13"
|
||||
d="M94.6,60.3v19.2h5.2v15.7l12.2-21h-7l7-14C112.1,60.3,94.6,60.3,94.6,60.3z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 75.4 104" style="enable-background:new 0 0 75.4 104;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFB300;}
|
||||
.st1{fill:#90A4AE;}
|
||||
.st2{fill:#546E7A;}
|
||||
.st3{fill:#00E676;}
|
||||
.st4{fill:#FFFFFF;fill-opacity:0.2;}
|
||||
.st5{fill:#3E2723;fill-opacity:0.2;}
|
||||
.st6{opacity:0.45;enable-background:new ;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0"
|
||||
d="M9.2,76.5L7.3,59.9l-2.9,0.3l1.9,16.6L9.2,76.5z M19.5,75.3l-1.9-16.6L14.7,59l1.9,16.6L19.5,75.3z" />
|
||||
<path class="st1" d="M24.9,97.9c-0.9,1.1-1.6,1.8-1.7,1.9c-2.6,2.1-4.7,2.7-6.4,1.9c-3-1.5-2.8-7.1-2.7-7.7l2.1,0.1
|
||||
c-0.1,1.6,0.2,5,1.6,5.7c0.8,0.4,2.2-0.1,4-1.6l0,0c0,0,5.8-5.8,4.6-10.4c-1.4-5.5,5-13.4,7.1-16.1l0.3-0.3l1.7,1.3l-0.3,0.4
|
||||
c-6.5,8-7.2,12.1-6.7,14.2C29.5,91.3,26.8,95.6,24.9,97.9z" />
|
||||
<path class="st1" d="M2.8,76.3l0.8,6.8l6.3,4.2l8.5-0.9l5.2-5.5l-0.8-6.8L2.8,76.3z" />
|
||||
<g>
|
||||
<path class="st2"
|
||||
d="M18.3,86.4l-8.5,0.9l1.8,7.5l6.7-0.8V86.4L18.3,86.4z M24.4,68.4l0.7,6.2L0.7,77.4L0,71.2L24.4,68.4z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st3" d="M43.5,0C26,0,11.8,14.2,11.8,31.7c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7s1.8-0.7,1.9-1.7
|
||||
c3.2-34.1,29.9-46.6,29.9-70.5C75.2,14.1,61,0,43.5,0z" />
|
||||
<path class="st4" d="M43.5,0.7c17.4,0,31.5,14,31.7,31.3c0-0.1,0-0.2,0-0.3C75.2,14.2,61,0,43.5,0S11.8,14.1,11.8,31.7
|
||||
c0,0.1,0,0.2,0,0.3C12,14.7,26.1,0.7,43.5,0.7L43.5,0.7z" />
|
||||
<path class="st5" d="M45.4,101.4c-0.1,1-0.9,1.7-1.9,1.7s-1.8-0.7-1.9-1.7c-3.1-34-29.6-46.5-29.8-70.1c0,0.2,0,0.3,0,0.5
|
||||
c0,23.9,26.7,36.4,29.9,70.5c0.1,1,0.9,1.7,1.9,1.7s1.8-0.7,1.9-1.7c3.2-34.1,29.9-46.6,29.9-70.5c0-0.2,0-0.3,0-0.5
|
||||
C75,54.9,48.5,67.4,45.4,101.4L45.4,101.4z" />
|
||||
</g>
|
||||
<path class="st6"
|
||||
d="M36.2,16.2v19.2h5.2v15.7l12.2-21h-7l7-14C53.7,16.2,36.2,16.2,36.2,16.2z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1,429 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 360 200" style="enable-background:new 0 0 360 200;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#A5A4AA;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
.st2{fill:#454B55;}
|
||||
.st3{fill:#7CA5C7;}
|
||||
.st4{fill:#7CA4C5;}
|
||||
.st5{fill:#86CBC4;}
|
||||
.st6{fill:#039F96;}
|
||||
.st7{fill:#3F4B55;}
|
||||
.st8{fill:#444C55;}
|
||||
.st9{fill:#E5E5E4;}
|
||||
.st10{fill:#98BED8;}
|
||||
.st11{fill:#EC6678;}
|
||||
.st12{fill:#D95C6C;}
|
||||
.st13{fill:#E9EEF4;}
|
||||
.st14{fill:#687078;}
|
||||
.st15{fill:#EF7879;}
|
||||
.st16{opacity:0.2;fill:#020203;enable-background:new ;}
|
||||
.st17{fill:#C1D777;}
|
||||
.st18{fill:#C7C7C6;}
|
||||
.st19{fill:#93CEBF;}
|
||||
.st20{fill:#FFDE78;}
|
||||
.st21{fill:#EDA975;}
|
||||
.st22{fill:#F5EAA1;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M47.7,59.7l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
|
||||
<path class="st1"
|
||||
d="M51.1,130.2L13.4,92.5c-2.1-2.1-2.1-5.4,0-7.5L32,66.3c2-2,5.4-2,7.5,0L77.2,104" />
|
||||
<path class="st1" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C47.4,83.2,47.3,84.9,46.3,85.9z" />
|
||||
<path class="st2" d="M78.5,105.3c-0.7,0.7-1.9,0.7-2.6,0L38.2,67.7c-1.3-1.3-3.6-1.4-4.9,0L14.7,86.3c-1.3,1.3-1.3,3.5,0,4.9
|
||||
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0L12.1,93.8c-2.8-2.8-2.8-7.3,0-10.1L30.7,65c2.8-2.8,7.3-2.8,10.1,0
|
||||
l37.7,37.7C79.2,103.5,79.2,104.6,78.5,105.3z" />
|
||||
<path class="st3" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C47.4,83.2,47.3,84.9,46.3,85.9z" />
|
||||
<path class="st2" d="M47.6,87.2l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
|
||||
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C49.4,82.6,49.4,85.4,47.6,87.2z M21.8,88.2c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7c0.3,0.3,0.8,0.3,1.1,0
|
||||
L45,84.6c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L21.8,88.2z M82.2,101.6l-33.5,33.5c-0.7,0.7-1.9,0.7-2.6,0
|
||||
c-0.7-0.7-0.7-1.9,0-2.6L79.6,99c0.7-0.7,1.9-0.7,2.6,0S82.9,100.9,82.2,101.6z M80.3,95.9c-1.7,1.7-4.6,1.7-6.3,0L58.9,80.9
|
||||
c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1
|
||||
c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1L57,71.5c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l20.7,20.7C82,91.3,82,94.2,80.3,95.9z
|
||||
" />
|
||||
<path class="st2" d="M61.4,69.7l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6L46.4,61
|
||||
c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C62,67.8,62.1,69,61.4,69.7z M56.4,69.5
|
||||
l1.1-1.1l-4.9-4.9l-1.1,1.1L56.4,69.5z" />
|
||||
<g>
|
||||
<path class="st4" d="M57.6,114.1c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
|
||||
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
|
||||
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C58.2,113.1,58,113.7,57.6,114.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M29.9,146.1l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C27.4,145.1,28.9,145.1,29.9,146.1z M11.5,152.4l7.1-7.1l4.2,4.2l-7.1,7.1L11.5,152.4z" />
|
||||
<path class="st5" d="M37,153.1l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C34.4,152.1,36,152.1,37,153.1z" />
|
||||
<path class="st6" d="M50.4,170.1L50.4,170.1c1-1,1-2.5,0-3.5L37,153.1c-1-1-2.5-1-3.5,0l-1.8,1.8l15.2,15.2
|
||||
C47.9,171.1,49.4,171.1,50.4,170.1z" />
|
||||
<path class="st7" d="M55,174.7l-14.1,14.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7L25.3,145c1.6-1.6,4.1-1.6,5.7,0
|
||||
l24,24C56.6,170.6,56.6,173.1,55,174.7z M13.3,161.3c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0l14.1-14.1
|
||||
c0.4-0.4,0.4-1,0-1.4l-24-24c-0.4-0.4-1-0.4-1.4,0L13.3,161.3z" />
|
||||
<path class="st1" d="M39.1,183.5c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
|
||||
C39.7,182,39.7,182.9,39.1,183.5z M25,169.4c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C25.4,168.7,25.2,169.1,25,169.4z" />
|
||||
<path class="st7" d="M23.9,150.6c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-1.4-1.4
|
||||
c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C24.5,149.1,24.5,150,23.9,150.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M117.2,186.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
|
||||
C137.2,161.8,124.5,179.6,117.2,186.9z" />
|
||||
<path class="st5" d="M103.2,148.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8l-15.4,2.4
|
||||
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C102.8,148.9,102.9,148.5,103.2,148.4z" />
|
||||
<path class="st8" d="M131.7,166.8l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-2.8,2.8
|
||||
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1S132.2,167.3,131.7,166.8z" />
|
||||
<path class="st8" d="M115.1,189l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
|
||||
c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C116.6,189.6,115.6,189.6,115.1,189z
|
||||
M82.9,152.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C107.6,127.9,82.9,152.6,82.9,152.6z" />
|
||||
<path class="st8" d="M115.1,189l-36.8-36.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
|
||||
C116.6,189.6,115.6,189.6,115.1,189z" />
|
||||
<path class="st1" d="M88.3,156.3c-1.9-1.9-4.9-1.9-6.8,0s-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0S90.2,158.1,88.3,156.3
|
||||
C88.3,156.3,88.3,156.3,88.3,156.3z M110.9,178.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0
|
||||
c1.9,1.9,4.9,1.9,6.8,0C112.8,183.8,112.8,180.8,110.9,178.9C110.9,178.9,110.9,178.9,110.9,178.9z" />
|
||||
<path class="st8" d="M80.4,164.1c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9
|
||||
C86.9,166.6,82.9,166.6,80.4,164.1z M87.2,157.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
|
||||
C88.5,160.7,88.5,158.6,87.2,157.3z" />
|
||||
<g>
|
||||
<path class="st8" d="M103.1,186.7c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
|
||||
C109.5,189.2,105.5,189.2,103.1,186.7z M109.9,180c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
|
||||
S111.2,181.3,109.9,180z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st8" d="M103.1,186.7L73.4,157c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
|
||||
C104.6,187.3,103.7,187.3,103.1,186.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st8" d="M68.4,152.1c-0.1-0.1-0.1-0.1-0.2-0.2s-0.1-0.2-0.1-0.3s-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2s0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
|
||||
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1c0.1,0,0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3s-0.1,0.2-0.1,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1c-0.1,0-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0
|
||||
c-0.1,0-0.2,0-0.3,0s-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C68.6,152.3,68.5,152.2,68.4,152.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M336.4,33.8l-13.6,13.6c-1.9,1.9-5.1,1.9-7.1,0l-13.8-13.8c-1.9-1.9-1.9-5.1,0-7.1L315.5,13
|
||||
c1.9-1.9,5.1-1.9,7.1,0l13.8,13.8C338.3,28.7,338.3,31.9,336.4,33.8z" />
|
||||
<path class="st2" d="M337.4,34.9l-13.6,13.6c-2.5,2.5-6.6,2.5-9.2,0l-13.8-13.8c-2.5-2.5-2.5-6.6,0-9.2l13.6-13.6
|
||||
c2.5-2.5,6.6-2.5,9.2,0l13.8,13.8C340,28.2,340,32.3,337.4,34.9z M303,27.6c-1.4,1.4-1.4,3.6,0,4.9l13.8,13.8
|
||||
c1.4,1.4,3.6,1.4,4.9,0l13.6-13.6c1.4-1.4,1.4-3.6,0-4.9L321.5,14c-1.4-1.4-3.6-1.4-4.9,0L303,27.6z" />
|
||||
<path class="st9" d="M325.4,36.4L325.4,36.4c-3.4,3.4-9,3.4-12.4,0l-0.1-0.1c-3.4-3.4-3.4-9,0-12.4l0,0c3.4-3.4,9-3.4,12.4,0
|
||||
l0.1,0.1C328.8,27.5,328.8,33,325.4,36.4z" />
|
||||
<path class="st2" d="M326.5,37.5c-4,4-10.5,4-14.5,0l-0.1-0.1c-4-4-4-10.5,0-14.5s10.5-4,14.5,0l0.1,0.1
|
||||
C330.5,27,330.5,33.5,326.5,37.5z M314,25c-2.8,2.8-2.8,7.4,0,10.3l0.1,0.1c2.8,2.8,7.4,2.8,10.3,0c2.8-2.8,2.8-7.4,0-10.3
|
||||
l-0.1-0.1C321.4,22.2,316.8,22.2,314,25z" />
|
||||
<path class="st2" d="M315.3,31.5c-0.7,0.7-0.7,1.8,0,2.5c0.7,0.7,1.8,0.7,2.5,0c0,0,0,0,0,0c0.7-0.7,0.7-1.8,0-2.5
|
||||
C317.2,30.8,316,30.8,315.3,31.5C315.3,31.5,315.3,31.5,315.3,31.5z M320.4,26.4c-0.7,0.7-0.7,1.8,0,2.5s1.8,0.7,2.5,0c0,0,0,0,0,0
|
||||
c0.7-0.7,0.7-1.8,0-2.5C322.3,25.7,321.1,25.7,320.4,26.4C320.4,26.4,320.4,26.4,320.4,26.4z" />
|
||||
<path class="st1"
|
||||
d="M341.7,43.3l3.8,3.8c2.6,2.6,2.6,6.8,0,9.4l0,0c-2.6,2.6-6.8,2.6-9.4,0l-3.8-3.8L341.7,43.3z" />
|
||||
<path class="st2" d="M346.6,57.6c-3.2,3.2-8.3,3.2-11.5,0l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
|
||||
c2,2,5.3,2,7.3,0c2-2,2-5.3,0-7.3l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
|
||||
C349.8,49.3,349.8,54.4,346.6,57.6z" />
|
||||
<path class="st2" d="M335.1,51.8c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.3,3.3
|
||||
C335.7,50.3,335.7,51.2,335.1,51.8z M340.8,46.2c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l3.3,3.3
|
||||
C341.4,44.6,341.4,45.6,340.8,46.2z" />
|
||||
<path class="st2" d="M344.1,43.1l-12,12c-0.6,0.6-1.5,0.6-2.1,0s-0.6-1.5,0-2.1l12-12c0.6-0.6,1.5-0.6,2.1,0S344.7,42.5,344.1,43.1
|
||||
z M349.6,60.6c-0.6,0.6-1.5,0.6-2.1,0l-3-3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3,3C350.1,59,350.2,60,349.6,60.6z" />
|
||||
<g>
|
||||
<path class="st10" d="M339.8,50.8c-0.5,0.5-0.5,1.4,0,1.9c0.5,0.5,1.4,0.5,1.9,0c0,0,0,0,0,0c0.5-0.5,0.5-1.4,0-1.9
|
||||
C341.2,50.3,340.3,50.3,339.8,50.8C339.8,50.8,339.8,50.8,339.8,50.8z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st11" d="M171,149l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
|
||||
C186.3,145.2,179.1,145.5,171,149" />
|
||||
<path class="st12" d="M182,146.6C181.9,146.6,181.9,146.6,182,146.6c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
|
||||
C185.6,156.3,185.6,150.3,182,146.6 M210.7,175.6c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C210.2,178.4,210.7,177,210.7,175.6" />
|
||||
<path class="st2" d="M204.3,184.4L170,150c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
|
||||
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
|
||||
l-12.1-12.1c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3l11.6,11.6
|
||||
c2.7,2.7,2.7,7.1,0,9.8l-3.8,3.8C205.8,185,204.9,184.9,204.3,184.4z" />
|
||||
<path class="st1" d="M179.1,149.4c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
C181.3,155,181.3,151.6,179.1,149.4C179.1,149.4,179.1,149.4,179.1,149.4z M198.9,164.9c-1.2-1.2-3.1-1.2-4.2,0s-1.2,3.1,0,4.2
|
||||
s3.1,1.2,4.2,0C200.1,168,200.1,166.1,198.9,164.9z" />
|
||||
<path class="st2" d="M170.4,158.1c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
|
||||
C177.5,160.8,173.1,160.8,170.4,158.1z M178.1,150.5c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5s4,1.5,5.5,0
|
||||
C179.6,154.5,179.6,152,178.1,150.5z" />
|
||||
<path class="st1" d="M209,179.3c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
C211.1,184.8,211.1,181.4,209,179.3C209,179.3,209,179.3,209,179.3z" />
|
||||
<path class="st2" d="M200.3,188c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
|
||||
C207.4,190.7,203,190.7,200.3,188z M207.9,180.3c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0
|
||||
C209.4,184.3,209.4,181.9,207.9,180.3z" />
|
||||
<path class="st2" d="M200.3,188l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
|
||||
C201.8,188.6,200.9,188.6,200.3,188z M161.6,149.3c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1c0.3-0.3,0.7-0.5,1.1-0.4
|
||||
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C162.2,149.7,161.9,149.6,161.6,149.3z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1"
|
||||
d="M107.9,26.1L95.6,13.7c-2.7-2.7-7-2.9-9.9-0.6l-9,7.1l24.7,24.7l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
|
||||
<path class="st13"
|
||||
d="M107.9,26.1l-6.2-6.2L89,32.6l12.4,12.4l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
|
||||
<path class="st14" d="M78.7,18.1l24.7,24.7l-12,12L66.7,30.1L78.7,18.1z" />
|
||||
<path class="st8" d="M91.1,30.4l12.4,12.4l-12,12L79.1,42.5L91.1,30.4z" />
|
||||
<path class="st14" d="M76.6,20.2L76.6,20.2c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4l7.1,7.1l6.4-6.4C78.6,25.3,78.6,22.1,76.6,20.2z
|
||||
M95,58.4l6.4-6.4c1.9-1.9,1.9-5.1,0-7.1l0,0c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4" />
|
||||
<path class="st8" d="M69.2,34.7c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9c-1.4-1.4-3.6-1.4-4.9,0l-6.4,6.4
|
||||
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0s2.5,6.6,0,9.2l-6.4,6.4
|
||||
C70.7,35.3,69.8,35.3,69.2,34.7z M93.9,59.4c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9s-3.6-1.4-4.9,0L89,52.4
|
||||
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0c2.5,2.5,2.5,6.6,0,9.2l-6.4,6.4
|
||||
C95.5,60,94.5,60,93.9,59.4z" />
|
||||
<path class="st8" d="M101.8,43.8c0,0-0.1-0.1-0.1-0.1C95,34.6,87,26.6,78,19.9c-0.6-0.5-0.8-1.4-0.3-2.1c0.5-0.6,1.4-0.8,2.1-0.3
|
||||
c9.3,6.9,17.5,15.1,24.4,24.4c0.5,0.6,0.4,1.6-0.3,2.1C103.2,44.3,102.4,44.3,101.8,43.8z" />
|
||||
<path class="st8" d="M75.6,21.2c0,0-0.1-0.1-0.1-0.1c-0.5-0.6-0.4-1.6,0.2-2.1l9-7.1c3.6-2.8,8.7-2.5,12,0.7L109,25
|
||||
c3.3,3.3,3.5,8.4,0.7,12l-7.1,8.9c-0.5,0.6-1.4,0.7-2.1,0.2c-0.7-0.5-0.7-1.4-0.2-2.1l7.1-8.9c1.9-2.4,1.7-5.8-0.4-8L94.5,14.7
|
||||
c-2.2-2.2-5.6-2.4-8-0.5l-9,7.1C76.9,21.9,76.1,21.8,75.6,21.2z M92.9,56.3l-4.9,4.9l-2.8-2.8l4.9-4.9 M68.1,31.5l-4.9,4.9
|
||||
l-2.8-2.8l4.9-4.9" />
|
||||
<path class="st8" d="M86.9,62.3L84,59.4c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9
|
||||
l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L89,62.3C88.4,62.9,87.5,62.9,86.9,62.3z M62.1,37.5l-2.8-2.8
|
||||
c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0
|
||||
c0.6,0.6,0.6,1.5,0,2.1l-4.9,4.9C63.6,38.1,62.7,38.1,62.1,37.5z" />
|
||||
<path class="st8" d="M95,60.5L61.1,26.6c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l33.9,33.9c0.6,0.6,0.6,1.5,0,2.1
|
||||
C96.5,61.1,95.6,61.1,95,60.5z" />
|
||||
<path class="st1" d="M84.2,43.3l-5.9-5.9c-0.6-0.6-0.6-1.5,0-2.1l3.5-3.5c2.2-2.2,5.8-2.2,8.1,0l0,0c2.2,2.2,2.2,5.8,0,8.1
|
||||
l-3.5,3.5C85.7,43.9,84.8,43.9,84.2,43.3z" />
|
||||
<path class="st8" d="M106.3,22.3l-1.1,1.1l-7.1-7.1l1.1-1.1l0,0c1.2-1.2,3.1-1.2,4.2,0l2.8,2.8C107.5,19.2,107.5,21.1,106.3,22.3
|
||||
L106.3,22.3z" />
|
||||
<path class="st1" d="M74.9,22c-1-1-2.6-1-3.5,0c-1,1-1,2.6,0,3.5c1,1,2.6,1,3.5,0C75.8,24.5,75.8,22.9,74.9,22z M99.6,46.7
|
||||
c-1-1-2.6-1-3.5,0s-1,2.6,0,3.5s2.6,1,3.5,0S100.6,47.7,99.6,46.7z" />
|
||||
<path class="st8" d="M89,64.4L59.4,34.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.6,29.6c0.6,0.6,0.6,1.5,0,2.1
|
||||
C90.5,65,89.6,65,89,64.4z M96.4,67.6c-0.6-0.6-1.5-0.6-2.1,0c-0.6,0.6-0.6,1.5,0,2.1s1.5,0.6,2.1,0C97,69.1,97,68.2,96.4,67.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M139.1,66.4l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C136.5,65.4,138.1,65.4,139.1,66.4z M120.7,72.7l7.1-7.1l4.2,4.2l-7.1,7.1L120.7,72.7z" />
|
||||
<path class="st5" d="M146.1,73.4l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C143.6,72.4,145.1,72.4,146.1,73.4z" />
|
||||
<path class="st6" d="M159.6,90.4L159.6,90.4c1-1,1-2.5,0-3.5l-13.4-13.4c-1-1-2.5-1-3.5,0l-1.8,1.8L156,90.4
|
||||
C157,91.4,158.6,91.4,159.6,90.4z" />
|
||||
<path class="st7" d="M164.2,95L150,109.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7l14.1-14.1c1.6-1.6,4.1-1.6,5.7,0
|
||||
l24,24C165.7,90.9,165.7,93.4,164.2,95z M122.4,81.6c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0L162,92.9c0.4-0.4,0.4-1,0-1.4
|
||||
l-24-24c-0.4-0.4-1-0.4-1.4,0L122.4,81.6z" />
|
||||
<path class="st1" d="M148.2,103.8c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
|
||||
C148.8,102.3,148.8,103.2,148.2,103.8z M134.1,89.7c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C134.6,89,134.4,89.4,134.1,89.7z" />
|
||||
<path class="st7" d="M133,71c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0
|
||||
l-1.4-1.4c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C133.6,69.4,133.6,70.4,133,71z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st0" d="M315,113.9l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
|
||||
<path class="st1"
|
||||
d="M318.3,184.5l-37.7-37.7c-2.1-2.1-2.1-5.4,0-7.5l18.7-18.7c2-2,5.4-2,7.5,0l37.7,37.7" />
|
||||
<path class="st1" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C314.6,137.5,314.6,139.2,313.6,140.2z" />
|
||||
<path class="st2" d="M345.7,159.6c-0.7,0.7-1.9,0.7-2.6,0L305.5,122c-1.3-1.3-3.6-1.4-4.9,0l-18.7,18.7c-1.3,1.3-1.3,3.5,0,4.9
|
||||
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0l-37.7-37.7c-2.8-2.8-2.8-7.3,0-10.1l18.7-18.7c2.8-2.8,7.3-2.8,10.1,0
|
||||
l37.7,37.7C346.5,157.8,346.4,158.9,345.7,159.6z" />
|
||||
<path class="st3" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C314.6,137.5,314.6,139.2,313.6,140.2z" />
|
||||
<path class="st2" d="M314.9,141.5l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
|
||||
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C316.6,136.9,316.6,139.7,314.9,141.5z M289.1,142.5c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7
|
||||
c0.3,0.3,0.8,0.3,1.1,0l13.4-13.4c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L289.1,142.5z M349.5,155.9l-33.5,33.5
|
||||
c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l33.5-33.5c0.7-0.7,1.9-0.7,2.6,0C350.2,154,350.2,155.2,349.5,155.9z M347.5,150.2
|
||||
c-1.7,1.7-4.6,1.7-6.3,0l-15.1-15.1c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9
|
||||
c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-20.7-20.7c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0
|
||||
l20.7,20.7C349.2,145.6,349.2,148.5,347.5,150.2z" />
|
||||
<path class="st2" d="M328.6,124l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6l-1.7-1.7
|
||||
c-0.7-0.7-0.7-1.9,0-2.6c0.7-0.7,1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C329.3,122.1,329.3,123.3,328.6,124z
|
||||
M323.6,123.8l1.1-1.1l-4.9-4.9l-1.1,1.1L323.6,123.8z" />
|
||||
<g>
|
||||
<path class="st4" d="M324.8,168.4c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
|
||||
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
|
||||
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C325.4,167.4,325.3,168,324.8,168.4z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st15"
|
||||
d="M233.8,171.2l15.3,15.3l15.9-26.2c0.3-0.5,0.3-1.1-0.2-1.6l-3.3-3.3c-0.4-0.4-1-0.5-1.6-0.2L233.8,171.2z" />
|
||||
<path class="st1"
|
||||
d="M244.5,164.7l-6.2,3.8l13.5,13.5l0,0l3.8-6.2L244.5,164.7z M256.1,157.6l-6.2,3.8l9,9l3.8-6.2L256.1,157.6z" />
|
||||
<path class="st16"
|
||||
d="M263.6,157.4l1.3,1.3c0.4,0.4,0.5,1,0.2,1.6l-16,26.2l-3.5-3.5l18.2-24C264.1,158.4,264,157.8,263.6,157.4z" />
|
||||
<g>
|
||||
<path class="st2" d="M248.4,187.2l-15.3-15.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.3,0.2-0.6,0.5-0.7l26.2-15.9c0.9-0.5,2-0.4,2.8,0.3
|
||||
l3.3,3.3c0.7,0.7,0.9,1.9,0.3,2.8L249.9,187c-0.2,0.3-0.4,0.4-0.7,0.5C248.9,187.5,248.6,187.4,248.4,187.2z M235.4,171.4
|
||||
l13.5,13.5l15.3-25.2c0.1-0.1,0.1-0.3,0-0.3l-3.3-3.3c-0.1-0.1-0.2-0.1-0.3,0L235.4,171.4z" />
|
||||
<path class="st2" d="M230.5,169.3c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l20.4,20.4l0,0c0.4,0.4,0.4,1,0,1.4c-0.4,0.4-1,0.4-1.4,0
|
||||
L230.5,169.3L230.5,169.3z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st17" d="M184,108.7L196.6,96c3.9-3.9,10.3-3.9,14.1,0l22.6,22.6c2.7,2.7,3.6,6.7,2.5,10.3l-0.4,1.2
|
||||
c-2.2,6.4-6.2,12.1-11.5,16.4l-2,1.7c-1.1,0.9-2.5-0.7-1.5-1.7l0,0" />
|
||||
<path class="st1" d="M226,144.4l0.1,0.1l0,0c1.2-1.2,2.4-2.6,3.5-3.9c0,0,0.1-0.1,0.1-0.1c0.2-0.2,0.4-0.5,0.5-0.7
|
||||
c0.1-0.1,0.1-0.1,0.1-0.2c0.2-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.2,0.2-0.2c0.1-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.1,0.1-0.2
|
||||
c0.3-0.5,0.6-1,0.9-1.4c0,0,0.1-0.1,0.1-0.2c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.2,0.2-0.3,0.3-0.5c0.1-0.2,0.2-0.3,0.2-0.5
|
||||
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7
|
||||
c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2c1.2-3.6,0.2-7.6-2.5-10.3l-22.5-22.5c-3.9-3.9-10.3-3.9-14.1,0l-2.8,2.8
|
||||
l30.8,30.8C228.5,133.6,229.1,139.7,226,144.4z" />
|
||||
<path class="st18" d="M209.2,110.1l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5l-0.7,0.7
|
||||
C211.7,111.1,210.2,111,209.2,110.1z M218,118.9l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5
|
||||
l-0.7,0.7C220.6,119.9,219,119.9,218,118.9z M233.5,134.4c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5
|
||||
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2
|
||||
c0.1-0.2,0.1-0.4,0.2-0.7l-8.2-8.2c-1-1-2.5-1-3.5,0l-0.8,0.8c-1,1-1,2.5,0,3.5L233.5,134.4L233.5,134.4z" />
|
||||
<path class="st2" d="M219.3,149.2c0,0-0.1-0.1-0.1-0.1c-1-1-0.9-2.6,0.1-3.6c0.6-0.6,1.5-0.6,2.1,0c0.2,0.2,0.4,0.5,0.4,0.8
|
||||
l1.1-0.9c5.1-4.1,8.8-9.6,11-15.8l0.4-1.2c1-3.1,0.2-6.4-2.1-8.7l-22.6-22.6c-3.3-3.3-8.7-3.3-12,0L185,109.7
|
||||
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1L195.5,95c4.5-4.5,11.8-4.5,16.3,0l22.6,22.6c3.1,3.1,4.2,7.6,2.8,11.8l-0.4,1.2
|
||||
c-2.3,6.7-6.4,12.6-11.9,17.1l-2,1.7C221.7,150.2,220.3,150.1,219.3,149.2z M226.7,148.6c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4
|
||||
c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-6.4,6.4C228.3,149.2,227.3,149.2,226.7,148.6z" />
|
||||
<path class="st2" d="M210.8,139.1l-16.5-16.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0L213,137c0.6,0.6,0.6,1.5,0,2.1
|
||||
C212.4,139.7,211.4,139.7,210.8,139.1z M186.8,115l-5.4-5.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l5.4,5.4
|
||||
c0.6,0.6,0.6,1.5,0,2.1C188.3,115.6,187.4,115.6,186.8,115z M236.2,117.3l-23.4-23.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0
|
||||
l23.4,23.4c0.6,0.6,0.6,1.5,0,2.1C237.7,117.9,236.8,117.9,236.2,117.3z M225.1,145.6c-0.6-0.6-0.6-1.4-0.1-2.1
|
||||
c2.7-3.1,1.9-9.3-1.6-12.8l-30.8-30.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l30.8,30.8c4.6,4.6,5.4,12.6,1.7,16.9
|
||||
C226.7,146.1,225.8,146.2,225.1,145.6C225.1,145.6,225.1,145.6,225.1,145.6z M198.7,98.9l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
|
||||
s1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C200.3,99.5,199.3,99.5,198.7,98.9z M203,97.5l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
|
||||
c0.6-0.6,1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C204.5,98.1,203.6,98.1,203,97.5z" />
|
||||
<path class="st1" d="M219.2,138.5c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
|
||||
C221,143.4,221,140.4,219.2,138.5C219.2,138.5,219.2,138.5,219.2,138.5z" />
|
||||
<path class="st2" d="M211.3,146.4c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.5,6.5,0,8.9
|
||||
C217.8,148.9,213.8,148.8,211.3,146.4z M218.1,139.6c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
|
||||
C219.4,143,219.4,140.9,218.1,139.6z" />
|
||||
<path class="st1" d="M188.2,114.3c-1.9,1.9-1.9,4.9,0,6.8c1.9,1.9,4.9,1.9,6.8,0c0.8-0.8,1.3-1.8,1.4-2.9l-5.3-5.3
|
||||
C190,113.1,189,113.5,188.2,114.3z" />
|
||||
<path class="st2" d="M187.2,122.2c-2.5-2.5-2.4-6.5,0-8.9c1-1,2.3-1.6,3.8-1.8c0.5,0,0.9,0.1,1.2,0.4l5.3,5.3
|
||||
c0.3,0.3,0.5,0.8,0.4,1.2c-0.1,1.4-0.8,2.8-1.8,3.8C193.6,124.7,189.6,124.6,187.2,122.2z M190.6,114.6c-0.5,0.1-0.9,0.4-1.3,0.8
|
||||
c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0c0.4-0.4,0.7-0.8,0.8-1.3L190.6,114.6z" />
|
||||
<g>
|
||||
<path class="st2" d="M217.7,98.1c-0.2-0.2-0.4-0.6-0.4-1l-0.2-5.2c0-0.8,0.6-1.5,1.4-1.6c0.8,0,1.5,0.6,1.6,1.4l0.2,5.2
|
||||
c0,0.8-0.6,1.5-1.4,1.6C218.4,98.5,218,98.3,217.7,98.1z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M219.3,147.6l-0.9-0.9c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l0.9,0.9c0.6,0.6,0.6,1.5,0,2.1
|
||||
C220.8,148.2,219.9,148.2,219.3,147.6z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M194.9,72.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
|
||||
C214.9,47.8,202.2,65.6,194.9,72.9z" />
|
||||
<path class="st5" d="M181,34.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8L176,44.4
|
||||
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C180.5,34.9,180.6,34.5,181,34.4z" />
|
||||
<path class="st8" d="M209.4,52.7l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0s0.6,1.5,0,2.1l-2.8,2.8
|
||||
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1C210.9,53.3,210,53.3,209.4,52.7z" />
|
||||
<path class="st8" d="M192.8,75l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
|
||||
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C194.3,75.6,193.3,75.6,192.8,75z
|
||||
M160.6,38.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C185.3,13.9,160.6,38.6,160.6,38.6z" />
|
||||
<path class="st8" d="M192.8,75L156,38.2c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
|
||||
C194.3,75.6,193.3,75.6,192.8,75z" />
|
||||
<path class="st1" d="M166,42.2c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
|
||||
C167.9,47.2,167.9,44.1,166,42.2C166,42.2,166,42.2,166,42.2z M188.6,64.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8
|
||||
c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0C190.5,69.8,190.5,66.7,188.6,64.9C188.6,64.9,188.6,64.9,188.6,64.9z" />
|
||||
<path class="st8" d="M158.2,50.1c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9C164.6,52.5,160.6,52.6,158.2,50.1z
|
||||
M164.9,43.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0C166.2,46.7,166.3,44.6,164.9,43.3z" />
|
||||
<g>
|
||||
<path class="st8" d="M180.8,72.7c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
|
||||
C187.3,75.2,183.3,75.2,180.8,72.7z M187.6,65.9c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7s3.4,1.3,4.7,0
|
||||
C188.8,69.3,188.9,67.2,187.6,65.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st8" d="M180.8,72.7L151.1,43c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
|
||||
C182.3,73.3,181.4,73.3,180.8,72.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st8" d="M146.1,38.1c-0.1-0.1-0.1-0.1-0.2-0.2c0-0.1-0.1-0.2-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2c0.1-0.1,0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
|
||||
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1s0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.1,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1s-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0
|
||||
c-0.1,0-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C146.3,38.3,146.2,38.1,146.1,38.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<path class="st2"
|
||||
d="M30.2,31.2l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8L26.8,36l-0.6-0.6" />
|
||||
<path class="st2" d="M32.6,35L29.5,32c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S33,35.4,32.6,35z M37.7,23.8
|
||||
l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8l-0.3-0.2l-0.6-0.6" />
|
||||
<path class="st2" d="M40,27.6L37,24.5c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S40.5,28,40,27.6z M20.8,21.8
|
||||
l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
|
||||
<path class="st2" d="M20.1,22.5L17,19.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S20.4,22.9,20.1,22.5z
|
||||
M28.2,14.3l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
|
||||
<path class="st2"
|
||||
d="M27.5,15L24.4,12c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4C28.6,15.5,27.9,15.4,27.5,15z" />
|
||||
<g>
|
||||
<path class="st1" d="M20.3,36.7L15.7,32c-1.3-1.3-1.3-3.5,0-4.8l13.2-13.2c1.3-1.3,3.5-1.3,4.8,0l4.7,4.7c1.3,1.3,1.3,3.5,0,4.8
|
||||
L25.1,36.7C23.8,38,21.7,38,20.3,36.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st15" d="M33.6,18.5c-1.5-1.5-3.9-1.5-5.3,0c-1.5,1.5-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0
|
||||
S35,19.9,33.6,18.5C33.6,18.5,33.6,18.5,33.6,18.5z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st19" d="M26,26c-1.5-1.5-3.9-1.5-5.3,0s-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0C27.5,29.9,27.5,27.5,26,26
|
||||
C26,26,26,26,26,26z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M19.6,37.4l-4.7-4.7c-1.7-1.7-1.7-4.5,0-6.2l13.2-13.2c1.7-1.7,4.5-1.7,6.2,0l4.7,4.7c1.7,1.7,1.7,4.5,0,6.2
|
||||
L25.9,37.4C24.1,39.1,21.4,39.1,19.6,37.4z M33,14.7c-0.9-0.9-2.4-0.9-3.4,0L16.4,27.9c-0.9,0.9-0.9,2.4,0,3.4l4.7,4.7
|
||||
c0.9,0.9,2.4,0.9,3.4,0l13.2-13.2c0.9-0.9,0.9-2.4,0-3.4L33,14.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M10.3,41.7c-0.4-0.4-0.4-1,0-1.4l6.7-6.7c0.4-0.4,1-0.4,1.4,0c0.4,0.4,0.4,1,0,1.4l-6.7,6.7
|
||||
C11.4,42.1,10.7,42.1,10.3,41.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st20"
|
||||
d="M282.1,91.1l-24.8-24.8l-16.5,16.5c-1.5,1.5-1.5,4,0,5.5l19.3,19.3c1.5,1.5,4,1.5,5.5,0L282.1,91.1z" />
|
||||
<path class="st1" d="M256.4,84.9l13.2-3.2c0.4-0.1,0.7,0.5,0.3,0.7l-7.8,5.2l2.7,2.7c0.2,0.2,0.1,0.6-0.2,0.7l-12,1.9
|
||||
c-0.4,0.1-0.6-0.5-0.3-0.7l6.5-3.9l-2.6-2.6C256,85.4,256.1,85,256.4,84.9z" />
|
||||
<path class="st21" d="M279.4,88.4l-16,16c-0.3,0.3-0.8,0.3-1.1,0l-11.8-11.8c-0.8-0.8-2-0.8-2.8,0l0,0c-0.8,0.8-0.8,2,0,2.8
|
||||
l12.4,12.4c1.5,1.5,4,1.5,5.5,0l16.5-16.5L279.4,88.4z" />
|
||||
<path class="st1" d="M259.3,61.7l27.5,27.5l-3.3,3.3L256,65L259.3,61.7z M267.5,69.9l3.3-3.3l-4.1-4.1l-3.3,3.3 M282.7,85.1
|
||||
l3.3-3.3l-4.1-4.1l-3.3,3.3" />
|
||||
<path class="st7" d="M259.3,108.5L240,89.2c-2-2-2-5.2,0-7.2l13.8-13.8c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-13.8,13.8
|
||||
c-1.1,1.1-1.1,2.8,0,3.9l19.3,19.3c1.1,1.1,2.8,1.1,3.9,0l16.5-16.5c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-16.5,16.5
|
||||
C264.4,110.4,261.3,110.4,259.3,108.5z" />
|
||||
<path class="st1" d="M251,78.2c-0.5-0.5-0.5-1.2,0-1.7l8.3-8.3c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-8.3,8.3
|
||||
C252.2,78.6,251.5,78.6,251,78.2z M246.9,82.3c-0.1-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-0.1-0.1-0.1-0.2c0-0.1,0-0.1-0.1-0.2
|
||||
c0-0.1,0-0.1,0-0.2c0-0.1,0-0.2,0-0.2c0-0.1,0-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1,0,0.1-0.1c0.1-0.1,0.1-0.1,0.2-0.1
|
||||
c0.1,0,0.1-0.1,0.2-0.1s0.2-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.2,0s0.1,0,0.2,0.1c0.1,0,0.1,0.1,0.2,0.1
|
||||
c0.3,0.2,0.5,0.6,0.5,1c0,0.1,0,0.2,0,0.2c0,0.1,0,0.1-0.1,0.2c0,0.1-0.1,0.1-0.1,0.2s-0.2,0.1-0.2,0.1c-0.2,0.2-0.5,0.4-0.8,0.3
|
||||
C247.4,82.7,247.1,82.5,246.9,82.3z" />
|
||||
<path class="st7" d="M282.7,93.3l-27.5-27.5c-0.5-0.5-0.5-1.2,0-1.7l3.3-3.3c0.5-0.5,1.2-0.5,1.7,0l27.5,27.5
|
||||
c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283.9,93.8,283.1,93.8,282.7,93.3z M257.6,65l25.9,25.9l1.7-1.7l-25.9-25.9L257.6,65z" />
|
||||
<path class="st7" d="M266.7,70.7c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7
|
||||
l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C267.9,71.2,267.2,71.2,266.7,70.7z M281.8,85.9
|
||||
c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0
|
||||
l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283,86.3,282.3,86.3,281.8,85.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st11" d="M308.8,72.2l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
|
||||
C324,68.5,316.8,68.8,308.8,72.2" />
|
||||
<path class="st12" d="M319.7,69.9C319.7,69.8,319.7,69.8,319.7,69.9c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
|
||||
C323.3,79.5,323.3,73.5,319.7,69.9 M348.4,98.8c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C347.9,101.6,348.4,100.2,348.4,98.8" />
|
||||
<path class="st2" d="M342,107.6l-34.3-34.3c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
|
||||
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
|
||||
L333.7,84c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3L348,94c2.7,2.7,2.7,7.1,0,9.8
|
||||
l-3.8,3.8C343.6,108.2,342.6,108.2,342,107.6z" />
|
||||
<path class="st1" d="M316.9,72.7c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
C319,78.2,319,74.8,316.9,72.7C316.9,72.7,316.9,72.7,316.9,72.7z M336.7,88.2c-1.2-1.2-3.1-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.2
|
||||
s3.1,1.2,4.2,0C337.8,91.2,337.8,89.3,336.7,88.2z" />
|
||||
<path class="st2" d="M308.2,81.4c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0s2.7,7.1,0,9.8C315.2,84.1,310.9,84.1,308.2,81.4z
|
||||
M315.8,73.7c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C317.3,77.7,317.3,75.3,315.8,73.7z" />
|
||||
<path class="st1" d="M346.7,102.5c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
S348.8,104.6,346.7,102.5C346.7,102.5,346.7,102.5,346.7,102.5z" />
|
||||
<path class="st2" d="M338,111.2c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8C345.1,113.9,340.7,113.9,338,111.2z
|
||||
M345.7,103.6c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C347.2,107.6,347.2,105.1,345.7,103.6z" />
|
||||
<path class="st2" d="M338,111.2l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
|
||||
C339.5,111.8,338.6,111.8,338,111.2z M299.3,72.5c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1s0.7-0.5,1.1-0.4
|
||||
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C300,73,299.6,72.8,299.3,72.5z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st22" d="M277.7,28.4c0-3,2.4-5.4,5.4-5.4s5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4 M264.6,15.2c0-3,2.4-5.4,5.4-5.4
|
||||
c3,0,5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4" />
|
||||
<path class="st15" d="M275,44.3l-21-21c-0.2-0.2-0.2-0.6,0-0.9l6.8-6.8c0.2-0.2,0.6-0.2,0.9,0l21,21c0.2,0.2,0.2,0.6,0,0.9
|
||||
l-6.8,6.8C275.6,44.6,275.2,44.6,275,44.3z" />
|
||||
<path class="st1" d="M255.1,21.4l-1.1,1.1c-0.2,0.2-0.2,0.6,0,0.9l1.8,1.8L270,24l0,0l-3.8-3.8L255.1,21.4z M259.2,28.6l-0.2-0.2
|
||||
l4,4l14.2-1.1l0,0l-3.8-3.8L259.2,28.6z M281.6,38.7l1.1-1.1c0.2-0.2,0.2-0.6,0-0.9l-1.8-1.8L266.7,36l0,0l3.7,3.7L281.6,38.7z
|
||||
M271.6,13.6c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3c0.9,0.9,2.4,0.9,3.3,0C272.5,16,272.5,14.5,271.6,13.6z M284.8,26.7
|
||||
c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3s2.4,0.9,3.3,0S285.7,27.6,284.8,26.7z" />
|
||||
<path class="st2" d="M274.3,45l-21-21c-0.6-0.6-0.6-1.6,0-2.2l6.8-6.8c0.6-0.6,1.6-0.6,2.2,0l21,21c0.6,0.6,0.6,1.6,0,2.2l-6.8,6.8
|
||||
C275.9,45.6,275,45.6,274.3,45z M254.9,22.9l20.6,20.6l6.3-6.3l-20.6-20.6L254.9,22.9z M250.3,34.9c-0.4-0.4-0.4-0.9,0-1.3l5-5
|
||||
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C251.2,35.3,250.7,35.3,250.3,34.9z M263.4,48.1c-0.4-0.4-0.4-0.9,0-1.3l5-5
|
||||
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C264.4,48.4,263.8,48.4,263.4,48.1z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,420 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 360 200" style="enable-background:new 0 0 360 200;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E6E6E6;}
|
||||
.st1{fill:#DCDCDC;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
.st3{fill:#808080;}
|
||||
.st4{fill:#E8E8E8;}
|
||||
.st5{fill:#99F5C8;}
|
||||
.st6{opacity:0.26;fill:#1D1D1B;}
|
||||
.st7{fill:#C8C8C8;}
|
||||
.st8{fill:#B5B5B5;}
|
||||
.st9{fill:#878787;}
|
||||
.st10{fill:#666666;}
|
||||
.st11{fill:#D1D1D1;}
|
||||
.st12{opacity:0.2;fill:#808080;enable-background:new ;}
|
||||
</style>
|
||||
<rect class="st0" width="360" height="200" />
|
||||
<g>
|
||||
<path class="st1" d="M47.7,59.7l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
|
||||
<path class="st2"
|
||||
d="M51.1,130.2L13.4,92.5c-2.1-2.1-2.1-5.4,0-7.5L32,66.3c2-2,5.4-2,7.5,0L77.2,104" />
|
||||
<path class="st2" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C47.4,83.2,47.3,84.9,46.3,85.9z" />
|
||||
<path class="st3" d="M78.5,105.3c-0.7,0.7-1.9,0.7-2.6,0L38.2,67.7c-1.3-1.3-3.6-1.4-4.9,0L14.7,86.3c-1.3,1.3-1.3,3.5,0,4.9
|
||||
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0L12.1,93.8c-2.8-2.8-2.8-7.3,0-10.1L30.7,65c2.8-2.8,7.3-2.8,10.1,0
|
||||
l37.7,37.7C79.2,103.5,79.2,104.6,78.5,105.3z" />
|
||||
<path class="st4" d="M46.3,85.9L32.9,99.3c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C47.4,83.2,47.3,84.9,46.3,85.9z" />
|
||||
<path class="st3" d="M47.6,87.2l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
|
||||
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C49.4,82.6,49.4,85.4,47.6,87.2z M21.8,88.2c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7c0.3,0.3,0.8,0.3,1.1,0
|
||||
L45,84.6c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L21.8,88.2z M82.2,101.6l-33.5,33.5c-0.7,0.7-1.9,0.7-2.6,0
|
||||
c-0.7-0.7-0.7-1.9,0-2.6L79.6,99c0.7-0.7,1.9-0.7,2.6,0S82.9,100.9,82.2,101.6z M80.3,95.9c-1.7,1.7-4.6,1.7-6.3,0L58.9,80.9
|
||||
c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1
|
||||
c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1L57,71.5c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l20.7,20.7C82,91.3,82,94.2,80.3,95.9z
|
||||
" />
|
||||
<path class="st3" d="M61.4,69.7l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6L46.4,61
|
||||
c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C62,67.8,62.1,69,61.4,69.7z M56.4,69.5
|
||||
l1.1-1.1l-4.9-4.9l-1.1,1.1L56.4,69.5z" />
|
||||
<g>
|
||||
<path class="st5" d="M57.6,114.1c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
|
||||
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
|
||||
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C58.2,113.1,58,113.7,57.6,114.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M29.9,146.1l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C27.4,145.1,28.9,145.1,29.9,146.1z M11.5,152.4l7.1-7.1l4.2,4.2l-7.1,7.1L11.5,152.4z" />
|
||||
<path class="st5" d="M37,153.1l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C34.4,152.1,36,152.1,37,153.1z" />
|
||||
<path class="st6" d="M50.4,170.1L50.4,170.1c1-1,1-2.5,0-3.5L37,153.1c-1-1-2.5-1-3.5,0l-1.8,1.8l15.2,15.2
|
||||
C47.9,171.1,49.4,171.1,50.4,170.1z" />
|
||||
<path class="st3" d="M55,174.7l-14.1,14.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7L25.3,145c1.6-1.6,4.1-1.6,5.7,0
|
||||
l24,24C56.6,170.6,56.6,173.1,55,174.7z M13.3,161.3c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0l14.1-14.1
|
||||
c0.4-0.4,0.4-1,0-1.4l-24-24c-0.4-0.4-1-0.4-1.4,0L13.3,161.3z" />
|
||||
<path class="st2" d="M39.1,183.5c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
|
||||
C39.7,182,39.7,182.9,39.1,183.5z M25,169.4c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C25.4,168.7,25.2,169.1,25,169.4z" />
|
||||
<path class="st3" d="M23.9,150.6c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-1.4-1.4
|
||||
c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C24.5,149.1,24.5,150,23.9,150.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M117.2,186.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
|
||||
C137.2,161.8,124.5,179.6,117.2,186.9z" />
|
||||
<path class="st5" d="M103.2,148.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8l-15.4,2.4
|
||||
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C102.8,148.9,102.9,148.5,103.2,148.4z" />
|
||||
<path class="st3" d="M131.7,166.8l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-2.8,2.8
|
||||
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1S132.2,167.3,131.7,166.8z" />
|
||||
<path class="st3" d="M115.1,189l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
|
||||
c0.6,0.6,0.6,1.5,0,2.1s-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C116.6,189.6,115.6,189.6,115.1,189z
|
||||
M82.9,152.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C107.6,127.9,82.9,152.6,82.9,152.6z" />
|
||||
<path class="st3" d="M115.1,189l-36.8-36.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
|
||||
C116.6,189.6,115.6,189.6,115.1,189z" />
|
||||
<path class="st2" d="M88.3,156.3c-1.9-1.9-4.9-1.9-6.8,0s-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0S90.2,158.1,88.3,156.3
|
||||
C88.3,156.3,88.3,156.3,88.3,156.3z M110.9,178.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0
|
||||
c1.9,1.9,4.9,1.9,6.8,0C112.8,183.8,112.8,180.8,110.9,178.9C110.9,178.9,110.9,178.9,110.9,178.9z" />
|
||||
<path class="st3" d="M80.4,164.1c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9
|
||||
C86.9,166.6,82.9,166.6,80.4,164.1z M87.2,157.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
|
||||
C88.5,160.7,88.5,158.6,87.2,157.3z" />
|
||||
<g>
|
||||
<path class="st3" d="M103.1,186.7c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
|
||||
C109.5,189.2,105.5,189.2,103.1,186.7z M109.9,180c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
|
||||
S111.2,181.3,109.9,180z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M103.1,186.7L73.4,157c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
|
||||
C104.6,187.3,103.7,187.3,103.1,186.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M68.4,152.1c-0.1-0.1-0.1-0.1-0.2-0.2s-0.1-0.2-0.1-0.3s-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2s0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
|
||||
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1c0.1,0,0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3s-0.1,0.2-0.1,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1c-0.1,0-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0
|
||||
c-0.1,0-0.2,0-0.3,0s-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C68.6,152.3,68.5,152.2,68.4,152.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M336.4,33.8l-13.6,13.6c-1.9,1.9-5.1,1.9-7.1,0l-13.8-13.8c-1.9-1.9-1.9-5.1,0-7.1L315.5,13
|
||||
c1.9-1.9,5.1-1.9,7.1,0l13.8,13.8C338.3,28.7,338.3,31.9,336.4,33.8z" />
|
||||
<path class="st3" d="M337.4,34.9l-13.6,13.6c-2.5,2.5-6.6,2.5-9.2,0l-13.8-13.8c-2.5-2.5-2.5-6.6,0-9.2l13.6-13.6
|
||||
c2.5-2.5,6.6-2.5,9.2,0l13.8,13.8C340,28.2,340,32.3,337.4,34.9z M303,27.6c-1.4,1.4-1.4,3.6,0,4.9l13.8,13.8
|
||||
c1.4,1.4,3.6,1.4,4.9,0l13.6-13.6c1.4-1.4,1.4-3.6,0-4.9L321.5,14c-1.4-1.4-3.6-1.4-4.9,0L303,27.6z" />
|
||||
<path class="st2" d="M325.4,36.4L325.4,36.4c-3.4,3.4-9,3.4-12.4,0l-0.1-0.1c-3.4-3.4-3.4-9,0-12.4l0,0c3.4-3.4,9-3.4,12.4,0
|
||||
l0.1,0.1C328.8,27.5,328.8,33,325.4,36.4z" />
|
||||
<path class="st3" d="M326.5,37.5c-4,4-10.5,4-14.5,0l-0.1-0.1c-4-4-4-10.5,0-14.5s10.5-4,14.5,0l0.1,0.1
|
||||
C330.5,27,330.5,33.5,326.5,37.5z M314,25c-2.8,2.8-2.8,7.4,0,10.3l0.1,0.1c2.8,2.8,7.4,2.8,10.3,0c2.8-2.8,2.8-7.4,0-10.3
|
||||
l-0.1-0.1C321.4,22.2,316.8,22.2,314,25z" />
|
||||
<path class="st3" d="M315.3,31.5c-0.7,0.7-0.7,1.8,0,2.5c0.7,0.7,1.8,0.7,2.5,0c0,0,0,0,0,0c0.7-0.7,0.7-1.8,0-2.5
|
||||
C317.2,30.8,316,30.8,315.3,31.5C315.3,31.5,315.3,31.5,315.3,31.5z M320.4,26.4c-0.7,0.7-0.7,1.8,0,2.5s1.8,0.7,2.5,0c0,0,0,0,0,0
|
||||
c0.7-0.7,0.7-1.8,0-2.5C322.3,25.7,321.1,25.7,320.4,26.4C320.4,26.4,320.4,26.4,320.4,26.4z" />
|
||||
<path class="st2"
|
||||
d="M341.7,43.3l3.8,3.8c2.6,2.6,2.6,6.8,0,9.4l0,0c-2.6,2.6-6.8,2.6-9.4,0l-3.8-3.8L341.7,43.3z" />
|
||||
<path class="st3" d="M346.6,57.6c-3.2,3.2-8.3,3.2-11.5,0l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
|
||||
c2,2,5.3,2,7.3,0c2-2,2-5.3,0-7.3l-3.1-3.1c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.1,3.1
|
||||
C349.8,49.3,349.8,54.4,346.6,57.6z" />
|
||||
<path class="st3" d="M335.1,51.8c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3.3,3.3
|
||||
C335.7,50.3,335.7,51.2,335.1,51.8z M340.8,46.2c-0.6,0.6-1.5,0.6-2.1,0l-3.3-3.3c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l3.3,3.3
|
||||
C341.4,44.6,341.4,45.6,340.8,46.2z" />
|
||||
<path class="st3" d="M344.1,43.1l-12,12c-0.6,0.6-1.5,0.6-2.1,0s-0.6-1.5,0-2.1l12-12c0.6-0.6,1.5-0.6,2.1,0S344.7,42.5,344.1,43.1
|
||||
z M349.6,60.6c-0.6,0.6-1.5,0.6-2.1,0l-3-3c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l3,3C350.1,59,350.2,60,349.6,60.6z" />
|
||||
<g>
|
||||
<path class="st5" d="M339.8,50.8c-0.5,0.5-0.5,1.4,0,1.9c0.5,0.5,1.4,0.5,1.9,0c0,0,0,0,0,0c0.5-0.5,0.5-1.4,0-1.9
|
||||
C341.2,50.3,340.3,50.3,339.8,50.8C339.8,50.8,339.8,50.8,339.8,50.8z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st7" d="M171,149l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
|
||||
C186.3,145.2,179.1,145.5,171,149" />
|
||||
<path class="st8" d="M182,146.6C181.9,146.6,181.9,146.6,182,146.6c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
|
||||
C185.6,156.3,185.6,150.3,182,146.6 M210.7,175.6c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C210.2,178.4,210.7,177,210.7,175.6" />
|
||||
<path class="st3" d="M204.3,184.4L170,150c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
|
||||
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
|
||||
l-12.1-12.1c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3l11.6,11.6
|
||||
c2.7,2.7,2.7,7.1,0,9.8l-3.8,3.8C205.8,185,204.9,184.9,204.3,184.4z" />
|
||||
<path class="st2" d="M179.1,149.4c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
C181.3,155,181.3,151.6,179.1,149.4C179.1,149.4,179.1,149.4,179.1,149.4z M198.9,164.9c-1.2-1.2-3.1-1.2-4.2,0s-1.2,3.1,0,4.2
|
||||
s3.1,1.2,4.2,0C200.1,168,200.1,166.1,198.9,164.9z" />
|
||||
<path class="st3" d="M170.4,158.1c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
|
||||
C177.5,160.8,173.1,160.8,170.4,158.1z M178.1,150.5c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5s4,1.5,5.5,0
|
||||
C179.6,154.5,179.6,152,178.1,150.5z" />
|
||||
<path class="st2" d="M209,179.3c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
C211.1,184.8,211.1,181.4,209,179.3C209,179.3,209,179.3,209,179.3z" />
|
||||
<path class="st3" d="M200.3,188c-2.7-2.7-2.7-7.1,0-9.8c2.7-2.7,7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8
|
||||
C207.4,190.7,203,190.7,200.3,188z M207.9,180.3c-1.5-1.5-4-1.5-5.5,0s-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0
|
||||
C209.4,184.3,209.4,181.9,207.9,180.3z" />
|
||||
<path class="st3" d="M200.3,188l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
|
||||
C201.8,188.6,200.9,188.6,200.3,188z M161.6,149.3c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1c0.3-0.3,0.7-0.5,1.1-0.4
|
||||
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C162.2,149.7,161.9,149.6,161.6,149.3z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2"
|
||||
d="M107.9,26.1L95.6,13.7c-2.7-2.7-7-2.9-9.9-0.6l-9,7.1l24.7,24.7l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
|
||||
<path class="st2"
|
||||
d="M107.9,26.1l-6.2-6.2L89,32.6l12.4,12.4l7.1-8.9C110.9,33,110.6,28.7,107.9,26.1z" />
|
||||
<path class="st9" d="M78.7,18.1l24.7,24.7l-12,12L66.7,30.1L78.7,18.1z" />
|
||||
<path class="st10" d="M91.1,30.4l12.4,12.4l-12,12L79.1,42.5L91.1,30.4z" />
|
||||
<path class="st9" d="M76.6,20.2L76.6,20.2c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4l7.1,7.1l6.4-6.4C78.6,25.3,78.6,22.1,76.6,20.2z
|
||||
M95,58.4l6.4-6.4c1.9-1.9,1.9-5.1,0-7.1l0,0c-1.9-1.9-5.1-1.9-7.1,0l-6.4,6.4" />
|
||||
<path class="st3" d="M69.2,34.7c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9c-1.4-1.4-3.6-1.4-4.9,0l-6.4,6.4
|
||||
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0s2.5,6.6,0,9.2l-6.4,6.4
|
||||
C70.7,35.3,69.8,35.3,69.2,34.7z M93.9,59.4c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c1.4-1.4,1.4-3.6,0-4.9s-3.6-1.4-4.9,0L89,52.4
|
||||
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4c2.5-2.5,6.6-2.5,9.2,0c2.5,2.5,2.5,6.6,0,9.2l-6.4,6.4
|
||||
C95.5,60,94.5,60,93.9,59.4z" />
|
||||
<path class="st3" d="M101.8,43.8c0,0-0.1-0.1-0.1-0.1C95,34.6,87,26.6,78,19.9c-0.6-0.5-0.8-1.4-0.3-2.1c0.5-0.6,1.4-0.8,2.1-0.3
|
||||
c9.3,6.9,17.5,15.1,24.4,24.4c0.5,0.6,0.4,1.6-0.3,2.1C103.2,44.3,102.4,44.3,101.8,43.8z" />
|
||||
<path class="st3" d="M75.6,21.2c0,0-0.1-0.1-0.1-0.1c-0.5-0.6-0.4-1.6,0.2-2.1l9-7.1c3.6-2.8,8.7-2.5,12,0.7L109,25
|
||||
c3.3,3.3,3.5,8.4,0.7,12l-7.1,8.9c-0.5,0.6-1.4,0.7-2.1,0.2c-0.7-0.5-0.7-1.4-0.2-2.1l7.1-8.9c1.9-2.4,1.7-5.8-0.4-8L94.5,14.7
|
||||
c-2.2-2.2-5.6-2.4-8-0.5l-9,7.1C76.9,21.9,76.1,21.8,75.6,21.2z M92.9,56.3l-4.9,4.9l-2.8-2.8l4.9-4.9 M68.1,31.5l-4.9,4.9
|
||||
l-2.8-2.8l4.9-4.9" />
|
||||
<path class="st3" d="M86.9,62.3L84,59.4c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9
|
||||
l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1L89,62.3C88.4,62.9,87.5,62.9,86.9,62.3z M62.1,37.5l-2.8-2.8
|
||||
c-0.6-0.6-0.6-1.5,0-2.1l4.9-4.9c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-3.9,3.9l0.7,0.7l3.9-3.9c0.6-0.6,1.5-0.6,2.1,0
|
||||
c0.6,0.6,0.6,1.5,0,2.1l-4.9,4.9C63.6,38.1,62.7,38.1,62.1,37.5z" />
|
||||
<path class="st3" d="M95,60.5L61.1,26.6c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l33.9,33.9c0.6,0.6,0.6,1.5,0,2.1
|
||||
C96.5,61.1,95.6,61.1,95,60.5z" />
|
||||
<path class="st2" d="M84.2,43.3l-5.9-5.9c-0.6-0.6-0.6-1.5,0-2.1l3.5-3.5c2.2-2.2,5.8-2.2,8.1,0l0,0c2.2,2.2,2.2,5.8,0,8.1
|
||||
l-3.5,3.5C85.7,43.9,84.8,43.9,84.2,43.3z" />
|
||||
<path class="st3" d="M106.3,22.3l-1.1,1.1l-7.1-7.1l1.1-1.1l0,0c1.2-1.2,3.1-1.2,4.2,0l2.8,2.8C107.5,19.2,107.5,21.1,106.3,22.3
|
||||
L106.3,22.3z" />
|
||||
<path class="st2" d="M74.9,22c-1-1-2.6-1-3.5,0c-1,1-1,2.6,0,3.5c1,1,2.6,1,3.5,0C75.8,24.5,75.8,22.9,74.9,22z M99.6,46.7
|
||||
c-1-1-2.6-1-3.5,0s-1,2.6,0,3.5s2.6,1,3.5,0S100.6,47.7,99.6,46.7z" />
|
||||
<path class="st3" d="M89,64.4L59.4,34.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.6,29.6c0.6,0.6,0.6,1.5,0,2.1
|
||||
C90.5,65,89.6,65,89,64.4z M96.4,67.6c-0.6-0.6-1.5-0.6-2.1,0c-0.6,0.6-0.6,1.5,0,2.1s1.5,0.6,2.1,0C97,69.1,97,68.2,96.4,67.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M139.1,66.4l24,24c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-24-24c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C136.5,65.4,138.1,65.4,139.1,66.4z M120.7,72.7l7.1-7.1l4.2,4.2l-7.1,7.1L120.7,72.7z" />
|
||||
<path class="st5" d="M146.1,73.4l17,17c1,1,1,2.5,0,3.5l-14.1,14.1c-1,1-2.5,1-3.5,0l-17-17c-1-1-1-2.5,0-3.5l14.1-14.1
|
||||
C143.6,72.4,145.1,72.4,146.1,73.4z" />
|
||||
<path class="st6" d="M159.6,90.4L159.6,90.4c1-1,1-2.5,0-3.5l-13.4-13.4c-1-1-2.5-1-3.5,0l-1.8,1.8L156,90.4
|
||||
C157,91.4,158.6,91.4,159.6,90.4z" />
|
||||
<path class="st3" d="M164.2,95L150,109.1c-1.6,1.6-4.1,1.6-5.7,0l-24-24c-1.6-1.6-1.6-4.1,0-5.7l14.1-14.1c1.6-1.6,4.1-1.6,5.7,0
|
||||
l24,24C165.7,90.9,165.7,93.4,164.2,95z M122.4,81.6c-0.4,0.4-0.4,1,0,1.4l24,24c0.4,0.4,1,0.4,1.4,0L162,92.9c0.4-0.4,0.4-1,0-1.4
|
||||
l-24-24c-0.4-0.4-1-0.4-1.4,0L122.4,81.6z" />
|
||||
<path class="st2" d="M148.2,103.8c-0.6,0.6-1.5,0.6-2.1,0l-8.8-8.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l8.8,8.8
|
||||
C148.8,102.3,148.8,103.2,148.2,103.8z M134.1,89.7c-0.3,0.3-0.7,0.5-1.1,0.4c-0.4,0-0.8-0.1-1.1-0.4c-0.3-0.3-0.5-0.7-0.4-1.1
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3s0.1-0.2,0.1-0.3c0.3-0.4,0.7-0.7,1.2-0.7c0.4,0,0.8,0.1,1.1,0.4c0.1,0.1,0.1,0.1,0.2,0.2
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3s0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3C134.6,89,134.4,89.4,134.1,89.7z" />
|
||||
<path class="st3" d="M133,71c-0.6,0.6-1.5,0.6-2.1,0l-3.2-3.2l-4.9,4.9l0.4,0.4c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0
|
||||
l-1.4-1.4c-0.6-0.6-0.6-1.5,0-2.1l7.1-7.1c0.6-0.6,1.5-0.6,2.1,0l4.2,4.2C133.6,69.4,133.6,70.4,133,71z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M315,113.9l3,3l-1.9,1.9l7.6,7.6l3.7-3.7l-7.6-7.6l-1.8,1.8" />
|
||||
<path class="st2"
|
||||
d="M318.3,184.5l-37.7-37.7c-2.1-2.1-2.1-5.4,0-7.5l18.7-18.7c2-2,5.4-2,7.5,0l37.7,37.7" />
|
||||
<path class="st2" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C314.6,137.5,314.6,139.2,313.6,140.2z" />
|
||||
<path class="st3" d="M345.7,159.6c-0.7,0.7-1.9,0.7-2.6,0L305.5,122c-1.3-1.3-3.6-1.4-4.9,0l-18.7,18.7c-1.3,1.3-1.3,3.5,0,4.9
|
||||
l37.7,37.7c0.7,0.7,0.7,1.9,0,2.6c-0.7,0.7-1.9,0.7-2.6,0l-37.7-37.7c-2.8-2.8-2.8-7.3,0-10.1l18.7-18.7c2.8-2.8,7.3-2.8,10.1,0
|
||||
l37.7,37.7C346.5,157.8,346.4,158.9,345.7,159.6z" />
|
||||
<path class="st4" d="M313.6,140.2l-13.4,13.4c-1,1-2.7,1-3.7,0l-8.7-8.7c-1-1-1-2.7,0-3.7l13.4-13.4c1-1,2.7-1,3.7,0l8.7,8.7
|
||||
C314.6,137.5,314.6,139.2,313.6,140.2z" />
|
||||
<path class="st3" d="M314.9,141.5l-13.4,13.4c-1.7,1.7-4.6,1.7-6.3,0l-8.7-8.7c-1.7-1.7-1.7-4.6,0-6.3l13.4-13.4
|
||||
c1.7-1.7,4.6-1.7,6.3,0l8.7,8.7C316.6,136.9,316.6,139.7,314.9,141.5z M289.1,142.5c-0.3,0.3-0.3,0.8,0,1.1l8.7,8.7
|
||||
c0.3,0.3,0.8,0.3,1.1,0l13.4-13.4c0.3-0.3,0.3-0.8,0-1.1l-8.7-8.7c-0.3-0.3-0.8-0.3-1.1,0L289.1,142.5z M349.5,155.9l-33.5,33.5
|
||||
c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l33.5-33.5c0.7-0.7,1.9-0.7,2.6,0C350.2,154,350.2,155.2,349.5,155.9z M347.5,150.2
|
||||
c-1.7,1.7-4.6,1.7-6.3,0l-15.1-15.1c-0.3-0.3-0.8-0.3-1.1,0l-1.9,1.9c-0.7,0.7-1.9,0.7-2.6,0c-0.7-0.7-0.7-1.9,0-2.6l1.9-1.9
|
||||
c1.7-1.7,4.6-1.7,6.3,0l15.1,15.1c0.3,0.3,0.8,0.3,1.1,0c0.3-0.3,0.3-0.8,0-1.1l-20.7-20.7c-0.7-0.7-0.7-1.9,0-2.6s1.9-0.7,2.6,0
|
||||
l20.7,20.7C349.2,145.6,349.2,148.5,347.5,150.2z" />
|
||||
<path class="st3" d="M328.6,124l-3.7,3.7c-0.7,0.7-1.9,0.7-2.6,0l-7.6-7.6c-0.7-0.7-0.7-1.9,0-2.6l0.6-0.6l-1.7-1.7
|
||||
c-0.7-0.7-0.7-1.9,0-2.6c0.7-0.7,1.9-0.7,2.6,0l1.7,1.7l0.5-0.5c0.7-0.7,1.9-0.7,2.6,0l7.5,7.5C329.3,122.1,329.3,123.3,328.6,124z
|
||||
M323.6,123.8l1.1-1.1l-4.9-4.9l-1.1,1.1L323.6,123.8z" />
|
||||
<g>
|
||||
<path class="st5" d="M324.8,168.4c-0.2,0.2-0.5,0.4-0.9,0.5c-1,0.2-2-0.4-2.2-1.3l-1.3-5.7l-1.3,1.9c-0.4,0.6-1.1,0.9-1.8,0.7
|
||||
c-0.7-0.2-1.3-0.7-1.4-1.4l-2.3-9.5c-0.2-1,0.4-2,1.3-2.2c1-0.2,2,0.4,2.2,1.3l1.3,5.7l1.3-1.9c0.4-0.6,1.1-0.9,1.8-0.7
|
||||
c0.7,0.2,1.3,0.7,1.4,1.4l2.2,9.5C325.4,167.4,325.3,168,324.8,168.4z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st11"
|
||||
d="M233.8,171.2l15.3,15.3l15.9-26.2c0.3-0.5,0.3-1.1-0.2-1.6l-3.3-3.3c-0.4-0.4-1-0.5-1.6-0.2L233.8,171.2z" />
|
||||
<path class="st2"
|
||||
d="M244.5,164.7l-6.2,3.8l13.5,13.5l0,0l3.8-6.2L244.5,164.7z M256.1,157.6l-6.2,3.8l9,9l3.8-6.2L256.1,157.6z" />
|
||||
<path class="st12"
|
||||
d="M263.6,157.4l1.3,1.3c0.4,0.4,0.5,1,0.2,1.6l-16,26.2l-3.5-3.5l18.2-24C264.1,158.4,264,157.8,263.6,157.4z" />
|
||||
<g>
|
||||
<path class="st3" d="M248.4,187.2l-15.3-15.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.3,0.2-0.6,0.5-0.7l26.2-15.9c0.9-0.5,2-0.4,2.8,0.3
|
||||
l3.3,3.3c0.7,0.7,0.9,1.9,0.3,2.8L249.9,187c-0.2,0.3-0.4,0.4-0.7,0.5C248.9,187.5,248.6,187.4,248.4,187.2z M235.4,171.4
|
||||
l13.5,13.5l15.3-25.2c0.1-0.1,0.1-0.3,0-0.3l-3.3-3.3c-0.1-0.1-0.2-0.1-0.3,0L235.4,171.4z" />
|
||||
<path class="st3" d="M230.5,169.3c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l20.4,20.4l0,0c0.4,0.4,0.4,1,0,1.4c-0.4,0.4-1,0.4-1.4,0
|
||||
L230.5,169.3L230.5,169.3z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M184,108.7L196.6,96c3.9-3.9,10.3-3.9,14.1,0l22.6,22.6c2.7,2.7,3.6,6.7,2.5,10.3l-0.4,1.2
|
||||
c-2.2,6.4-6.2,12.1-11.5,16.4l-2,1.7c-1.1,0.9-2.5-0.7-1.5-1.7l0,0" />
|
||||
<path class="st2" d="M226,144.4l0.1,0.1l0,0c1.2-1.2,2.4-2.6,3.5-3.9c0,0,0.1-0.1,0.1-0.1c0.2-0.2,0.4-0.5,0.5-0.7
|
||||
c0.1-0.1,0.1-0.1,0.1-0.2c0.2-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.2,0.2-0.2c0.1-0.2,0.3-0.5,0.5-0.7c0.1-0.1,0.1-0.1,0.1-0.2
|
||||
c0.3-0.5,0.6-1,0.9-1.4c0,0,0.1-0.1,0.1-0.2c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.2,0.2-0.3,0.3-0.5c0.1-0.2,0.2-0.3,0.2-0.5
|
||||
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7
|
||||
c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2c1.2-3.6,0.2-7.6-2.5-10.3l-22.5-22.5c-3.9-3.9-10.3-3.9-14.1,0l-2.8,2.8
|
||||
l30.8,30.8C228.5,133.6,229.1,139.7,226,144.4z" />
|
||||
<path class="st2" d="M209.2,110.1l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5l-0.7,0.7
|
||||
C211.7,111.1,210.2,111,209.2,110.1z M218,118.9l-3.5-3.5c-1-1-1-2.5,0-3.5l0.7-0.7c1-1,2.5-1,3.5,0l3.5,3.5c1,1,1,2.5,0,3.5
|
||||
l-0.7,0.7C220.6,119.9,219,119.9,218,118.9z M233.5,134.4c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.2-0.3,0.2-0.5
|
||||
c0.1-0.2,0.2-0.4,0.3-0.6c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.2,0.2-0.5,0.3-0.7c0-0.1,0.1-0.2,0.1-0.4c0.1-0.4,0.2-0.7,0.4-1.1l0.4-1.2
|
||||
c0.1-0.2,0.1-0.4,0.2-0.7l-8.2-8.2c-1-1-2.5-1-3.5,0l-0.8,0.8c-1,1-1,2.5,0,3.5L233.5,134.4L233.5,134.4z" />
|
||||
<path class="st3" d="M219.3,149.2c0,0-0.1-0.1-0.1-0.1c-1-1-0.9-2.6,0.1-3.6c0.6-0.6,1.5-0.6,2.1,0c0.2,0.2,0.4,0.5,0.4,0.8
|
||||
l1.1-0.9c5.1-4.1,8.8-9.6,11-15.8l0.4-1.2c1-3.1,0.2-6.4-2.1-8.7l-22.6-22.6c-3.3-3.3-8.7-3.3-12,0L185,109.7
|
||||
c-0.6,0.6-1.5,0.6-2.1,0c-0.6-0.6-0.6-1.5,0-2.1L195.5,95c4.5-4.5,11.8-4.5,16.3,0l22.6,22.6c3.1,3.1,4.2,7.6,2.8,11.8l-0.4,1.2
|
||||
c-2.3,6.7-6.4,12.6-11.9,17.1l-2,1.7C221.7,150.2,220.3,150.1,219.3,149.2z M226.7,148.6c-0.6-0.6-0.6-1.5,0-2.1l6.4-6.4
|
||||
c0.6-0.6,1.5-0.6,2.1,0c0.6,0.6,0.6,1.5,0,2.1l-6.4,6.4C228.3,149.2,227.3,149.2,226.7,148.6z" />
|
||||
<path class="st3" d="M210.8,139.1l-16.5-16.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0L213,137c0.6,0.6,0.6,1.5,0,2.1
|
||||
C212.4,139.7,211.4,139.7,210.8,139.1z M186.8,115l-5.4-5.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l5.4,5.4
|
||||
c0.6,0.6,0.6,1.5,0,2.1C188.3,115.6,187.4,115.6,186.8,115z M236.2,117.3l-23.4-23.4c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0
|
||||
l23.4,23.4c0.6,0.6,0.6,1.5,0,2.1C237.7,117.9,236.8,117.9,236.2,117.3z M225.1,145.6c-0.6-0.6-0.6-1.4-0.1-2.1
|
||||
c2.7-3.1,1.9-9.3-1.6-12.8l-30.8-30.8c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l30.8,30.8c4.6,4.6,5.4,12.6,1.7,16.9
|
||||
C226.7,146.1,225.8,146.2,225.1,145.6C225.1,145.6,225.1,145.6,225.1,145.6z M198.7,98.9l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
|
||||
s1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C200.3,99.5,199.3,99.5,198.7,98.9z M203,97.5l-2.2-2.2c-0.6-0.6-0.6-1.5,0-2.1
|
||||
c0.6-0.6,1.5-0.6,2.1,0l2.2,2.2c0.6,0.6,0.6,1.5,0,2.1C204.5,98.1,203.6,98.1,203,97.5z" />
|
||||
<path class="st2" d="M219.2,138.5c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
|
||||
C221,143.4,221,140.4,219.2,138.5C219.2,138.5,219.2,138.5,219.2,138.5z" />
|
||||
<path class="st3" d="M211.3,146.4c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.4,2.4,2.5,6.5,0,8.9
|
||||
C217.8,148.9,213.8,148.8,211.3,146.4z M218.1,139.6c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0
|
||||
C219.4,143,219.4,140.9,218.1,139.6z" />
|
||||
<path class="st2" d="M188.2,114.3c-1.9,1.9-1.9,4.9,0,6.8c1.9,1.9,4.9,1.9,6.8,0c0.8-0.8,1.3-1.8,1.4-2.9l-5.3-5.3
|
||||
C190,113.1,189,113.5,188.2,114.3z" />
|
||||
<path class="st3" d="M187.2,122.2c-2.5-2.5-2.4-6.5,0-8.9c1-1,2.3-1.6,3.8-1.8c0.5,0,0.9,0.1,1.2,0.4l5.3,5.3
|
||||
c0.3,0.3,0.5,0.8,0.4,1.2c-0.1,1.4-0.8,2.8-1.8,3.8C193.6,124.7,189.6,124.6,187.2,122.2z M190.6,114.6c-0.5,0.1-0.9,0.4-1.3,0.8
|
||||
c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0c0.4-0.4,0.7-0.8,0.8-1.3L190.6,114.6z" />
|
||||
<g>
|
||||
<path class="st3" d="M217.7,98.1c-0.2-0.2-0.4-0.6-0.4-1l-0.2-5.2c0-0.8,0.6-1.5,1.4-1.6c0.8,0,1.5,0.6,1.6,1.4l0.2,5.2
|
||||
c0,0.8-0.6,1.5-1.4,1.6C218.4,98.5,218,98.3,217.7,98.1z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M219.3,147.6l-0.9-0.9c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l0.9,0.9c0.6,0.6,0.6,1.5,0,2.1
|
||||
C220.8,148.2,219.9,148.2,219.3,147.6z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M194.9,72.9l-1.1,1.1l-35.4-35.4l25.8-25.8c1.9-1.9,5.1-1.9,7.1,0l24.7,24.7l0,0
|
||||
C214.9,47.8,202.2,65.6,194.9,72.9z" />
|
||||
<path class="st5" d="M181,34.4l16.9-4.1c0.6-0.1,0.9,0.6,0.4,0.9l-10,6.6l3.4,3.4c0.3,0.3,0.1,0.8-0.3,0.8L176,44.4
|
||||
c-0.5,0.1-0.8-0.6-0.4-0.9l8.3-5.1l-3.3-3.3C180.5,34.9,180.6,34.5,181,34.4z" />
|
||||
<path class="st3" d="M209.4,52.7l-5.3-5.3c-2.5-2.5-2.5-6.6,0-9.2l2.8-2.8c0.6-0.6,1.5-0.6,2.1,0s0.6,1.5,0,2.1l-2.8,2.8
|
||||
c-1.4,1.4-1.4,3.6,0,4.9l5.3,5.3c0.6,0.6,0.6,1.5,0,2.1C210.9,53.3,210,53.3,209.4,52.7z" />
|
||||
<path class="st3" d="M192.8,75l-35.4-35.4c-0.6-0.6-0.6-1.5,0-2.1l25.8-25.8c2.5-2.5,6.6-2.5,9.2,0l24.7,24.7c0,0,0,0,0,0l2.8,2.8
|
||||
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.8-0.8c-2.9,11-14.6,26.8-21.1,33.3l-1.1,1.1C194.3,75.6,193.3,75.6,192.8,75z
|
||||
M160.6,38.6l33.2,33.2c7.6-7.6,19.2-24.3,20.7-33.8l-24.2-24.2c-1.4-1.4-3.6-1.4-4.9,0C185.3,13.9,160.6,38.6,160.6,38.6z" />
|
||||
<path class="st3" d="M192.8,75L156,38.2c-0.6-0.6-0.6-1.5,0-2.1s1.5-0.6,2.1,0l36.8,36.8c0.6,0.6,0.6,1.5,0,2.1
|
||||
C194.3,75.6,193.3,75.6,192.8,75z" />
|
||||
<path class="st2" d="M166,42.2c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0
|
||||
C167.9,47.2,167.9,44.1,166,42.2C166,42.2,166,42.2,166,42.2z M188.6,64.9c-1.9-1.9-4.9-1.9-6.8,0c-1.9,1.9-1.9,4.9,0,6.8
|
||||
c0,0,0,0,0,0c1.9,1.9,4.9,1.9,6.8,0C190.5,69.8,190.5,66.7,188.6,64.9C188.6,64.9,188.6,64.9,188.6,64.9z" />
|
||||
<path class="st3" d="M158.2,50.1c-2.5-2.5-2.4-6.5,0-8.9s6.5-2.4,8.9,0c2.4,2.4,2.4,6.5,0,8.9C164.6,52.5,160.6,52.6,158.2,50.1z
|
||||
M164.9,43.3c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7c1.3,1.3,3.4,1.3,4.7,0C166.2,46.7,166.3,44.6,164.9,43.3z" />
|
||||
<g>
|
||||
<path class="st3" d="M180.8,72.7c-2.5-2.5-2.4-6.5,0-8.9c2.4-2.4,6.5-2.4,8.9,0c2.5,2.5,2.4,6.5,0,8.9
|
||||
C187.3,75.2,183.3,75.2,180.8,72.7z M187.6,65.9c-1.3-1.3-3.4-1.3-4.7,0c-1.3,1.3-1.3,3.4,0,4.7s3.4,1.3,4.7,0
|
||||
C188.8,69.3,188.9,67.2,187.6,65.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M180.8,72.7L151.1,43c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l29.7,29.7c0.6,0.6,0.6,1.5,0,2.1
|
||||
C182.3,73.3,181.4,73.3,180.8,72.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M146.1,38.1c-0.1-0.1-0.1-0.1-0.2-0.2c0-0.1-0.1-0.2-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0-0.3
|
||||
c0-0.1,0-0.2,0-0.3c0-0.1,0-0.2,0.1-0.3c0.1,0,0.1-0.1,0.2-0.2c0.1-0.1,0.1-0.2,0.2-0.2s0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.1,0.3-0.1
|
||||
s0.2,0,0.3-0.1s0.2,0,0.3,0c0.1,0,0.2,0,0.3,0c0.1,0,0.2,0,0.3,0.1s0.2,0.1,0.3,0.1c0.2,0.1,0.3,0.2,0.4,0.4
|
||||
c0.1,0.1,0.1,0.2,0.1,0.3c0,0.1,0.1,0.2,0.1,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2-0.1,0.3c0,0.1-0.1,0.2-0.1,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.2,0.2s-0.1,0.1-0.2,0.2c-0.1,0.1-0.2,0.1-0.3,0.1s-0.2,0.1-0.3,0.1c-0.1,0-0.2,0-0.3,0c-0.1,0-0.2,0-0.3,0
|
||||
c-0.1,0-0.2,0-0.3-0.1c-0.1,0-0.2-0.1-0.3-0.1C146.3,38.3,146.2,38.1,146.1,38.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<path class="st3"
|
||||
d="M30.2,31.2l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8L26.8,36l-0.6-0.6" />
|
||||
<path class="st3" d="M32.6,35L29.5,32c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S33,35.4,32.6,35z M37.7,23.8
|
||||
l3.1,3.1l0,0c0,0,0,0,0,0c-2,1.9-3.9,3.4-6.2,1.8l-0.3-0.2l-0.6-0.6" />
|
||||
<path class="st3" d="M40,27.6L37,24.5c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S40.5,28,40,27.6z M20.8,21.8
|
||||
l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
|
||||
<path class="st3" d="M20.1,22.5L17,19.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4S20.4,22.9,20.1,22.5z
|
||||
M28.2,14.3l-3.1-3.1l0,0c0,0,0,0,0,0c-1.9,2-3.4,3.9-1.8,6.2l0.2,0.3l0.6,0.6" />
|
||||
<path class="st3"
|
||||
d="M27.5,15L24.4,12c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l3.1,3.1c0.4,0.4,0.4,1,0,1.4C28.6,15.5,27.9,15.4,27.5,15z" />
|
||||
<g>
|
||||
<path class="st2" d="M20.3,36.7L15.7,32c-1.3-1.3-1.3-3.5,0-4.8l13.2-13.2c1.3-1.3,3.5-1.3,4.8,0l4.7,4.7c1.3,1.3,1.3,3.5,0,4.8
|
||||
L25.1,36.7C23.8,38,21.7,38,20.3,36.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st11" d="M33.6,18.5c-1.5-1.5-3.9-1.5-5.3,0c-1.5,1.5-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0
|
||||
S35,19.9,33.6,18.5C33.6,18.5,33.6,18.5,33.6,18.5z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M26,26c-1.5-1.5-3.9-1.5-5.3,0s-1.5,3.9,0,5.3c0,0,0,0,0,0c1.5,1.5,3.9,1.5,5.3,0C27.5,29.9,27.5,27.5,26,26
|
||||
C26,26,26,26,26,26z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M19.6,37.4l-4.7-4.7c-1.7-1.7-1.7-4.5,0-6.2l13.2-13.2c1.7-1.7,4.5-1.7,6.2,0l4.7,4.7c1.7,1.7,1.7,4.5,0,6.2
|
||||
L25.9,37.4C24.1,39.1,21.4,39.1,19.6,37.4z M33,14.7c-0.9-0.9-2.4-0.9-3.4,0L16.4,27.9c-0.9,0.9-0.9,2.4,0,3.4l4.7,4.7
|
||||
c0.9,0.9,2.4,0.9,3.4,0l13.2-13.2c0.9-0.9,0.9-2.4,0-3.4L33,14.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M10.3,41.7c-0.4-0.4-0.4-1,0-1.4l6.7-6.7c0.4-0.4,1-0.4,1.4,0c0.4,0.4,0.4,1,0,1.4l-6.7,6.7
|
||||
C11.4,42.1,10.7,42.1,10.3,41.7z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st5"
|
||||
d="M282.1,91.1l-24.8-24.8l-16.5,16.5c-1.5,1.5-1.5,4,0,5.5l19.3,19.3c1.5,1.5,4,1.5,5.5,0L282.1,91.1z" />
|
||||
<path class="st2" d="M257,85.2l11.7-2.9c0.4-0.1,0.6,0.4,0.3,0.6l-6.9,4.6l2.4,2.4c0.2,0.2,0.1,0.5-0.2,0.6l-10.6,1.7
|
||||
c-0.4,0.1-0.5-0.4-0.2-0.6l5.7-3.4l-2.3-2.3C256.6,85.6,256.7,85.3,257,85.2z" />
|
||||
<path class="st6" d="M279.4,88.4l-16,16c-0.3,0.3-0.8,0.3-1.1,0l-11.8-11.8c-0.8-0.8-2-0.8-2.8,0l0,0c-0.8,0.8-0.8,2,0,2.8
|
||||
l12.4,12.4c1.5,1.5,4,1.5,5.5,0l16.5-16.5L279.4,88.4z" />
|
||||
<path class="st2" d="M259.3,61.7l27.5,27.5l-3.3,3.3L256,65L259.3,61.7z M267.5,69.9l3.3-3.3l-4.1-4.1l-3.3,3.3 M282.7,85.1
|
||||
l3.3-3.3l-4.1-4.1l-3.3,3.3" />
|
||||
<path class="st3" d="M259.3,108.5L240,89.2c-2-2-2-5.2,0-7.2l13.8-13.8c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-13.8,13.8
|
||||
c-1.1,1.1-1.1,2.8,0,3.9l19.3,19.3c1.1,1.1,2.8,1.1,3.9,0l16.5-16.5c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-16.5,16.5
|
||||
C264.4,110.4,261.3,110.4,259.3,108.5z" />
|
||||
<path class="st2" d="M251,78.2c-0.5-0.5-0.5-1.2,0-1.7l8.3-8.3c0.5-0.5,1.2-0.5,1.7,0c0.5,0.5,0.5,1.2,0,1.7l-8.3,8.3
|
||||
C252.2,78.6,251.5,78.6,251,78.2z M246.9,82.3c-0.1-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-0.1-0.1-0.1-0.2c0-0.1,0-0.1-0.1-0.2
|
||||
c0-0.1,0-0.1,0-0.2c0-0.1,0-0.2,0-0.2c0-0.1,0-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1,0,0.1-0.1c0.1-0.1,0.1-0.1,0.2-0.1
|
||||
c0.1,0,0.1-0.1,0.2-0.1s0.2-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.2,0s0.1,0,0.2,0.1c0.1,0,0.1,0.1,0.2,0.1
|
||||
c0.3,0.2,0.5,0.6,0.5,1c0,0.1,0,0.2,0,0.2c0,0.1,0,0.1-0.1,0.2c0,0.1-0.1,0.1-0.1,0.2s-0.2,0.1-0.2,0.1c-0.2,0.2-0.5,0.4-0.8,0.3
|
||||
C247.4,82.7,247.1,82.5,246.9,82.3z" />
|
||||
<path class="st3" d="M282.7,93.3l-27.5-27.5c-0.5-0.5-0.5-1.2,0-1.7l3.3-3.3c0.5-0.5,1.2-0.5,1.7,0l27.5,27.5
|
||||
c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283.9,93.8,283.1,93.8,282.7,93.3z M257.6,65l25.9,25.9l1.7-1.7l-25.9-25.9L257.6,65z" />
|
||||
<path class="st3" d="M266.7,70.7c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7
|
||||
l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C267.9,71.2,267.2,71.2,266.7,70.7z M281.8,85.9
|
||||
c-0.5-0.5-0.5-1.2,0-1.7l2.5-2.5l-2.5-2.5l-0.3,0.3c-0.5,0.5-1.2,0.5-1.7,0c-0.5-0.5-0.5-1.2,0-1.7l1.1-1.1c0.5-0.5,1.2-0.5,1.7,0
|
||||
l4.1,4.1c0.5,0.5,0.5,1.2,0,1.7l-3.3,3.3C283,86.3,282.3,86.3,281.8,85.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st7" d="M308.8,72.2l34.3,34.3l3.8-3.8c2.1-2.1,2.1-5.5,0-7.6l-13.6-13.6l-0.6-0.2c-2.3-0.8-3-3.7-1.3-5.5l-1.2-1.2
|
||||
C324,68.5,316.8,68.8,308.8,72.2" />
|
||||
<path class="st8" d="M319.7,69.9C319.7,69.8,319.7,69.8,319.7,69.9c-0.4-0.1-0.7-0.1-1-0.1c-3.1,0.1-6.4,1-9.9,2.4l0,0l10.9,10.9
|
||||
C323.3,79.5,323.3,73.5,319.7,69.9 M348.4,98.8c-3.6-2.7-8.8-2.4-12.2,0.9l6.8,6.8l3.8-3.8C347.9,101.6,348.4,100.2,348.4,98.8" />
|
||||
<path class="st3" d="M342,107.6l-34.3-34.3c-0.4-0.4-0.5-0.8-0.4-1.3c0.1-0.5,0.4-0.9,0.9-1.1c9.8-4.2,17.1-3.3,23.1,2.8l0.1,0.1
|
||||
c0.6,0.6,0.6,1.5,0,2.1c-0.6,0.6-1.5,0.6-2.1,0l-0.1-0.1c-4.7-4.7-10.3-5.7-17.8-2.9l31.6,31.6l2.8-2.8c1.5-1.5,1.5-4,0-5.5
|
||||
L333.7,84c-0.4-0.4-0.5-0.8-0.4-1.3l0.6-3.8c0.1-0.8,0.9-1.4,1.7-1.2c0.8,0.1,1.4,0.9,1.2,1.7l-0.5,3L348,94c2.7,2.7,2.7,7.1,0,9.8
|
||||
l-3.8,3.8C343.6,108.2,342.6,108.2,342,107.6z" />
|
||||
<path class="st2" d="M316.9,72.7c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
C319,78.2,319,74.8,316.9,72.7C316.9,72.7,316.9,72.7,316.9,72.7z M336.7,88.2c-1.2-1.2-3.1-1.2-4.2,0c-1.2,1.2-1.2,3.1,0,4.2
|
||||
s3.1,1.2,4.2,0C337.8,91.2,337.8,89.3,336.7,88.2z" />
|
||||
<path class="st3" d="M308.2,81.4c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0s2.7,7.1,0,9.8C315.2,84.1,310.9,84.1,308.2,81.4z
|
||||
M315.8,73.7c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C317.3,77.7,317.3,75.3,315.8,73.7z" />
|
||||
<path class="st2" d="M346.7,102.5c-2.1-2.1-5.5-2.1-7.6,0c-2.1,2.1-2.1,5.5,0,7.6c0,0,0,0,0,0c2.1,2.1,5.5,2.1,7.6,0
|
||||
S348.8,104.6,346.7,102.5C346.7,102.5,346.7,102.5,346.7,102.5z" />
|
||||
<path class="st3" d="M338,111.2c-2.7-2.7-2.7-7.1,0-9.8s7.1-2.7,9.8,0c2.7,2.7,2.7,7.1,0,9.8C345.1,113.9,340.7,113.9,338,111.2z
|
||||
M345.7,103.6c-1.5-1.5-4-1.5-5.5,0c-1.5,1.5-1.5,4,0,5.5c1.5,1.5,4,1.5,5.5,0C347.2,107.6,347.2,105.1,345.7,103.6z" />
|
||||
<path class="st3" d="M338,111.2l-34.5-34.5c-0.6-0.6-0.6-1.5,0-2.1c0.6-0.6,1.5-0.6,2.1,0l34.5,34.5c0.6,0.6,0.6,1.5,0,2.1
|
||||
C339.5,111.8,338.6,111.8,338,111.2z M299.3,72.5c-0.3-0.3-0.5-0.7-0.4-1.1c0-0.4,0.1-0.8,0.4-1.1s0.7-0.5,1.1-0.4
|
||||
c0.8,0,1.5,0.7,1.5,1.5c0,0.4-0.1,0.8-0.4,1.1c-0.3,0.3-0.7,0.5-1.1,0.4C300,73,299.6,72.8,299.3,72.5z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M277.7,28.4c0-3,2.4-5.4,5.4-5.4s5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4 M264.6,15.2c0-3,2.4-5.4,5.4-5.4
|
||||
c3,0,5.4,2.4,5.4,5.4s-2.4,5.4-5.4,5.4" />
|
||||
<path class="st11" d="M275,44.3l-21-21c-0.2-0.2-0.2-0.6,0-0.9l6.8-6.8c0.2-0.2,0.6-0.2,0.9,0l21,21c0.2,0.2,0.2,0.6,0,0.9
|
||||
l-6.8,6.8C275.6,44.6,275.2,44.6,275,44.3z" />
|
||||
<path class="st2" d="M255.1,21.4l-1.1,1.1c-0.2,0.2-0.2,0.6,0,0.9l1.8,1.8L270,24l0,0l-3.8-3.8L255.1,21.4z M259.2,28.6l-0.2-0.2
|
||||
l4,4l14.2-1.1l0,0l-3.8-3.8L259.2,28.6z M281.6,38.7l1.1-1.1c0.2-0.2,0.2-0.6,0-0.9l-1.8-1.8L266.7,36l0,0l3.7,3.7L281.6,38.7z
|
||||
M271.6,13.6c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3c0.9,0.9,2.4,0.9,3.3,0C272.5,16,272.5,14.5,271.6,13.6z M284.8,26.7
|
||||
c-0.9-0.9-2.4-0.9-3.3,0s-0.9,2.4,0,3.3s2.4,0.9,3.3,0S285.7,27.6,284.8,26.7z" />
|
||||
<path class="st3" d="M274.3,45l-21-21c-0.6-0.6-0.6-1.6,0-2.2l6.8-6.8c0.6-0.6,1.6-0.6,2.2,0l21,21c0.6,0.6,0.6,1.6,0,2.2l-6.8,6.8
|
||||
C275.9,45.6,275,45.6,274.3,45z M254.9,22.9l20.6,20.6l6.3-6.3l-20.6-20.6L254.9,22.9z M250.3,34.9c-0.4-0.4-0.4-0.9,0-1.3l5-5
|
||||
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C251.2,35.3,250.7,35.3,250.3,34.9z M263.4,48.1c-0.4-0.4-0.4-0.9,0-1.3l5-5
|
||||
c0.4-0.4,0.9-0.4,1.3,0c0.4,0.4,0.4,0.9,0,1.3l-5,5C264.4,48.4,263.8,48.4,263.4,48.1z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,14 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>.cls-1{fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:2px;}</style>
|
||||
</defs>
|
||||
<title>connector_ccs</title>
|
||||
<path class="cls-1"
|
||||
d="M20,8.2a7.89,7.89,0,0,1-8,7.87A7.91,7.91,0,0,1,6.29,2.6H17.71A8,8,0,0,1,20,8.2Z" />
|
||||
<circle cx="12" cy="8.4" r="1.6" />
|
||||
<circle cx="9.6" cy="5" r="1" />
|
||||
<circle cx="14.4" cy="5" r="1" />
|
||||
<circle cx="9" cy="18.73" r="1.37" />
|
||||
<circle cx="15" cy="18.73" r="1.37" />
|
||||
<rect class="cls-1" x="6.25" y="16.07" width="11.51" height="5.33" rx="2.67" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 651 B |
@@ -1,13 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>connector_cee_blau</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<circle cx="16" cy="10" r="2" />
|
||||
<circle cx="8" cy="10" r="2" />
|
||||
<circle cx="12" cy="17" r="1.25" />
|
||||
<circle class="cls-2" cx="12" cy="12" r="9.5" />
|
||||
<circle cx="12" cy="22" r="1.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 530 B |
@@ -1,15 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>connector_cee_rot</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<circle cx="16" cy="8" r="2" />
|
||||
<circle cx="17" cy="14.5" r="2" />
|
||||
<circle cx="7" cy="14.5" r="2" />
|
||||
<circle cx="8" cy="8" r="2" />
|
||||
<circle cx="12" cy="18" r="1.25" />
|
||||
<circle class="cls-2" cx="12" cy="12" r="9.5" />
|
||||
<circle cx="12" cy="22" r="1.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 604 B |
@@ -1,22 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-2,.cls-3,.cls-4{fill:none;}.cls-2,.cls-3,.cls-4{stroke:#000;stroke-miterlimit:10;}.cls-3{stroke-width:2px;}.cls-4{stroke-width:0.5px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>connector_chademo</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<circle class="cls-2" cx="17" cy="12" r="2" />
|
||||
<path class="cls-3" d="M21,12A9,9,0,0,1,3,12a9.46,9.46,0,0,1,.06-1.07A9,9,0,0,1,21,12Z" />
|
||||
<rect x="18.05" y="3.61" width="1.69" height="2"
|
||||
transform="translate(2.27 14.71) rotate(-45)" />
|
||||
<rect x="4.27" y="3.61" width="1.69" height="2"
|
||||
transform="translate(5.46 11.49) rotate(-135)" />
|
||||
<circle class="cls-2" cx="7" cy="12" r="2" />
|
||||
<circle class="cls-2" cx="12" cy="7" r="2" />
|
||||
<line class="cls-4" x1="10.5" y1="5.5" x2="13.5" y2="8.5" />
|
||||
<line class="cls-4" x1="10.5" y1="8.5" x2="13.5" y2="5.5" />
|
||||
<circle class="cls-2" cx="12" cy="17" r="2" />
|
||||
<line class="cls-4" x1="10.5" y1="15.5" x2="13.5" y2="18.5" />
|
||||
<line class="cls-4" x1="10.5" y1="18.5" x2="13.5" y2="15.5" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,13 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>schuko</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<circle cx="16.5" cy="12" r="2" />
|
||||
<circle cx="7.5" cy="12" r="2" />
|
||||
<path class="cls-2" d="M21,12A9,9,0,0,1,3,12a9.46,9.46,0,0,1,.06-1.07A9,9,0,0,1,21,12Z" />
|
||||
<rect x="11" y="3" width="2" height="2" />
|
||||
<rect x="11" y="19" width="2" height="2" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 580 B |
@@ -1,23 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>connector_supercharger</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<path class="cls-2"
|
||||
d="M13.45,17.08a8.24,8.24,0,0,1-3.11.6,8.34,8.34,0,0,1-6-14.18H16.3a8.35,8.35,0,0,1,1.07,10.33" />
|
||||
<circle cx="10.34" cy="9.34" r="1.67" />
|
||||
<circle cx="15.35" cy="9.34" r="1.67" />
|
||||
<circle cx="12.84" cy="13.51" r="1.67" />
|
||||
<circle cx="7.84" cy="13.51" r="1.67" />
|
||||
<circle cx="5.34" cy="9.34" r="1.67" />
|
||||
<circle cx="7.84" cy="5.59" r="1" />
|
||||
<circle cx="12.84" cy="5.59" r="1.04" />
|
||||
<g id="T">
|
||||
<path id="path35"
|
||||
d="M18.18,22.23l1-5.48c.93,0,1.22.1,1.27.52a2.15,2.15,0,0,0,.93-.7,6.91,6.91,0,0,0-2.46-.6l-.71.88h0L17.46,16a7,7,0,0,0-2.46.6,2.22,2.22,0,0,0,.94.7c0-.42.33-.52,1.26-.52l1,5.48" />
|
||||
<path id="path37"
|
||||
d="M18.18,15.72a7.9,7.9,0,0,1,3.28.66,2.65,2.65,0,0,0,.2-.4,9.24,9.24,0,0,0-7,0,2.61,2.61,0,0,0,.19.4,7.94,7.94,0,0,1,3.29-.66h0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,19 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1,.cls-2,.cls-3{fill:none;}.cls-2,.cls-3{stroke:#000;stroke-miterlimit:10;}.cls-2{stroke-width:2px;}.cls-3{stroke-width:0.5px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>connector_typ1</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<circle cx="15.79" cy="8.26" r="1.89" />
|
||||
<circle cx="16.74" cy="14" r="1.18" />
|
||||
<circle cx="7.26" cy="14" r="1.18" />
|
||||
<circle cx="8.21" cy="8.26" r="1.89" />
|
||||
<circle cx="12" cy="17.74" r="1.89" />
|
||||
<circle class="cls-2" cx="12" cy="12.05" r="9" />
|
||||
<rect x="10.58" y="21.05" width="2.84" height="1.89" />
|
||||
<line class="cls-3" x1="10.5" y1="1" x2="13.5" y2="1" />
|
||||
<polygon points="13.5 0.4 13.5 2.5 15.5 3.5 14.5 0.5 13.5 0.4" />
|
||||
<polygon points="10.5 0.4 10.5 2.5 8.5 3.5 9.5 0.5 10.5 0.4" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 913 B |
@@ -1,16 +0,0 @@
|
||||
<svg id="Ebene_5" data-name="Ebene 5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style>.cls-1,.cls-2{fill:none;}.cls-2{stroke:#000;stroke-miterlimit:10;stroke-width:2px;}
|
||||
</style>
|
||||
</defs>
|
||||
<title>connector_typ2</title>
|
||||
<path class="cls-1" d="M12,12H36V36H12Z" />
|
||||
<path class="cls-2" d="M22,10.5a10,10,0,1,1-17.14-7H19.14A10,10,0,0,1,22,10.5Z" />
|
||||
<circle cx="12" cy="10.5" r="2" />
|
||||
<circle cx="18" cy="10.5" r="2" />
|
||||
<circle cx="15" cy="15.5" r="2" />
|
||||
<circle cx="9" cy="15.5" r="2" />
|
||||
<circle cx="6" cy="10.5" r="2" />
|
||||
<circle cx="9" cy="6" r="1.25" />
|
||||
<circle cx="15" cy="6" r="1.25" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 678 B |
|
Before Width: | Height: | Size: 71 KiB |
@@ -1,473 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1024 500"
|
||||
style="enable-background:new 0 0 1024 500;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E6E6E6;}
|
||||
.st1{fill:#DCDCDC;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
.st3{fill:#808080;}
|
||||
.st4{fill:#E8E8E8;}
|
||||
.st5{fill:#99F5C8;}
|
||||
.st6{opacity:0.26;fill:#1D1D1B;enable-background:new ;}
|
||||
.st7{fill:#C8C8C8;}
|
||||
.st8{fill:#B5B5B5;}
|
||||
.st9{fill:#878787;}
|
||||
.st10{fill:#666666;}
|
||||
.st11{fill:#D1D1D1;}
|
||||
.st12{opacity:0.2;fill:#808080;enable-background:new ;}
|
||||
.st13{opacity:0.5;}
|
||||
.st14{fill:#FFB300;}
|
||||
.st15{fill:#90A4AE;}
|
||||
.st16{fill:#546E7A;}
|
||||
.st17{fill:#00E676;}
|
||||
.st18{fill:#FFFFFF;fill-opacity:0.2;}
|
||||
.st19{fill:#3E2723;fill-opacity:0.2;}
|
||||
.st20{opacity:0.45;enable-background:new ;}
|
||||
.st21{font-family:'Roboto-Light';}
|
||||
.st22{font-size:136.5333px;}
|
||||
</style>
|
||||
<g id="Ebene_1">
|
||||
<rect y="-34.4" class="st0" width="1024" height="568.9" />
|
||||
<g>
|
||||
<path class="st1"
|
||||
d="M135.7,135.4l8.5,8.5l-5.4,5.4l21.6,21.6l10.5-10.5l-21.6-21.6l-5.1,5.1" />
|
||||
<path class="st2"
|
||||
d="M145.4,335.9L38.1,228.7c-6-6-6-15.4,0-21.3L91,154.1c5.7-5.7,15.4-5.7,21.3,0l107.2,107.2" />
|
||||
<path class="st2" d="M131.7,209.9L93.6,248c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
|
||||
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C134.8,202.2,134.5,207,131.7,209.9z" />
|
||||
<path class="st3" d="M223.3,265.1c-2,2-5.4,2-7.4,0l-107.2-107c-3.7-3.7-10.2-4-13.9,0L41.8,211c-3.7,3.7-3.7,10,0,13.9L149,332.2
|
||||
c2,2,2,5.4,0,7.4c-2,2-5.4,2-7.4,0L34.4,232.4c-8-8-8-20.8,0-28.7l52.9-53.2c8-8,20.8-8,28.7,0l107.2,107.2
|
||||
C225.3,260,225.3,263.1,223.3,265.1z" />
|
||||
<path class="st4" d="M131.7,209.9L93.6,248c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
|
||||
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C134.8,202.2,134.5,207,131.7,209.9z" />
|
||||
<path class="st3" d="M135.4,213.6l-38.1,38.1c-4.8,4.8-13.1,4.8-17.9,0L54.6,227c-4.8-4.8-4.8-13.1,0-17.9l38.1-38.1
|
||||
c4.8-4.8,13.1-4.8,17.9,0l24.7,24.7C140.5,200.5,140.5,208.5,135.4,213.6z M62,216.4c-0.9,0.9-0.9,2.3,0,3.1l24.7,24.7
|
||||
c0.9,0.9,2.3,0.9,3.1,0l38.1-38.1c0.9-0.9,0.9-2.3,0-3.1l-24.7-24.7c-0.9-0.9-2.3-0.9-3.1,0L62,216.4z M233.8,254.6l-95.3,95.3
|
||||
c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l95.3-95.3c2-2,5.4-2,7.4,0S235.8,252.6,233.8,254.6z M228.4,238.3c-4.8,4.8-13.1,4.8-17.9,0
|
||||
l-43-42.7c-0.9-0.9-2.3-0.9-3.1,0l-5.4,5.4c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l5.4-5.4c4.8-4.8,13.1-4.8,17.9,0l43,43
|
||||
c0.9,0.9,2.3,0.9,3.1,0c0.9-0.9,0.9-2.3,0-3.1l-58.9-59.2c-2-2-2-5.4,0-7.4c2-2,5.4-2,7.4,0l58.9,58.9
|
||||
C233.2,225.3,233.2,233.5,228.4,238.3z" />
|
||||
<path class="st3" d="M174.6,163.8l-10.5,10.5c-2,2-5.4,2-7.4,0l-21.6-21.6c-2-2-2-5.4,0-7.4l1.7-1.7l-4.8-4.6c-2-2-2-5.4,0-7.4
|
||||
c2-2,5.4-2,7.4,0l4.8,4.8l1.4-1.4c2-2,5.4-2,7.4,0l21.3,21.3C176.4,158.4,176.6,161.8,174.6,163.8z M160.4,163.2l3.1-3.1
|
||||
l-13.9-13.9l-3.1,3.1L160.4,163.2z" />
|
||||
<g>
|
||||
<path class="st5" d="M163.8,290.1c-0.6,0.6-1.4,1.1-2.6,1.4c-2.8,0.6-5.7-1.1-6.3-3.7l-3.7-16.2l-3.7,5.4c-1.1,1.7-3.1,2.6-5.1,2
|
||||
c-2-0.6-3.7-2-4-4l-6.5-27c-0.6-2.8,1.1-5.7,3.7-6.3c2.8-0.6,5.7,1.1,6.3,3.7l3.7,16.2l3.7-5.4c1.1-1.7,3.1-2.6,5.1-2
|
||||
c2,0.6,3.7,2,4,4l6.3,27C165.5,287.3,165,289,163.8,290.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M85,381.1l68.3,68.3c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0L35,431.2c-2.8-2.8-2.8-7.1,0-10
|
||||
l40.1-40.1C77.9,378.3,82.2,378.3,85,381.1z M32.7,399l20.2-20.2l11.9,11.9L44.7,411L32.7,399z" />
|
||||
<path class="st5" d="M105.2,401l48.4,48.4c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0l-48.4-48.4
|
||||
c-2.8-2.8-2.8-7.1,0-10L95.3,401C97.8,398.2,102.4,398.2,105.2,401z" />
|
||||
<path class="st6" d="M143.4,449.4L143.4,449.4c2.8-2.8,2.8-7.1,0-10L105.2,401c-2.8-2.8-7.1-2.8-10,0l-5.1,5.1l43.2,43.2
|
||||
C136.2,452.2,140.5,452.2,143.4,449.4z" />
|
||||
<path class="st3" d="M156.4,462.5l-40.1,40.1c-4.6,4.6-11.7,4.6-16.2,0l-68.3-68.3c-4.6-4.6-4.6-11.7,0-16.2L72,378
|
||||
c4.6-4.6,11.7-4.6,16.2,0l68.3,68.3C161,450.8,161,457.9,156.4,462.5z M37.8,424.4c-1.1,1.1-1.1,2.8,0,4l68.3,68.3
|
||||
c1.1,1.1,2.8,1.1,4,0l40.1-40.1c1.1-1.1,1.1-2.8,0-4l-68.3-68.3c-1.1-1.1-2.8-1.1-4,0L37.8,424.4z" />
|
||||
<path class="st2" d="M111.2,487.5c-1.7,1.7-4.3,1.7-6,0l-25-25c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l25,25
|
||||
C112.9,483.2,112.9,485.8,111.2,487.5z M71.1,447.4c-0.9,0.9-2,1.4-3.1,1.1c-1.1,0-2.3-0.3-3.1-1.1c-0.9-0.9-1.4-2-1.1-3.1
|
||||
c0-0.3,0-0.6,0-0.9s0-0.6,0.3-0.9s0.3-0.6,0.3-0.9c0.9-1.1,2-2,3.4-2c1.1,0,2.3,0.3,3.1,1.1c0.3,0.3,0.3,0.3,0.6,0.6
|
||||
c0.3,0.3,0.3,0.6,0.3,0.9c0,0.3,0.3,0.6,0.3,0.9s0,0.6,0,0.9C72.2,445.4,71.7,446.6,71.1,447.4z" />
|
||||
<path class="st3" d="M68,393.9c-1.7,1.7-4.3,1.7-6,0l-9.1-9.1L39,398.8l1.1,1.1c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-4-4
|
||||
c-1.7-1.7-1.7-4.3,0-6l20.2-20.2c1.7-1.7,4.3-1.7,6,0l11.9,11.9C69.7,389.7,69.7,392.2,68,393.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M333.4,497.2l-3.1,3.1L229.5,399.6l73.4-73.4c5.4-5.4,14.5-5.4,20.2,0l70.3,70.3l0,0
|
||||
C390.3,425.8,354.1,476.4,333.4,497.2z" />
|
||||
<path class="st5" d="M293.5,387.7l48.1-11.7c1.7-0.3,2.6,1.7,1.1,2.6l-28.4,18.8l9.7,9.7c0.9,0.9,0.3,2.3-0.9,2.3l-43.8,6.8
|
||||
c-1.4,0.3-2.3-1.7-1.1-2.6l23.6-14.5l-9.4-9.4C292.4,389.1,292.7,388,293.5,387.7z" />
|
||||
<path class="st3" d="M374.6,440l-15.1-15.1c-7.1-7.1-7.1-18.8,0-26.2l8-8c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-8,8
|
||||
c-4,4-4,10.2,0,13.9l15.1,15.1c1.7,1.7,1.7,4.3,0,6C378.9,441.4,376,441.4,374.6,440z" />
|
||||
<path class="st3" d="M327.4,503.2L226.7,402.5c-1.7-1.7-1.7-4.3,0-6l73.4-73.4c7.1-7.1,18.8-7.1,26.2,0l70.3,70.3l0,0l8,8
|
||||
c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-2.3-2.3c-8.2,31.3-41.5,76.2-60,94.7l-3.1,3.1C331.7,504.9,328.8,504.9,327.4,503.2z
|
||||
M235.8,399.6l94.4,94.4c21.6-21.6,54.6-69.1,58.9-96.1l-68.8-68.8c-4-4-10.2-4-13.9,0C306.1,329.4,235.8,399.6,235.8,399.6z" />
|
||||
<path class="st3" d="M327.4,503.2L222.7,398.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l104.7,104.7c1.7,1.7,1.7,4.3,0,6
|
||||
C331.7,504.9,328.8,504.9,327.4,503.2z" />
|
||||
<path class="st2" d="M251.2,410.1c-5.4-5.4-13.9-5.4-19.3,0s-5.4,13.9,0,19.3l0,0c5.4,5.4,13.9,5.4,19.3,0
|
||||
S256.6,415.3,251.2,410.1L251.2,410.1z M315.4,474.4c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0
|
||||
c5.4,5.4,13.9,5.4,19.3,0S320.9,479.8,315.4,474.4L315.4,474.4z" />
|
||||
<path class="st3" d="M228.7,432.3c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c6.8,6.8,6.8,18.5,0,25.3
|
||||
C247.2,439.4,235.8,439.4,228.7,432.3z M248,413c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4s9.7,3.7,13.4,0
|
||||
C251.7,422.7,251.7,416.7,248,413z" />
|
||||
<g>
|
||||
<path class="st3" d="M293.3,496.6c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c7.1,7.1,6.8,18.5,0,25.3
|
||||
C311.5,503.7,300.1,503.7,293.3,496.6z M312.6,477.6c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4s9.7,3.7,13.4,0
|
||||
C316.3,487.2,316.3,481.3,312.6,477.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M293.3,496.6l-84.5-84.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l84.5,84.5c1.7,1.7,1.7,4.3,0,6
|
||||
C297.5,498.3,295,498.3,293.3,496.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M194.6,398.2c-0.3-0.3-0.3-0.3-0.6-0.6c-0.3-0.3-0.3-0.6-0.3-0.9c0-0.3-0.3-0.6-0.3-0.9s0-0.6,0-0.9
|
||||
c0-0.3,0-0.6,0-0.9c0-0.3,0-0.6,0.3-0.9c0.3,0,0.3-0.3,0.6-0.6s0.3-0.6,0.6-0.6c0.3,0,0.3-0.3,0.6-0.6c0.3-0.3,0.6-0.3,0.9-0.3
|
||||
c0.3,0,0.6,0,0.9-0.3c0.3-0.3,0.6,0,0.9,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9,0.3c0.3,0,0.6,0.3,0.9,0.3c0.6,0.3,0.9,0.6,1.1,1.1
|
||||
c0.3,0.3,0.3,0.6,0.3,0.9c0,0.3,0.3,0.6,0.3,0.9c0,0.3,0,0.6,0,0.9s0,0.6,0,0.9c0,0.3,0,0.6-0.3,0.9c-0.3,0.3-0.3,0.6-0.3,0.9
|
||||
c-0.3,0.3-0.3,0.6-0.6,0.6s-0.3,0.3-0.6,0.6c-0.3,0.3-0.6,0.3-0.9,0.3c-0.3,0-0.6,0.3-0.9,0.3s-0.6,0-0.9,0c-0.3,0-0.6,0-0.9,0
|
||||
c-0.3,0-0.6,0-0.9-0.3c-0.3,0-0.6-0.3-0.9-0.3C195.1,398.8,194.8,398.5,194.6,398.2z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M956.9,61.7l-38.7,38.7c-5.4,5.4-14.5,5.4-20.2,0l-39.3-39.3c-5.4-5.4-5.4-14.5,0-20.2l38.7-38.4
|
||||
c5.4-5.4,14.5-5.4,20.2,0l39.3,39.3C962.3,47.2,962.3,56.3,956.9,61.7z" />
|
||||
<path class="st3" d="M959.7,64.8L921,103.5c-7.1,7.1-18.8,7.1-26.2,0l-39.3-39.3c-7.1-7.1-7.1-18.8,0-26.2l38.7-38.7
|
||||
c7.1-7.1,18.8-7.1,26.2,0l39.3,39.3C967.1,45.8,967.1,57.4,959.7,64.8z M861.9,44.1c-4,4-4,10.2,0,13.9l39.3,39.3
|
||||
c4,4,10.2,4,13.9,0l38.7-38.7c4-4,4-10.2,0-13.9L914.5,5.4c-4-4-10.2-4-13.9,0L861.9,44.1z" />
|
||||
<path class="st2" d="M925.6,69.1L925.6,69.1c-9.7,9.7-25.6,9.7-35.3,0l-0.3-0.3c-9.7-9.7-9.7-25.6,0-35.3l0,0
|
||||
c9.7-9.7,25.6-9.7,35.3,0l0.3,0.3C935.3,43.8,935.3,59.4,925.6,69.1z" />
|
||||
<path class="st3" d="M928.7,72.2c-11.4,11.4-29.9,11.4-41.2,0l-0.3-0.3c-11.4-11.4-11.4-29.9,0-41.2c11.4-11.4,29.9-11.4,41.2,0
|
||||
l0.3,0.3C940.1,42.4,940.1,60.8,928.7,72.2z M893.2,36.7c-8,8-8,21,0,29.3l0.3,0.3c8,8,21,8,29.3,0c8-8,8-21,0-29.3l-0.3-0.3
|
||||
C914.2,28.7,901.1,28.7,893.2,36.7z" />
|
||||
<path class="st3" d="M896.9,55.2c-2,2-2,5.1,0,7.1c2,2,5.1,2,7.1,0l0,0c2-2,2-5.1,0-7.1C902.3,53.2,898.8,53.2,896.9,55.2
|
||||
L896.9,55.2z M911.4,40.6c-2,2-2,5.1,0,7.1c2,2,5.1,2,7.1,0l0,0c2-2,2-5.1,0-7.1C916.8,38.7,913.4,38.7,911.4,40.6L911.4,40.6z" />
|
||||
<path class="st2"
|
||||
d="M971.9,88.7l10.8,10.8c7.4,7.4,7.4,19.3,0,26.7l0,0c-7.4,7.4-19.3,7.4-26.7,0l-10.8-10.8L971.9,88.7z" />
|
||||
<path class="st3" d="M985.9,129.4c-9.1,9.1-23.6,9.1-32.7,0l-8.8-8.8c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l8.8,8.8
|
||||
c5.7,5.7,15.1,5.7,20.8,0c5.7-5.7,5.7-15.1,0-20.8l-8.8-8.8c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l8.8,8.8
|
||||
C995,105.8,995,120.3,985.9,129.4z" />
|
||||
<path class="st3" d="M953.2,112.9c-1.7,1.7-4.3,1.7-6,0l-9.4-9.4c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l9.4,9.4
|
||||
C954.9,108.6,954.9,111.2,953.2,112.9z M969.4,97c-1.7,1.7-4.3,1.7-6,0l-9.4-9.4c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0
|
||||
l9.4,9.4C971.1,92.4,971.1,95.3,969.4,97z" />
|
||||
<path class="st3" d="M978.8,88.2l-34.1,34.1c-1.7,1.7-4.3,1.7-6,0c-1.7-1.7-1.7-4.3,0-6l34.1-34.1c1.7-1.7,4.3-1.7,6,0
|
||||
C980.5,83.9,980.5,86.4,978.8,88.2z M994.4,137.9c-1.7,1.7-4.3,1.7-6,0l-8.5-8.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0
|
||||
l8.5,8.5C995.8,133.4,996.1,136.2,994.4,137.9z" />
|
||||
<g>
|
||||
<path class="st5" d="M966.5,110.1c-1.4,1.4-1.4,4,0,5.4c1.4,1.4,4,1.4,5.4,0l0,0c1.4-1.4,1.4-4,0-5.4
|
||||
C970.5,108.6,968,108.6,966.5,110.1L966.5,110.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st7" d="M486.4,389.4l97.6,97.6l10.8-10.8c6-6,6-15.6,0-21.6l-38.7-38.7l-1.7-0.6c-6.5-2.3-8.5-10.5-3.7-15.6
|
||||
l-3.4-3.4C529.9,378.6,509.4,379.4,486.4,389.4" />
|
||||
<path class="st8" d="M517.7,382.6C517.4,382.6,517.4,382.6,517.7,382.6c-1.1-0.3-2-0.3-2.8-0.3c-8.8,0.3-18.2,2.8-28.2,6.8l0,0
|
||||
l31,31C527.9,410.1,527.9,393.1,517.7,382.6 M599.3,465c-10.2-7.7-25-6.8-34.7,2.6l19.3,19.3l10.8-10.8
|
||||
C597.9,473,599.3,469,599.3,465" />
|
||||
<path class="st3" d="M581.1,490.1l-97.6-97.8c-1.1-1.1-1.4-2.3-1.1-3.7c0.3-1.4,1.1-2.6,2.6-3.1c27.9-11.9,48.6-9.4,65.7,8
|
||||
l0.3,0.3c1.7,1.7,1.7,4.3,0,6s-4.3,1.7-6,0l-0.3-0.3c-13.4-13.4-29.3-16.2-50.6-8.2L584,481l8-8c4.3-4.3,4.3-11.4,0-15.6
|
||||
l-34.4-34.4c-1.1-1.1-1.4-2.3-1.1-3.7l1.7-10.8c0.3-2.3,2.6-4,4.8-3.4c2.3,0.3,4,2.6,3.4,4.8l-1.4,8.5l33,33
|
||||
c7.7,7.7,7.7,20.2,0,27.9l-10.8,10.8C585.4,491.8,582.8,491.5,581.1,490.1z" />
|
||||
<path class="st2" d="M509.4,390.5c-6-6-15.6-6-21.6,0c-6,6-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0
|
||||
C515.7,406.4,515.7,396.8,509.4,390.5L509.4,390.5z M565.8,434.6c-3.4-3.4-8.8-3.4-11.9,0c-3.1,3.4-3.4,8.8,0,11.9
|
||||
c3.4,3.1,8.8,3.4,11.9,0C569.2,443.4,569.2,438,565.8,434.6z" />
|
||||
<path class="st3" d="M484.7,415.3c-7.7-7.7-7.7-20.2,0-27.9s20.2-7.7,27.9,0c7.7,7.7,7.7,20.2,0,27.9
|
||||
C504.9,422.9,492.4,422.9,484.7,415.3z M506.6,393.6c-4.3-4.3-11.4-4.3-15.6,0c-4.3,4.3-4.3,11.4,0,15.6c4.3,4.3,11.4,4.3,15.6,0
|
||||
C510.9,405,510.9,397.9,506.6,393.6z" />
|
||||
<path class="st2" d="M594.5,475.6c-6-6-15.6-6-21.6,0s-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0S600.5,481.5,594.5,475.6L594.5,475.6z
|
||||
" />
|
||||
<path class="st3" d="M569.7,500.3c-7.7-7.7-7.7-20.2,0-27.9s20.2-7.7,27.9,0s7.7,20.2,0,27.9C589.9,508,577.4,508,569.7,500.3z
|
||||
M591.4,478.4c-4.3-4.3-11.4-4.3-15.6,0s-4.3,11.4,0,15.6s11.4,4.3,15.6,0S595.6,483,591.4,478.4z" />
|
||||
<path class="st3" d="M569.7,500.3l-98.1-98.1c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l98.1,98.1c1.7,1.7,1.7,4.3,0,6
|
||||
C574,502,571.4,502,569.7,500.3z M459.7,390.2c-0.9-0.9-1.4-2-1.1-3.1c0-1.1,0.3-2.3,1.1-3.1c0.9-0.9,2-1.4,3.1-1.1
|
||||
c2.3,0,4.3,2,4.3,4.3c0,1.1-0.3,2.3-1.1,3.1c-0.9,0.9-2,1.4-3.1,1.1C461.4,391.4,460.5,391.1,459.7,390.2z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M306.9,39.8l-35-35.3c-7.7-7.7-19.9-8.2-28.2-1.7L218.2,23l70.3,70.3L308.6,68
|
||||
C315.4,59.4,314.6,47.2,306.9,39.8z" />
|
||||
<path class="st2"
|
||||
d="M306.9,39.8l-17.6-17.6l-36.1,36.1l35.3,35.3l20.2-25.3C315.4,59.4,314.6,47.2,306.9,39.8z" />
|
||||
<path class="st9" d="M223.9,17l70.3,70.3L260,121.4l-70.3-70.3L223.9,17z" />
|
||||
<path class="st10" d="M259.1,52l35.3,35.3l-34.1,34.1l-35.3-35L259.1,52z" />
|
||||
<path class="st9" d="M217.9,23L217.9,23c-5.4-5.4-14.5-5.4-20.2,0l-18.2,18.2l20.2,20.2l18.2-18.2C223.6,37.5,223.6,28.4,217.9,23
|
||||
z M270.2,131.7l18.2-18.2c5.4-5.4,5.4-14.5,0-20.2l0,0c-5.4-5.4-14.5-5.4-20.2,0L250,111.5" />
|
||||
<path class="st3" d="M196.8,64.3c-1.7-1.7-1.7-4.3,0-6L215,40.1c4-4,4-10.2,0-13.9c-4-4-10.2-4-13.9,0l-18.2,18.2
|
||||
c-1.7,1.7-4.3,1.7-6,0s-1.7-4.3,0-6l18.2-18.2c7.1-7.1,18.8-7.1,26.2,0s7.1,18.8,0,26.2l-18.2,18.2C201.1,66,198.5,66,196.8,64.3z
|
||||
M267.1,134.5c-1.7-1.7-1.7-4.3,0-6l18.2-18.2c4-4,4-10.2,0-13.9c-4-3.7-10.2-4-13.9,0l-18.2,18.2c-1.7,1.7-4.3,1.7-6,0
|
||||
c-1.7-1.7-1.7-4.3,0-6l18.2-18.2c7.1-7.1,18.8-7.1,26.2,0c7.1,7.1,7.1,18.8,0,26.2l-18.2,18.2
|
||||
C271.6,136.2,268.8,136.2,267.1,134.5z" />
|
||||
<path class="st3" d="M289.6,90.1l-0.3-0.3c-19.1-25.9-41.8-48.6-67.4-67.7c-1.7-1.4-2.3-4-0.9-6c1.4-1.7,4-2.3,6-0.9
|
||||
c26.5,19.6,49.8,43,69.4,69.4c1.4,1.7,1.1,4.6-0.9,6C293.5,91.6,291.3,91.6,289.6,90.1z" />
|
||||
<path class="st3" d="M215,25.9l-0.3-0.3c-1.4-1.7-1.1-4.6,0.6-6l25.6-20.2c10.2-8,24.7-7.1,34.1,2l35,35.3c9.4,9.4,10,23.9,2,34.1
|
||||
l-20.2,25.3c-1.4,1.7-4,2-6,0.6c-2-1.4-2-4-0.6-6l20.2-25.3c5.4-6.8,4.8-16.5-1.1-22.8L268.8,7.4c-6.3-6.3-15.9-6.8-22.8-1.4
|
||||
l-25.6,20.2C218.7,27.8,216.5,27.6,215,25.9z M264.2,125.7l-13.9,13.9l-8-8l13.9-13.9 M193.7,55.2l-13.9,13.9l-8-8l13.9-13.9" />
|
||||
<path class="st3" d="M247.2,142.8l-8.2-8.2c-1.7-1.7-1.7-4.3,0-6l13.9-13.9c1.7-1.7,4.3-1.7,6,0s1.7,4.3,0,6l-11.1,11.1l2,2
|
||||
l11.1-11.1c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-13.7,14.2C251.4,144.5,248.9,144.5,247.2,142.8z M176.6,72.2l-8-8
|
||||
c-1.7-1.7-1.7-4.3,0-6l13.9-13.9c1.7-1.7,4.3-1.7,6,0s1.7,4.3,0,6l-11.1,11.1l2,2l11.1-11.1c1.7-1.7,4.3-1.7,6,0
|
||||
c1.7,1.7,1.7,4.3,0,6l-13.9,13.9C180.9,73.9,178.3,73.9,176.6,72.2z" />
|
||||
<path class="st3" d="M270.2,137.6l-96.4-96.4c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l96.4,96.4c1.7,1.7,1.7,4.3,0,6
|
||||
C274.5,139.4,271.9,139.4,270.2,137.6z" />
|
||||
<path class="st2" d="M239.5,88.7l-16.8-16.8c-1.7-1.7-1.7-4.3,0-6l10-10c6.3-6.3,16.5-6.3,23,0l0,0c6.3,6.3,6.3,16.5,0,23l-10,10
|
||||
C243.8,90.4,241.2,90.4,239.5,88.7z" />
|
||||
<path class="st3" d="M302.4,29l-3.1,3.1L279,11.9l3.1-3.1l0,0c3.4-3.4,8.8-3.4,11.9,0l8,8C305.8,20.2,305.8,25.6,302.4,29
|
||||
L302.4,29z" />
|
||||
<path class="st2" d="M213,28.1c-2.8-2.8-7.4-2.8-10,0c-2.8,2.8-2.8,7.4,0,10c2.8,2.8,7.4,2.8,10,0C215.6,35.2,215.6,30.7,213,28.1
|
||||
z M283.3,98.4c-2.8-2.8-7.4-2.8-10,0s-2.8,7.4,0,10s7.4,2.8,10,0C285.9,105.5,286.2,101.2,283.3,98.4z" />
|
||||
<path class="st3" d="M253.2,148.7L169,64.5c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l84.2,84.2c1.7,1.7,1.7,4.3,0,6
|
||||
C257.4,150.4,254.9,150.4,253.2,148.7z M274.2,157.8c-1.7-1.7-4.3-1.7-6,0s-1.7,4.3,0,6c1.7,1.7,4.3,1.7,6,0
|
||||
C275.9,162.1,275.9,159.5,274.2,157.8z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M395.7,154.4l68.3,68.3c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0l-68.3-68.3
|
||||
c-2.8-2.8-2.8-7.1,0-10l40.1-40.1C388.3,151.6,392.8,151.6,395.7,154.4z M343.3,172.3l20.2-20.2l11.9,11.9l-20.2,20.2L343.3,172.3
|
||||
z" />
|
||||
<path class="st5" d="M415.6,174.3l48.4,48.4c2.8,2.8,2.8,7.1,0,10l-40.1,40.1c-2.8,2.8-7.1,2.8-10,0l-48.4-48.4
|
||||
c-2.8-2.8-2.8-7.1,0-10l40.1-40.1C408.5,171.5,412.7,171.5,415.6,174.3z" />
|
||||
<path class="st6" d="M454,222.7L454,222.7c2.8-2.8,2.8-7.1,0-10l-38.1-38.1c-2.8-2.8-7.1-2.8-10,0l-5.1,5.1l43,43
|
||||
C446.6,225.5,451.1,225.5,454,222.7z" />
|
||||
<path class="st3" d="M467.1,235.8l-40.4,40.1c-4.6,4.6-11.7,4.6-16.2,0l-68.3-68.3c-4.6-4.6-4.6-11.7,0-16.2l40.1-40.1
|
||||
c4.6-4.6,11.7-4.6,16.2,0l68.3,68.3C471.3,224.1,471.3,231.2,467.1,235.8z M348.2,197.7c-1.1,1.1-1.1,2.8,0,4l68.3,68.3
|
||||
c1.1,1.1,2.8,1.1,4,0l40.4-40.1c1.1-1.1,1.1-2.8,0-4l-68.3-68.3c-1.1-1.1-2.8-1.1-4,0L348.2,197.7z" />
|
||||
<path class="st2" d="M421.5,260.8c-1.7,1.7-4.3,1.7-6,0l-25-25c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l25,25
|
||||
C423.3,256.5,423.3,259.1,421.5,260.8z M381.4,220.7c-0.9,0.9-2,1.4-3.1,1.1c-1.1,0-2.3-0.3-3.1-1.1c-0.9-0.9-1.4-2-1.1-3.1
|
||||
c0-0.3,0-0.6,0-0.9c0-0.3,0-0.6,0.3-0.9c0.3-0.3,0.3-0.6,0.3-0.9c0.9-1.1,2-2,3.4-2c1.1,0,2.3,0.3,3.1,1.1
|
||||
c0.3,0.3,0.3,0.3,0.6,0.6c0.3,0.3,0.3,0.6,0.3,0.9c0,0.3,0.3,0.6,0.3,0.9c0,0.3,0,0.6,0,0.9C382.9,218.7,382.3,219.8,381.4,220.7z
|
||||
" />
|
||||
<path class="st3" d="M378.3,167.5c-1.7,1.7-4.3,1.7-6,0l-9.1-9.1l-13.9,13.9l1.1,1.1c1.7,1.7,1.7,4.3,0,6s-4.3,1.7-6,0l-4-4
|
||||
c-1.7-1.7-1.7-4.3,0-6l20.2-20.2c1.7-1.7,4.3-1.7,6,0l11.9,11.9C380,163,380,165.8,378.3,167.5z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M896,289.5l8.5,8.5l-5.4,5.4l21.6,21.6l10.5-10.5L909.7,293l-5.1,5.1" />
|
||||
<path class="st2"
|
||||
d="M905.4,490.4L798.2,383.1c-6-6-6-15.4,0-21.3l53.2-53.2c5.7-5.7,15.4-5.7,21.3,0l107.2,107.2" />
|
||||
<path class="st2" d="M892,364.3l-38.1,38.1c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
|
||||
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C894.9,356.7,894.9,361.5,892,364.3z" />
|
||||
<path class="st3" d="M983.3,419.5c-2,2-5.4,2-7.4,0l-107-107c-3.7-3.7-10.2-4-13.9,0l-53.2,53.2c-3.7,3.7-3.7,10,0,13.9
|
||||
l107.2,107.2c2,2,2,5.4,0,7.4c-2,2-5.4,2-7.4,0L794.5,387.1c-8-8-8-20.8,0-28.7l53.2-53.2c8-8,20.8-8,28.7,0l107.2,107.2
|
||||
C985.6,414.4,985.3,417.5,983.3,419.5z" />
|
||||
<path class="st4" d="M892,364.3l-38.1,38.1c-2.8,2.8-7.7,2.8-10.5,0l-24.7-24.7c-2.8-2.8-2.8-7.7,0-10.5l38.1-38.1
|
||||
c2.8-2.8,7.7-2.8,10.5,0l24.7,24.7C894.9,356.7,894.9,361.5,892,364.3z" />
|
||||
<path class="st3" d="M895.7,368l-38.1,38.1c-4.8,4.8-13.1,4.8-17.9,0l-24.7-24.7c-4.8-4.8-4.8-13.1,0-17.9l38.1-38.1
|
||||
c4.8-4.8,13.1-4.8,17.9,0l24.7,24.7C900.6,355,900.6,362.9,895.7,368z M822.3,370.9c-0.9,0.9-0.9,2.3,0,3.1l24.7,24.7
|
||||
c0.9,0.9,2.3,0.9,3.1,0l38.1-38.1c0.9-0.9,0.9-2.3,0-3.1l-24.7-24.7c-0.9-0.9-2.3-0.9-3.1,0L822.3,370.9z M994.1,409l-95.3,95.3
|
||||
c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l95.3-95.3c2-2,5.4-2,7.4,0C996.1,403.6,996.1,407,994.1,409z M988.4,392.8
|
||||
c-4.8,4.8-13.1,4.8-17.9,0l-43-43c-0.9-0.9-2.3-0.9-3.1,0l-5.4,5.4c-2,2-5.4,2-7.4,0c-2-2-2-5.4,0-7.4l5.4-5.4
|
||||
c4.8-4.8,13.1-4.8,17.9,0l43,43c0.9,0.9,2.3,0.9,3.1,0c0.9-0.9,0.9-2.3,0-3.1l-58.9-58.9c-2-2-2-5.4,0-7.4c2-2,5.4-2,7.4,0
|
||||
l58.9,58.9C993.3,379.7,993.3,388,988.4,392.8z" />
|
||||
<path class="st3" d="M934.7,318.3l-10.5,10.5c-2,2-5.4,2-7.4,0l-21.6-21.6c-2-2-2-5.4,0-7.4l1.7-1.7l-4.8-4.8c-2-2-2-5.4,0-7.4
|
||||
c2-2,5.4-2,7.4,0l4.8,4.8l1.4-1.4c2-2,5.4-2,7.4,0l21.3,21.3C936.7,312.9,936.7,316.3,934.7,318.3z M920.5,317.7l3.1-3.1
|
||||
l-13.9-13.9l-3.1,3.1L920.5,317.7z" />
|
||||
<g>
|
||||
<path class="st5" d="M923.9,444.6c-0.6,0.6-1.4,1.1-2.6,1.4c-2.8,0.6-5.7-1.1-6.3-3.7l-3.7-16.2l-3.7,5.4c-1.1,1.7-3.1,2.6-5.1,2
|
||||
c-2-0.6-3.7-2-4-4l-6.5-27c-0.6-2.8,1.1-5.7,3.7-6.3c2.8-0.6,5.7,1.1,6.3,3.7l3.7,16.2l3.7-5.4c1.1-1.7,3.1-2.6,5.1-2
|
||||
c2,0.6,3.7,2,4,4l6.3,27C925.6,441.7,925.3,443.4,923.9,444.6z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st11"
|
||||
d="M665,452.5l43.5,43.5l45.2-74.5c0.9-1.4,0.9-3.1-0.6-4.6l-9.4-9.4c-1.1-1.1-2.8-1.4-4.6-0.6L665,452.5z" />
|
||||
<path class="st2" d="M695.5,434l-17.6,10.8l38.4,38.4l0,0l10.8-17.6L695.5,434z M728.5,413.8l-17.6,10.8l25.6,25.6l10.8-17.6
|
||||
L728.5,413.8z" />
|
||||
<path class="st12" d="M749.8,413.3l3.7,3.7c1.1,1.1,1.4,2.8,0.6,4.6L708.6,496l-10-10l51.8-68.3
|
||||
C751.2,416.1,750.9,414.4,749.8,413.3z" />
|
||||
<g>
|
||||
<path class="st3" d="M706.6,498L663,454.5c-0.6-0.6-0.9-1.4-0.9-2.3c0-0.9,0.6-1.7,1.4-2l74.5-45.2c2.6-1.4,5.7-1.1,8,0.9
|
||||
l9.4,9.4c2,2,2.6,5.4,0.9,8l-45.5,74.2c-0.6,0.9-1.1,1.1-2,1.4C708,498.9,707.1,498.6,706.6,498z M669.6,453.1l38.4,38.4
|
||||
l43.5-71.7c0.3-0.3,0.3-0.9,0-0.9l-9.4-9.4c-0.3-0.3-0.6-0.3-0.9,0L669.6,453.1z" />
|
||||
<path class="st3" d="M655.6,447.1c-1.1-1.1-1.1-2.8,0-4s2.8-1.1,4,0l58,58l0,0c1.1,1.1,1.1,2.8,0,4s-2.8,1.1-4,0L655.6,447.1
|
||||
L655.6,447.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M523.4,274.7l35.8-36.1c11.1-11.1,29.3-11.1,40.1,0l64.3,64.3c7.7,7.7,10.2,19.1,7.1,29.3l-1.1,3.4
|
||||
c-6.3,18.2-17.6,34.4-32.7,46.6l-5.7,4.8c-3.1,2.6-7.1-2-4.3-4.8l0,0" />
|
||||
<path class="st2" d="M642.8,376.3l0.3,0.3l0,0c3.4-3.4,6.8-7.4,10-11.1l0.3-0.3c0.6-0.6,1.1-1.4,1.4-2c0.3-0.3,0.3-0.3,0.3-0.6
|
||||
c0.6-0.6,0.9-1.4,1.4-2c0.3-0.3,0.3-0.6,0.6-0.6c0.3-0.6,0.9-1.4,1.4-2c0.3-0.3,0.3-0.3,0.3-0.6c0.9-1.4,1.7-2.8,2.6-4
|
||||
c0,0,0.3-0.3,0.3-0.6c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.6,0.6-0.9,0.9-1.4s0.6-0.9,0.6-1.4c0.3-0.6,0.6-1.1,0.9-1.7
|
||||
c0.3-0.3,0.6-0.9,0.6-1.4c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.3,0.3-0.9,0.6-1.4c0.3-0.6,0.6-1.4,0.9-2c0-0.3,0.3-0.6,0.3-1.1
|
||||
c0.3-1.1,0.6-2,1.1-3.1l1.1-3.4c3.4-10.2,0.6-21.6-7.1-29.3l-64-64c-11.1-11.1-29.3-11.1-40.1,0l-8,8l87.6,87.6
|
||||
C650,345.6,651.7,362.9,642.8,376.3z" />
|
||||
<path class="st2" d="M595.1,278.7l-10-10c-2.8-2.8-2.8-7.1,0-10l2-2c2.8-2.8,7.1-2.8,10,0l10,10c2.8,2.8,2.8,7.1,0,10l-2,2
|
||||
C602.2,281.6,597.9,281.3,595.1,278.7z M620.1,303.8l-10-10c-2.8-2.8-2.8-7.1,0-10l2-2c2.8-2.8,7.1-2.8,10,0l10,10
|
||||
c2.8,2.8,2.8,7.1,0,10l-2,2C627.5,306.6,622.9,306.6,620.1,303.8z M664.2,347.8c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.3,0.6-0.9,0.6-1.4
|
||||
c0.3-0.6,0.6-1.1,0.9-1.7c0.3-0.3,0.3-0.9,0.6-1.4c0.3-0.6,0.6-1.4,0.9-2c0-0.3,0.3-0.6,0.3-1.1c0.3-1.1,0.6-2,1.1-3.1l1.1-3.4
|
||||
c0.3-0.6,0.3-1.1,0.6-2l-23.3-23.3c-2.8-2.8-7.1-2.8-10,0l-2.3,2.3c-2.8,2.8-2.8,7.1,0,10L664.2,347.8L664.2,347.8z" />
|
||||
<path class="st3" d="M623.8,389.9l-0.3-0.3c-2.8-2.8-2.6-7.4,0.3-10.2c1.7-1.7,4.3-1.7,6,0c0.6,0.6,1.1,1.4,1.1,2.3l3.1-2.6
|
||||
c14.5-11.7,25-27.3,31.3-44.9l1.1-3.4c2.8-8.8,0.6-18.2-6-24.7l-64.3-64.3c-9.4-9.4-24.7-9.4-34.1,0l-35.8,35.8
|
||||
c-1.7,1.7-4.3,1.7-6,0c-1.7-1.7-1.7-4.3,0-6l35.8-35.8c12.8-12.8,33.6-12.8,46.4,0l64.3,64.3c8.8,8.8,11.9,21.6,8,33.6l-1.1,3.4
|
||||
c-6.5,19.1-18.2,35.8-33.8,48.6l-5.7,4.8C630.6,392.8,626.6,392.5,623.8,389.9z M644.8,388.2c-1.7-1.7-1.7-4.3,0-6l18.2-18.2
|
||||
c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-18.2,18.2C649.4,389.9,646.5,389.9,644.8,388.2z" />
|
||||
<path class="st3" d="M599.6,361.2l-46.9-46.9c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l47.2,46.9c1.7,1.7,1.7,4.3,0,6
|
||||
C604.2,362.9,601.3,362.9,599.6,361.2z M531.3,292.7L516,277.3c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l15.4,15.4c1.7,1.7,1.7,4.3,0,6
|
||||
C535.6,294.4,533,294.4,531.3,292.7z M671.9,299.2l-66.6-66.6c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l66.6,66.6c1.7,1.7,1.7,4.3,0,6
|
||||
S673.6,300.9,671.9,299.2z M640.3,379.7c-1.7-1.7-1.7-4-0.3-6c7.7-8.8,5.4-26.5-4.6-36.4l-87.6-87.6c-1.7-1.7-1.7-4.3,0-6
|
||||
s4.3-1.7,6,0l87.6,87.6c13.1,13.1,15.4,35.8,4.8,48.1C644.8,381.1,642.3,381.4,640.3,379.7L640.3,379.7z M565.2,246.9l-6.3-6.3
|
||||
c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l6.3,6.3c1.7,1.7,1.7,4.3,0,6C569.7,248.6,566.9,248.6,565.2,246.9z M577.4,242.9
|
||||
l-6.3-6.3c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l6.3,6.3c1.7,1.7,1.7,4.3,0,6C581.7,244.6,579.1,244.6,577.4,242.9z" />
|
||||
<path class="st2" d="M623.5,359.5c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0c5.4,5.4,13.9,5.4,19.3,0
|
||||
C628.6,373.4,628.6,364.9,623.5,359.5L623.5,359.5z" />
|
||||
<path class="st3" d="M601,382c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c6.8,6.8,7.1,18.5,0,25.3
|
||||
C619.5,389.1,608.1,388.8,601,382z M620.4,362.6c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0
|
||||
C624.1,372.3,624.1,366.3,620.4,362.6z" />
|
||||
<path class="st2" d="M535.3,290.7c-5.4,5.4-5.4,13.9,0,19.3c5.4,5.4,13.9,5.4,19.3,0c2.3-2.3,3.7-5.1,4-8.2l-15.1-15.1
|
||||
C540.4,287.3,537.6,288.4,535.3,290.7z" />
|
||||
<path class="st3" d="M532.5,313.1c-7.1-7.1-6.8-18.5,0-25.3c2.8-2.8,6.5-4.6,10.8-5.1c1.4,0,2.6,0.3,3.4,1.1l15.1,15.1
|
||||
c0.9,0.9,1.4,2.3,1.1,3.4c-0.3,4-2.3,8-5.1,10.8C550.7,320.3,539.3,320,532.5,313.1z M542.2,291.5c-1.4,0.3-2.6,1.1-3.7,2.3
|
||||
c-3.7,3.7-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0c1.1-1.1,2-2.3,2.3-3.7L542.2,291.5z" />
|
||||
<g>
|
||||
<path class="st3" d="M619.2,244.6c-0.6-0.6-1.1-1.7-1.1-2.8l-0.6-14.8c0-2.3,1.7-4.3,4-4.6c2.3,0,4.3,1.7,4.6,4l0.6,14.8
|
||||
c0,2.3-1.7,4.3-4,4.6C621.2,245.7,620.1,245.2,619.2,244.6z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M623.8,385.4l-2.6-2.6c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l2.6,2.6c1.7,1.7,1.7,4.3,0,6
|
||||
C628.1,387.1,625.5,387.1,623.8,385.4z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M554.4,172.9l-3.1,3.1L450.6,75.4L523.9,2c5.4-5.4,14.5-5.4,20.2,0l70.3,70.3l0,0
|
||||
C611.3,101.5,575.1,152.2,554.4,172.9z" />
|
||||
<path class="st5" d="M514.8,63.4l48.1-11.7c1.7-0.3,2.6,1.7,1.1,2.6l-28.4,18.8l9.7,9.7c0.9,0.9,0.3,2.3-0.9,2.3l-43.8,6.8
|
||||
c-1.4,0.3-2.3-1.7-1.1-2.6l23.6-14.5l-9.4-9.4C513.4,64.8,513.7,63.7,514.8,63.4z" />
|
||||
<path class="st3" d="M595.6,115.5l-15.1-15.1c-7.1-7.1-7.1-18.8,0-26.2l8-8c1.7-1.7,4.3-1.7,6,0c1.7,1.7,1.7,4.3,0,6l-8,8
|
||||
c-4,4-4,10.2,0,13.9l15.1,15.1c1.7,1.7,1.7,4.3,0,6C599.9,117.2,597.3,117.2,595.6,115.5z" />
|
||||
<path class="st3" d="M548.4,178.9L447.7,78.2c-1.7-1.7-1.7-4.3,0-6l73.4-73.4c7.1-7.1,18.8-7.1,26.2,0l70.3,70.3l0,0l8,8
|
||||
c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-2.3-2.3c-8.2,31.3-41.5,76.2-60,94.7l-3.1,3.1C552.7,180.6,549.8,180.6,548.4,178.9z
|
||||
M456.8,75.4l94.4,94.4c21.6-21.6,54.6-69.1,58.9-96.1L541.3,4.8c-4-4-10.2-4-13.9,0C527.1,5.1,456.8,75.4,456.8,75.4z" />
|
||||
<path class="st3" d="M548.4,178.9L443.7,74.2c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l104.7,104.7c1.7,1.7,1.7,4.3,0,6
|
||||
C552.7,180.6,549.8,180.6,548.4,178.9z" />
|
||||
<path class="st2" d="M472.2,85.6c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0c5.4,5.4,13.9,5.4,19.3,0
|
||||
C477.6,99.8,477.6,91,472.2,85.6L472.2,85.6z M536.5,150.2c-5.4-5.4-13.9-5.4-19.3,0c-5.4,5.4-5.4,13.9,0,19.3l0,0
|
||||
c5.4,5.4,13.9,5.4,19.3,0C541.9,164.1,541.9,155.3,536.5,150.2L536.5,150.2z" />
|
||||
<path class="st3" d="M450,108.1c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c6.8,6.8,6.8,18.5,0,25.3
|
||||
C468.2,114.9,456.8,115.2,450,108.1z M469,88.7c-3.7-3.7-9.7-3.7-13.4,0s-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0
|
||||
C472.7,98.4,473,92.4,469,88.7z" />
|
||||
<g>
|
||||
<path class="st3" d="M514.3,172.3c-7.1-7.1-6.8-18.5,0-25.3c6.8-6.8,18.5-6.8,25.3,0c7.1,7.1,6.8,18.5,0,25.3
|
||||
C532.8,179.5,521.4,179.5,514.3,172.3z M533.6,153c-3.7-3.7-9.7-3.7-13.4,0c-3.7,3.7-3.7,9.7,0,13.4c3.7,3.7,9.7,3.7,13.4,0
|
||||
C537,162.7,537.3,156.7,533.6,153z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M514.3,172.3l-84.5-84.5c-1.7-1.7-1.7-4.3,0-6c1.7-1.7,4.3-1.7,6,0l84.5,84.5c1.7,1.7,1.7,4.3,0,6
|
||||
C518.5,174.1,516,174.1,514.3,172.3z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M415.6,73.9c-0.3-0.3-0.3-0.3-0.6-0.6c0-0.3-0.3-0.6-0.3-0.9s-0.3-0.6-0.3-0.9s0-0.6,0-0.9s0-0.6,0-0.9
|
||||
c0-0.3,0-0.6,0.3-0.9c0.3,0,0.3-0.3,0.6-0.6c0.3-0.3,0.3-0.6,0.6-0.6c0.3,0,0.3-0.3,0.6-0.6c0.3-0.3,0.6-0.3,0.9-0.3
|
||||
s0.6,0,0.9-0.3s0.6,0,0.9,0c0.3,0,0.6,0,0.9,0c0.3,0,0.6,0,0.9,0.3c0.3,0.3,0.6,0.3,0.9,0.3c0.6,0.3,0.9,0.6,1.1,1.1
|
||||
c0.3,0.3,0.3,0.6,0.3,0.9s0.3,0.6,0.3,0.9c0,0.3,0,0.6,0,0.9c0,0.3,0,0.6,0,0.9s0,0.6-0.3,0.9c0,0.3-0.3,0.6-0.3,0.9
|
||||
c-0.3,0.3-0.3,0.6-0.6,0.6c-0.3,0-0.3,0.3-0.6,0.6c-0.3,0.3-0.6,0.3-0.9,0.3s-0.6,0.3-0.9,0.3c-0.3,0-0.6,0-0.9,0
|
||||
c-0.3,0-0.6,0-0.9,0s-0.6,0-0.9-0.3c-0.3,0-0.6-0.3-0.9-0.3C416.1,74.5,415.9,73.9,415.6,73.9z" />
|
||||
</g>
|
||||
</g>
|
||||
<path class="st3"
|
||||
d="M85.9,54.3l8.8,8.8l0,0l0,0c-5.7,5.4-11.1,9.7-17.6,5.1L76.2,68l-1.7-1.7" />
|
||||
<path class="st3" d="M92.7,65.1l-8.8-8.5c-1.1-1.1-1.1-2.8,0-4s2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4
|
||||
C95.6,66.5,93.9,66.2,92.7,65.1z M107.2,33.3l8.8,8.8l0,0l0,0c-5.7,5.4-11.1,9.7-17.6,5.1l-0.9-0.6l-1.7-1.7" />
|
||||
<path class="st3" d="M113.8,44.1l-8.5-8.8c-1.1-1.1-1.1-2.8,0-4c1.1-1.1,2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4
|
||||
C116.9,45.2,115.2,45.2,113.8,44.1z M59.2,27.6l-8.8-8.8l0,0l0,0c-5.4,5.7-9.7,11.1-5.1,17.6l0.6,0.9l1.7,1.7" />
|
||||
<path class="st3" d="M57.2,29.6l-8.8-8.8c-1.1-1.1-1.1-2.8,0-4c1.1-1.1,2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4S58,30.7,57.2,29.6
|
||||
z M80.2,6.2l-8.8-8.8l0,0l0,0C66,3.1,61.7,8.5,66.3,15l0.6,0.9l1.7,1.7" />
|
||||
<path class="st3" d="M78.2,8.2l-8.8-8.5c-1.1-1.1-1.1-2.8,0-4c1.1-1.1,2.8-1.1,4,0l8.8,8.8c1.1,1.1,1.1,2.8,0,4
|
||||
C81.4,9.6,79.4,9.4,78.2,8.2z" />
|
||||
<g>
|
||||
<path class="st2" d="M57.7,69.9L44.7,56.6c-3.7-3.7-3.7-10,0-13.7L82.2,5.4c3.7-3.7,10-3.7,13.7,0l13.4,13.4
|
||||
c3.7,3.7,3.7,10,0,13.7L71.4,69.9C67.7,73.6,61.7,73.6,57.7,69.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st11" d="M95.6,18.2c-4.3-4.3-11.1-4.3-15.1,0c-4.3,4.3-4.3,11.1,0,15.1l0,0c4.3,4.3,11.1,4.3,15.1,0
|
||||
S99.6,22.2,95.6,18.2L95.6,18.2z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M74,39.5c-4.3-4.3-11.1-4.3-15.1,0s-4.3,11.1,0,15.1l0,0c4.3,4.3,11.1,4.3,15.1,0
|
||||
C78.2,50.6,78.2,43.8,74,39.5L74,39.5z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M55.8,71.9L42.4,58.6c-4.8-4.8-4.8-12.8,0-17.6L79.9,3.4c4.8-4.8,12.8-4.8,17.6,0l13.4,13.4
|
||||
c4.8,4.8,4.8,12.8,0,17.6L73.7,71.9C68.6,76.8,60.9,76.8,55.8,71.9z M93.9,7.4c-2.6-2.6-6.8-2.6-9.7,0L46.6,44.9
|
||||
c-2.6,2.6-2.6,6.8,0,9.7L60,68c2.6,2.6,6.8,2.6,9.7,0l37.5-37.5c2.6-2.6,2.6-6.8,0-9.7L93.9,7.4z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M29.3,84.2c-1.1-1.1-1.1-2.8,0-4l19.1-19.1c1.1-1.1,2.8-1.1,4,0s1.1,2.8,0,4L33.3,84.2
|
||||
C32.4,85.3,30.4,85.3,29.3,84.2z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st5" d="M802.4,224.7l-70.5-70.5l-46.9,46.9c-4.3,4.3-4.3,11.4,0,15.6l54.9,54.9c4.3,4.3,11.4,4.3,15.6,0L802.4,224.7
|
||||
z" />
|
||||
<path class="st2" d="M731,207.9l33.3-8.2c1.1-0.3,1.7,1.1,0.9,1.7l-19.6,13.1l6.8,6.8c0.6,0.6,0.3,1.4-0.6,1.7l-30.2,4.8
|
||||
c-1.1,0.3-1.4-1.1-0.6-1.7l16.2-9.7l-6.5-6.5C729.9,209,730.2,208.2,731,207.9z" />
|
||||
<path class="st6" d="M794.7,217l-45.5,45.5c-0.9,0.9-2.3,0.9-3.1,0L712.5,229c-2.3-2.3-5.7-2.3-8,0l0,0c-2.3,2.3-2.3,5.7,0,8
|
||||
l35.3,35.3c4.3,4.3,11.4,4.3,15.6,0l46.9-46.9L794.7,217z" />
|
||||
<path class="st2" d="M737.6,141.1l78.2,78.2l-9.4,9.4l-78.2-78.2L737.6,141.1z M760.9,164.4l9.4-9.4l-11.7-11.7l-9.4,9.4
|
||||
M804.1,207.6l9.4-9.4l-11.7-11.7l-9.4,9.4" />
|
||||
<path class="st3" d="M737.6,274.2l-54.9-54.9c-5.7-5.7-5.7-14.8,0-20.5l39.3-39.3c1.4-1.4,3.4-1.4,4.8,0s1.4,3.4,0,4.8l-39.3,39.3
|
||||
c-3.1,3.1-3.1,8,0,11.1l54.9,54.9c3.1,3.1,8,3.1,11.1,0l46.9-46.9c1.4-1.4,3.4-1.4,4.8,0c1.4,1.4,1.4,3.4,0,4.8l-46.9,46.9
|
||||
C752.1,279.6,743.3,279.6,737.6,274.2z" />
|
||||
<path class="st2" d="M714,188c-1.4-1.4-1.4-3.4,0-4.8l23.6-23.6c1.4-1.4,3.4-1.4,4.8,0c1.4,1.4,1.4,3.4,0,4.8L718.8,188
|
||||
C717.4,189.1,715.4,189.1,714,188z M702.3,199.7c-0.3-0.3-0.3-0.3-0.3-0.6c-0.3-0.3-0.3-0.3-0.3-0.6c0-0.3,0-0.3-0.3-0.6
|
||||
c0-0.3,0-0.3,0-0.6s0-0.6,0-0.6c0-0.3,0-0.3,0.3-0.6c0-0.3,0.3-0.3,0.3-0.6s0.3,0,0.3-0.3c0.3-0.3,0.3-0.3,0.6-0.3
|
||||
c0.3,0,0.3-0.3,0.6-0.3s0.6-0.3,0.6-0.3c0.3,0,0.3,0,0.6,0s0.6,0,0.6,0s0.3,0,0.6,0.3c0.3,0,0.3,0.3,0.6,0.3
|
||||
c0.9,0.6,1.4,1.7,1.4,2.8c0,0.3,0,0.6,0,0.6c0,0.3,0,0.3-0.3,0.6c0,0.3-0.3,0.3-0.3,0.6s-0.6,0.3-0.6,0.3
|
||||
c-0.6,0.6-1.4,1.1-2.3,0.9C703.7,200.8,702.9,200.2,702.3,199.7z" />
|
||||
<path class="st3" d="M804.1,230.9l-78.2-78.2c-1.4-1.4-1.4-3.4,0-4.8l9.4-9.4c1.4-1.4,3.4-1.4,4.8,0l78.2,78.2
|
||||
c1.4,1.4,1.4,3.4,0,4.8l-9.4,9.4C807.5,232.4,805.3,232.4,804.1,230.9z M732.7,150.4l73.7,73.7l4.8-4.8l-73.7-73.7L732.7,150.4z" />
|
||||
<path class="st3" d="M758.6,166.7c-1.4-1.4-1.4-3.4,0-4.8l7.1-7.1l-7.1-7.1l-0.9,0.9c-1.4,1.4-3.4,1.4-4.8,0
|
||||
c-1.4-1.4-1.4-3.4,0-4.8l3.1-3.1c1.4-1.4,3.4-1.4,4.8,0l11.7,11.7c1.4,1.4,1.4,3.4,0,4.8l-9.4,9.4
|
||||
C762,168.1,760,168.1,758.6,166.7z M801.6,209.9c-1.4-1.4-1.4-3.4,0-4.8l7.1-7.1l-7.1-7.1l-0.9,0.9c-1.4,1.4-3.4,1.4-4.8,0
|
||||
c-1.4-1.4-1.4-3.4,0-4.8l3.1-3.1c1.4-1.4,3.4-1.4,4.8,0l11.7,11.7c1.4,1.4,1.4,3.4,0,4.8l-9.4,9.4C805,211,803,211,801.6,209.9z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st7" d="M878.4,170.9l97.6,97.6l10.8-10.8c6-6,6-15.6,0-21.6l-38.7-38.7l-1.7-0.6c-6.5-2.3-8.5-10.5-3.7-15.6
|
||||
l-3.4-3.4C921.6,160.4,901.1,161.3,878.4,170.9" />
|
||||
<path class="st8" d="M909.4,164.4C909.4,164.1,909.4,164.1,909.4,164.4c-1.1-0.3-2-0.3-2.8-0.3c-8.8,0.3-18.2,2.8-28.2,6.8l0,0
|
||||
l31,31C919.6,191.7,919.6,174.6,909.4,164.4 M991,246.6c-10.2-7.7-25-6.8-34.7,2.6l19.3,19.3l10.8-10.8
|
||||
C989.6,254.6,991,250.6,991,246.6" />
|
||||
<path class="st3" d="M972.8,271.6l-97.6-97.6c-1.1-1.1-1.4-2.3-1.1-3.7c0.3-1.4,1.1-2.6,2.6-3.1c27.9-11.9,48.6-9.4,65.7,8
|
||||
l0.3,0.3c1.7,1.7,1.7,4.3,0,6c-1.7,1.7-4.3,1.7-6,0l-0.3-0.3c-13.4-13.4-29.3-16.2-50.6-8.2l89.9,89.9l8-8
|
||||
c4.3-4.3,4.3-11.4,0-15.6l-34.4-34.7c-1.1-1.1-1.4-2.3-1.1-3.7l1.7-10.8c0.3-2.3,2.6-4,4.8-3.4c2.3,0.3,4,2.6,3.4,4.8l-1.4,8.5
|
||||
l33.3,33c7.7,7.7,7.7,20.2,0,27.9l-10.8,10.8C977.4,273.3,974.5,273.3,972.8,271.6z" />
|
||||
<path class="st2" d="M901.4,172.3c-6-6-15.6-6-21.6,0c-6,6-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0
|
||||
C907.4,188,907.4,178.3,901.4,172.3L901.4,172.3z M957.7,216.4c-3.4-3.4-8.8-3.4-11.9,0c-3.4,3.4-3.4,8.8,0,11.9
|
||||
c3.4,3.1,8.8,3.4,11.9,0C960.9,225,960.9,219.6,957.7,216.4z" />
|
||||
<path class="st3" d="M876.7,197.1c-7.7-7.7-7.7-20.2,0-27.9c7.7-7.7,20.2-7.7,27.9,0s7.7,20.2,0,27.9
|
||||
C896.6,204.8,884.3,204.8,876.7,197.1z M898.3,175.2c-4.3-4.3-11.4-4.3-15.6,0c-4.3,4.3-4.3,11.4,0,15.6c4.3,4.3,11.4,4.3,15.6,0
|
||||
C902.5,186.6,902.5,179.7,898.3,175.2z" />
|
||||
<path class="st2" d="M986.2,257.1c-6-6-15.6-6-21.6,0c-6,6-6,15.6,0,21.6l0,0c6,6,15.6,6,21.6,0
|
||||
C992.1,272.8,992.1,263.1,986.2,257.1L986.2,257.1z" />
|
||||
<path class="st3" d="M961.4,281.9c-7.7-7.7-7.7-20.2,0-27.9c7.7-7.7,20.2-7.7,27.9,0c7.7,7.7,7.7,20.2,0,27.9
|
||||
C981.6,289.5,969.1,289.5,961.4,281.9z M983.3,260.2c-4.3-4.3-11.4-4.3-15.6,0s-4.3,11.4,0,15.6c4.3,4.3,11.4,4.3,15.6,0
|
||||
C987.6,271.6,987.6,264.5,983.3,260.2z" />
|
||||
<path class="st3" d="M961.4,281.9l-98.1-98.1c-1.7-1.7-1.7-4.3,0-6s4.3-1.7,6,0l98.1,98.1c1.7,1.7,1.7,4.3,0,6
|
||||
S963.1,283.6,961.4,281.9z M851.3,171.8c-0.9-0.9-1.4-2-1.1-3.1c0-1.1,0.3-2.3,1.1-3.1c0.9-0.9,2-1.4,3.1-1.1c2.3,0,4.3,2,4.3,4.3
|
||||
c0,1.1-0.3,2.3-1.1,3.1c-0.9,0.9-2,1.4-3.1,1.1C853.3,173.2,852.2,172.6,851.3,171.8z" />
|
||||
</g>
|
||||
<g>
|
||||
<path class="st3" d="M789.9,46.3c0-8.5,6.8-15.4,15.4-15.4c8.5,0,15.4,6.8,15.4,15.4s-6.8,15.4-15.4,15.4 M752.6,8.8
|
||||
c0-8.5,6.8-15.4,15.4-15.4s15.4,6.8,15.4,15.4s-6.8,15.4-15.4,15.4" />
|
||||
<path class="st11" d="M782.2,91.6l-59.7-59.7c-0.6-0.6-0.6-1.7,0-2.6l19.3-19.3c0.6-0.6,1.7-0.6,2.6,0l59.7,59.7
|
||||
c0.6,0.6,0.6,1.7,0,2.6l-19.3,19.3C783.9,92.4,782.8,92.4,782.2,91.6z" />
|
||||
<path class="st2" d="M725.6,26.4l-3.1,3.1c-0.6,0.6-0.6,1.7,0,2.6l5.1,5.1l40.4-3.4l0,0L757.2,23L725.6,26.4z M737.3,46.9
|
||||
l-0.6-0.6l11.4,11.4l40.4-3.1l0,0l-10.8-10.8L737.3,46.9z M801,75.6l3.1-3.1c0.6-0.6,0.6-1.7,0-2.6l-5.1-5.1L758.6,68l0,0
|
||||
l10.5,10.5L801,75.6z M772.6,4.2c-2.6-2.6-6.8-2.6-9.4,0s-2.6,6.8,0,9.4s6.8,2.6,9.4,0C775.1,11.1,775.1,6.8,772.6,4.2z
|
||||
M810.1,41.5c-2.6-2.6-6.8-2.6-9.4,0c-2.6,2.6-2.6,6.8,0,9.4c2.6,2.6,6.8,2.6,9.4,0C812.7,48.3,812.7,44.1,810.1,41.5z" />
|
||||
<path class="st3" d="M780.2,93.6l-59.7-59.7c-1.7-1.7-1.7-4.6,0-6.3l19.3-19.3c1.7-1.7,4.6-1.7,6.3,0L805.8,68
|
||||
c1.7,1.7,1.7,4.6,0,6.3l-19.3,19.3C784.8,95.3,782.2,95.3,780.2,93.6z M725,30.7l58.6,58.6l17.9-17.9L743,12.8L725,30.7z
|
||||
M712,64.8c-1.1-1.1-1.1-2.6,0-3.7l14.2-14.2c1.1-1.1,2.6-1.1,3.7,0c1.1,1.1,1.1,2.6,0,3.7l-14.2,14.2C714.5,66,713.1,66,712,64.8
|
||||
z M749.2,102.4c-1.1-1.1-1.1-2.6,0-3.7l14.2-14.2c1.1-1.1,2.6-1.1,3.7,0c1.1,1.1,1.1,2.6,0,3.7l-14.2,14.2
|
||||
C752.1,103.2,750.4,103.2,749.2,102.4z" />
|
||||
</g>
|
||||
<rect y="-34.4" class="st13" width="1024" height="568.9" />
|
||||
</g>
|
||||
<g id="Ebene_2">
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st14"
|
||||
d="M195.1,301.8l-4-35.1l-6.1,0.6l4,35.1L195.1,301.8z M216.9,299.2l-4-35.1l-6.1,0.6l4,35.1L216.9,299.2z" />
|
||||
<path class="st15" d="M228.3,347c-1.9,2.3-3.4,3.8-3.6,4c-5.5,4.4-9.9,5.7-13.5,4c-6.3-3.2-5.9-15-5.7-16.3l4.4,0.2
|
||||
c-0.2,3.4,0.4,10.6,3.4,12c1.7,0.8,4.6-0.2,8.5-3.4l0,0c0,0,12.3-12.3,9.7-22c-3-11.6,10.6-28.3,15-34l0.6-0.6l3.6,2.7l-0.6,0.8
|
||||
c-13.7,16.9-15.2,25.6-14.2,30C238,333,232.3,342.1,228.3,347z" />
|
||||
<path class="st15"
|
||||
d="M181.6,301.3l1.7,14.4l13.3,8.9l18-1.9l11-11.6l-1.7-14.4L181.6,301.3z" />
|
||||
<g>
|
||||
<path class="st16" d="M214.3,322.7l-18,1.9l3.8,15.8l14.2-1.7V322.7L214.3,322.7z M227.2,284.6l1.5,13.1l-51.5,5.9l-1.5-13.1
|
||||
L227.2,284.6z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st17" d="M267.6,140.1c-37,0-67,30-67,67c0,50.5,56.4,76.9,63.2,148.9c0.2,2.1,1.9,3.6,4,3.6c2.1,0,3.8-1.5,4-3.6
|
||||
c6.8-72,63.2-98.4,63.2-148.9C334.5,169.9,304.5,140.1,267.6,140.1z" />
|
||||
<path class="st18" d="M267.6,141.6c36.8,0,66.5,29.6,67,66.1c0-0.2,0-0.4,0-0.6c0-37-30-67-67-67s-67,29.8-67,67
|
||||
c0,0.2,0,0.4,0,0.6C201,171.2,230.8,141.6,267.6,141.6L267.6,141.6z" />
|
||||
<path class="st19" d="M271.6,354.4c-0.2,2.1-1.9,3.6-4,3.6s-3.8-1.5-4-3.6c-6.5-71.8-62.5-98.2-63-148.1c0,0.4,0,0.6,0,1.1
|
||||
c0,50.5,56.4,76.9,63.2,148.9c0.2,2.1,1.9,3.6,4,3.6c2.1,0,3.8-1.5,4-3.6c6.8-72,63.2-98.4,63.2-148.9c0-0.4,0-0.6,0-1.1
|
||||
C334.1,256.1,278.1,282.5,271.6,354.4L271.6,354.4z" />
|
||||
</g>
|
||||
<path class="st20"
|
||||
d="M252.2,174.4v40.6h11v33.2l25.8-44.4h-14.8l14.8-29.6C289.1,174.4,252.2,174.4,252.2,174.4z" />
|
||||
</g>
|
||||
</g>
|
||||
<text transform="matrix(1 0 0 1 417.7245 289.5375)" class="st2 st21 st22">EVMap</text>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#DC4B3E;}
|
||||
.st1{fill:#802C27;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M109.8,0h13.6c33.9,1.9,67.1,18.5,87.7,45.8c13.5,17.2,21,38.6,22.7,60.3v8.1c-0.8,42.1-27.7,76.6-51,109.4
|
||||
c-26.2,37-50.4,77.3-57.1,122.9c-1.8,7.7,0.4,18.5-8.9,22c-2.2-1.7-4.7-3.1-6.2-5.4c-2.7-25.5-9.1-50.7-20-73.9
|
||||
c-12.3-27.1-29.5-51.6-47-75.6C33,199,23,184.2,14.7,168.3c-13-23.8-17.9-51.9-12.5-78.6c4.4-21.1,15.4-40.6,30.6-55.7
|
||||
C53.3,14,81.1,1.8,109.8,0z M107.2,74.1c-16.1,3.6-29.6,17.8-31,34.5c-1.7,15.5,7.4,31.3,21.3,38.1c15.1,8,34.9,5.5,47.5-6
|
||||
c11.8-10.4,16-28.3,9.9-42.9C147.6,79.7,126,69.3,107.2,74.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M107.2,74.1c18.9-4.8,40.4,5.5,47.7,23.7c6.1,14.5,1.9,32.5-9.9,42.9c-12.6,11.5-32.4,14-47.5,6
|
||||
c-13.9-6.8-23-22.6-21.3-38.1C77.6,92,91.1,77.7,107.2,74.1z" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 233.8 368.4" style="enable-background:new 0 0 233.8 368.4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{display:none;}
|
||||
.st2{display:inline;fill:#802C27;}
|
||||
.st3{fill:#808080;}
|
||||
.st4{display:none;fill:#802C27;}
|
||||
</style>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st0" d="M109.8,0h13.6c33.9,1.9,67.1,18.5,87.7,45.8c13.5,17.2,21,38.6,22.7,60.3v8.1c-0.8,42.1-27.7,76.6-51,109.4
|
||||
c-26.2,37-50.4,77.3-57.1,122.9c-1.8,7.7,0.4,18.5-8.9,22c-2.2-1.7-4.7-3.1-6.2-5.4c-2.7-25.5-9.1-50.7-20-73.9
|
||||
c-12.3-27.1-29.5-51.6-47-75.6C33,199,23,184.2,14.7,168.3c-13-23.8-17.9-51.9-12.5-78.6c4.4-21.1,15.4-40.6,30.6-55.7
|
||||
C53.3,14,81.1,1.8,109.8,0z" />
|
||||
</g>
|
||||
</g>
|
||||
<g class="st1">
|
||||
<path class="st2" d="M107.2,74.1c18.9-4.8,40.4,5.5,47.7,23.7c6.1,14.5,1.9,32.5-9.9,42.9c-12.6,11.5-32.4,14-47.5,6
|
||||
c-13.9-6.8-23-22.6-21.3-38.1C77.6,92,91.1,77.7,107.2,74.1z" />
|
||||
</g>
|
||||
</g>
|
||||
<path class="st3" d="M90.9,57.3v68.2h18.6v55.8l43.4-74.4h-24.8l24.8-49.6H90.9z" />
|
||||
<path class="st4" d="M159,85.3L159,85.3l-20.8-20.9l-5.9,5.9l11.8,11.8c-5.3,2-9,7.1-9,13.1c0,7.7,6.3,14,14,14c2,0,3.9-0.4,5.6-1.2
|
||||
v40.4c0,3.1-2.5,5.6-5.6,5.6s-5.6-2.5-5.6-5.6v-25.2c0-6.2-5-11.2-11.2-11.2h-5.6V72.8c0-6.2-5-11.2-11.2-11.2H81.8
|
||||
c-6.2,0-11.2,5-11.2,11.2v89.7h56.1v-42.1h8.4v28c0,7.7,6.3,14,14,14s14-6.3,14-14V95.2C163.1,91.3,161.6,87.8,159,85.3
|
||||
M149.1,100.8c-3.1,0-5.6-2.5-5.6-5.6c0-3.1,2.5-5.6,5.6-5.6s5.6,2.5,5.6,5.6C154.7,98.3,152.2,100.8,149.1,100.8 M93.1,145.6v-25.2
|
||||
H81.8l22.4-42.1v28h11.2L93.1,145.6z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px"
|
||||
height="24px">
|
||||
<path
|
||||
d="M20.055,7.713c-0.677-0.842-1.673-1.41-2.764-1.615C16.773,3.971,14.877,3,13.045,3H7.057C6.425,3,5.878,3.409,5.689,4.009 c-0.015,0.04-0.026,0.082-0.036,0.125L3.034,16.262c-0.009,0.041-0.015,0.083-0.019,0.125C3.006,16.449,3,16.513,3,16.56 C3,17.354,3.648,18,4.444,18h2.316l-0.267,1.262c-0.008,0.04-0.014,0.081-0.018,0.121c-0.009,0.063-0.016,0.126-0.016,0.173 C6.461,20.353,7.109,21,7.905,21h3.259c0.056,0,0.111-0.005,0.166-0.015c0.549-0.063,1.011-0.437,1.191-0.963 c0.021-0.05,0.038-0.103,0.05-0.156L13.475,16h1.398c3.365,0,5.38-1.445,5.989-4.295C21.278,9.752,20.653,8.456,20.055,7.713z M5.137,16L7.512,5h5.533c0.293,0,1.5,0.061,2.078,1.013h-4.706c-0.626,0-1.17,0.401-1.363,0.99c-0.019,0.049-0.033,0.1-0.043,0.151 l-1.034,5.093L7.183,16H5.137z M18.906,11.287C18.5,13.188,17.293,14,14.873,14h-1.857c-0.823,0-1.338,0.652-1.405,1.198L10.721,19 H8.594l1.271-6h1.444c4.259,0,5.665-2.394,6.094-4.402c0.027-0.128,0.045-0.256,0.057-0.382c0.378,0.151,0.749,0.393,1.038,0.751 C18.971,9.557,19.108,10.337,18.906,11.287z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 909 KiB |
|
Before Width: | Height: | Size: 943 KiB |
@@ -1 +0,0 @@
|
||||
/build
|
||||
@@ -1,175 +0,0 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'androidx.navigation.safeargs.kotlin'
|
||||
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "net.vonforst.evmap"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 21
|
||||
versionName "0.3.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
debuggable true
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "dependencies"
|
||||
productFlavors {
|
||||
foss {
|
||||
dimension "dependencies"
|
||||
versionNameSuffix "-foss"
|
||||
}
|
||||
google {
|
||||
dimension "dependencies"
|
||||
versionNameSuffix "-google"
|
||||
}
|
||||
}
|
||||
|
||||
def isRunningOnTravis = System.getenv("CI") == "true"
|
||||
if (isRunningOnTravis) {
|
||||
// configure keystore
|
||||
signingConfigs.release.storeFile = file("../_ci/keystore.jks")
|
||||
signingConfigs.release.storePassword = System.getenv("keystore_password")
|
||||
signingConfigs.release.keyAlias = System.getenv("keystore_alias")
|
||||
signingConfigs.release.keyPassword = System.getenv("keystore_alias_password")
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
}
|
||||
|
||||
// add API keys from environment variable if not set in apikeys.xml
|
||||
applicationVariants.all { variant ->
|
||||
ext.env = System.getenv()
|
||||
def goingelectricKey = env.GOINGELECTRIC_API_KEY
|
||||
if (goingelectricKey != null) {
|
||||
variant.resValue "string", "goingelectric_key", goingelectricKey
|
||||
}
|
||||
def googleMapsKey = env.GOOGLE_MAPS_API_KEY
|
||||
if (googleMapsKey != null) {
|
||||
variant.resValue "string", "google_maps_key", googleMapsKey
|
||||
}
|
||||
def mapboxKey = env.MAPBOX_API_KEY
|
||||
if (mapboxKey != null) {
|
||||
variant.resValue "string", "mapbox_key", mapboxKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.1'
|
||||
implementation "androidx.activity:activity-ktx:1.1.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.2.5"
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.core:core:1.3.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'androidx.browser:browser:1.2.0'
|
||||
implementation 'com.github.johan12345:CustomBottomSheetBehavior:f69f532660'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.7.2'
|
||||
implementation 'com.squareup.retrofit2:converter-moshi:2.7.2'
|
||||
implementation 'com.squareup.moshi:moshi-kotlin:1.9.2'
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
implementation 'com.github.MikeOrtiz:TouchImageView:2.3.3'
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libs_version"
|
||||
implementation "com.mikepenz:aboutlibraries:$about_libs_version"
|
||||
implementation 'com.airbnb.android:lottie:3.4.0'
|
||||
implementation 'io.michaelrocks:bimap:1.0.2'
|
||||
implementation 'com.mapzen.android:lost:3.0.2'
|
||||
|
||||
// AnyMaps
|
||||
def anyMapsVersion = 'cc2a044656'
|
||||
implementation "com.github.johan12345.AnyMaps:anymaps-base:$anyMapsVersion"
|
||||
googleImplementation "com.github.johan12345.AnyMaps:anymaps-google:$anyMapsVersion"
|
||||
implementation "com.github.johan12345.AnyMaps:anymaps-mapbox:$anyMapsVersion"
|
||||
|
||||
// Google Maps v3 Beta
|
||||
googleImplementation 'com.google.android.libraries.maps:maps:3.1.0-beta'
|
||||
googleImplementation name:'places-maps-sdk-3.1.0-beta', ext:'aar'
|
||||
googleImplementation 'com.android.volley:volley:1.1.1'
|
||||
googleImplementation 'com.google.android.gms:play-services-base:17.3.0'
|
||||
googleImplementation 'com.google.android.gms:play-services-basement:17.3.0'
|
||||
googleImplementation 'com.google.android.gms:play-services-gcm:17.0.0'
|
||||
googleImplementation 'com.google.android.gms:play-services-location:17.0.0'
|
||||
googleImplementation 'com.google.android.gms:play-services-tasks:17.1.0'
|
||||
googleImplementation 'com.google.auto.value:auto-value-annotations:1.6.3'
|
||||
googleImplementation 'com.google.code.gson:gson:2.8.6'
|
||||
googleImplementation 'com.google.android.datatransport:transport-runtime:2.2.3'
|
||||
googleImplementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
|
||||
// Mapbox places (autocomplete)
|
||||
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-places-v9:0.12.0'
|
||||
|
||||
// navigation library
|
||||
def nav_version = "2.3.0"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||
|
||||
// viewmodel library
|
||||
def lifecycle_version = "2.2.0"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
|
||||
// room library
|
||||
def room_version = "2.2.5"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
// billing library
|
||||
def billing_version = "3.0.0"
|
||||
googleImplementation "com.android.billingclient:billing:$billing_version"
|
||||
googleImplementation "com.android.billingclient:billing-ktx:$billing_version"
|
||||
|
||||
// debug tools
|
||||
implementation 'com.facebook.stetho:stetho:1.5.1'
|
||||
implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.7"
|
||||
//noinspection GradleDependency
|
||||
testImplementation 'org.json:json:20080701'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.9.2"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.johan.evmap
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.johan.evmap", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">EV Map (debug)</string>
|
||||
</resources>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">EV Map (debug)</string>
|
||||
</resources>
|
||||
@@ -1,12 +0,0 @@
|
||||
package net.vonforst.evmap
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
|
||||
fun init(context: Context) {
|
||||
|
||||
}
|
||||
|
||||
fun checkPlayServices(activity: Activity): Boolean {
|
||||
return true
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package net.vonforst.evmap.autocomplete
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.car2go.maps.model.LatLng
|
||||
import com.car2go.maps.model.LatLngBounds
|
||||
import com.mapbox.geojson.BoundingBox
|
||||
import com.mapbox.geojson.Point
|
||||
import com.mapbox.mapboxsdk.plugins.places.autocomplete.PlaceAutocomplete
|
||||
import com.mapbox.mapboxsdk.plugins.places.autocomplete.model.PlaceOptions
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.fragment.REQUEST_AUTOCOMPLETE
|
||||
import net.vonforst.evmap.viewmodel.PlaceWithBounds
|
||||
|
||||
|
||||
fun launchAutocomplete(fragment: Fragment) {
|
||||
val placeOptions = PlaceOptions.builder()
|
||||
.build(PlaceOptions.MODE_CARDS)
|
||||
|
||||
val intent = PlaceAutocomplete.IntentBuilder()
|
||||
.accessToken(fragment.getString(R.string.mapbox_key))
|
||||
.placeOptions(placeOptions)
|
||||
.build(fragment.requireActivity())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
|
||||
fragment.startActivityForResult(intent, REQUEST_AUTOCOMPLETE)
|
||||
|
||||
// show keyboard
|
||||
val imm = fragment.requireContext()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.toggleSoftInput(0, 0)
|
||||
}
|
||||
|
||||
fun handleAutocompleteResult(intent: Intent): PlaceWithBounds? {
|
||||
val place = PlaceAutocomplete.getPlace(intent) ?: return null
|
||||
val bbox = place.bbox()?.toLatLngBounds()
|
||||
val center = place.center()!!.toLatLng()
|
||||
return PlaceWithBounds(center, bbox)
|
||||
}
|
||||
|
||||
private fun BoundingBox.toLatLngBounds(): LatLngBounds {
|
||||
return LatLngBounds(
|
||||
southwest().toLatLng(),
|
||||
northeast().toLatLng()
|
||||
)
|
||||
}
|
||||
|
||||
private fun Point.toLatLng(): LatLng = LatLng(this.latitude(), this.longitude())
|
||||
@@ -1,37 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import kotlinx.android.synthetic.foss.fragment_donate.*
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
import net.vonforst.evmap.R
|
||||
|
||||
class DonateFragment : Fragment() {
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_donate, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
|
||||
|
||||
val navController = findNavController()
|
||||
toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
|
||||
btnDonate.setOnClickListener {
|
||||
(activity as? MapsActivity)?.openUrl(getString(R.string.paypal_link))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/toolbar_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDonate"
|
||||
style="@style/Widget.MaterialComponents.Button.Icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="@string/donate_paypal"
|
||||
app:icon="@drawable/ic_paypal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView20" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView20"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/donations_info"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btnDonate"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar_container"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.\n\nGoogle zieht von der Spende 30% Gebühren ab.</string>
|
||||
<string name="donate_paypal">Mit PayPal spenden</string>
|
||||
</resources>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_map_provider_values" tranlatable="false">
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string name="pref_map_provider_default" translatable="false">mapbox</string>
|
||||
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.</string>
|
||||
<string name="donate_paypal">Donate with PayPal</string>
|
||||
<string name="paypal_link" translatable="false">https://paypal.me/johan98</string>
|
||||
</resources>
|
||||
@@ -1,27 +0,0 @@
|
||||
package net.vonforst.evmap
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
import com.google.android.libraries.places.api.Places
|
||||
|
||||
fun init(context: Context) {
|
||||
Places.initialize(context, context.getString(R.string.google_maps_key));
|
||||
}
|
||||
|
||||
fun checkPlayServices(activity: Activity): Boolean {
|
||||
val request = 9000
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity)
|
||||
if (resultCode != ConnectionResult.SUCCESS) {
|
||||
if (apiAvailability.isUserResolvableError(resultCode)) {
|
||||
apiAvailability.getErrorDialog(activity, resultCode, request).show()
|
||||
} else {
|
||||
Log.d("EVMap", "This device is not supported.")
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.vonforst.evmap.adapter
|
||||
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.viewmodel.DonationItem
|
||||
|
||||
class DonationAdapter() : DataBindingAdapter<DonationItem>() {
|
||||
override fun getItemViewType(position: Int): Int = R.layout.item_donation
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package net.vonforst.evmap.autocomplete
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.car2go.maps.google.adapter.AnyMapAdapter
|
||||
import com.google.android.libraries.places.api.model.Place
|
||||
import com.google.android.libraries.places.widget.Autocomplete
|
||||
import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
|
||||
import net.vonforst.evmap.fragment.REQUEST_AUTOCOMPLETE
|
||||
import net.vonforst.evmap.viewmodel.PlaceWithBounds
|
||||
|
||||
fun launchAutocomplete(fragment: Fragment) {
|
||||
val fields = listOf(Place.Field.LAT_LNG, Place.Field.VIEWPORT)
|
||||
val intent: Intent = Autocomplete.IntentBuilder(
|
||||
AutocompleteActivityMode.OVERLAY, fields
|
||||
)
|
||||
.build(fragment.requireActivity())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
|
||||
fragment.startActivityForResult(intent, REQUEST_AUTOCOMPLETE)
|
||||
|
||||
// show keyboard
|
||||
val imm = fragment.requireContext()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.toggleSoftInput(0, 0)
|
||||
}
|
||||
|
||||
fun handleAutocompleteResult(intent: Intent): PlaceWithBounds? {
|
||||
val place = Autocomplete.getPlaceFromIntent(intent)
|
||||
return PlaceWithBounds(AnyMapAdapter.adapt(place.latLng), AnyMapAdapter.adapt(place.viewport))
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.DonationAdapter
|
||||
import net.vonforst.evmap.databinding.FragmentDonateBinding
|
||||
import net.vonforst.evmap.viewmodel.DonateViewModel
|
||||
|
||||
class DonateFragment : Fragment() {
|
||||
private lateinit var binding: FragmentDonateBinding
|
||||
private val vm: DonateViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_donate, container, false)
|
||||
binding.lifecycleOwner = this
|
||||
binding.vm = vm
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
|
||||
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
|
||||
|
||||
val navController = findNavController()
|
||||
toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
|
||||
binding.productsList.apply {
|
||||
adapter = DonationAdapter().apply {
|
||||
onClickListener = {
|
||||
vm.startPurchase(it, requireActivity())
|
||||
}
|
||||
}
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
}
|
||||
|
||||
vm.purchaseSuccessful.observe(viewLifecycleOwner, Observer {
|
||||
Snackbar.make(view, R.string.donation_successful, Snackbar.LENGTH_LONG).show()
|
||||
})
|
||||
vm.purchaseFailed.observe(viewLifecycleOwner, Observer {
|
||||
Snackbar.make(view, R.string.donation_failed, Snackbar.LENGTH_LONG).show()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package net.vonforst.evmap.viewmodel
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.android.billingclient.api.*
|
||||
import net.vonforst.evmap.BuildConfig
|
||||
import net.vonforst.evmap.adapter.Equatable
|
||||
|
||||
class DonateViewModel(application: Application) : AndroidViewModel(application),
|
||||
PurchasesUpdatedListener {
|
||||
private var billingClient = BillingClient.newBuilder(application)
|
||||
.setListener(this)
|
||||
.enablePendingPurchases()
|
||||
.build()
|
||||
|
||||
init {
|
||||
billingClient.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingServiceDisconnected() {
|
||||
}
|
||||
|
||||
override fun onBillingSetupFinished(p0: BillingResult) {
|
||||
loadProducts()
|
||||
|
||||
// consume pending purchases
|
||||
val purchases = billingClient.queryPurchases(BillingClient.SkuType.INAPP)
|
||||
purchases.purchasesList?.forEach {
|
||||
if (!it.isAcknowledged) {
|
||||
consumePurchase(it.purchaseToken, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadProducts() {
|
||||
val params = SkuDetailsParams.newBuilder()
|
||||
.setType(BillingClient.SkuType.INAPP)
|
||||
.setSkusList(
|
||||
listOf(
|
||||
"donate_1_eur", "donate_2_eur", "donate_5_eur", "donate_10_eur"
|
||||
) +
|
||||
if (BuildConfig.DEBUG) {
|
||||
listOf(
|
||||
"android.test.purchased", "android.test.canceled",
|
||||
"android.test.item_unavailable"
|
||||
)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
)
|
||||
.build()
|
||||
billingClient.querySkuDetailsAsync(params) { result, details ->
|
||||
if (result.responseCode == BillingClient.BillingResponseCode.OK && details != null) {
|
||||
products.value = Resource.success(details
|
||||
.sortedBy { it.priceAmountMicros }
|
||||
.map { DonationItem(it) }
|
||||
)
|
||||
} else {
|
||||
products.value = Resource.error(result.debugMessage, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val products: MutableLiveData<Resource<List<DonationItem>>> by lazy {
|
||||
MutableLiveData<Resource<List<DonationItem>>>().apply {
|
||||
value = Resource.loading(null)
|
||||
}
|
||||
}
|
||||
|
||||
val purchaseSuccessful = SingleLiveEvent<Nothing>()
|
||||
val purchaseFailed = SingleLiveEvent<Nothing>()
|
||||
|
||||
override fun onPurchasesUpdated(result: BillingResult, purchases: List<Purchase>?) {
|
||||
if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases != null) {
|
||||
for (purchase in purchases) {
|
||||
val purchaseToken = purchase.purchaseToken
|
||||
consumePurchase(purchaseToken)
|
||||
}
|
||||
} else if (result.responseCode == BillingClient.BillingResponseCode.USER_CANCELED) {
|
||||
// Handle an error caused by a user cancelling the purchase flow.
|
||||
} else {
|
||||
purchaseFailed.call()
|
||||
}
|
||||
}
|
||||
|
||||
private fun consumePurchase(purchaseToken: String, showSuccess: Boolean = true) {
|
||||
val params = ConsumeParams.newBuilder()
|
||||
.setPurchaseToken(purchaseToken)
|
||||
.build()
|
||||
billingClient.consumeAsync(params) { _, _ ->
|
||||
if (showSuccess) purchaseSuccessful.call()
|
||||
}
|
||||
}
|
||||
|
||||
fun startPurchase(it: DonationItem, activity: Activity) {
|
||||
val flowParams = BillingFlowParams.newBuilder()
|
||||
.setSkuDetails(it.sku)
|
||||
.build()
|
||||
val response = billingClient.launchBillingFlow(activity, flowParams)
|
||||
if (response.responseCode != BillingClient.BillingResponseCode.OK) {
|
||||
purchaseFailed.call()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
billingClient.endConnection()
|
||||
}
|
||||
}
|
||||
|
||||
data class DonationItem(val sku: SkuDetails) : Equatable
|
||||
@@ -1,73 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<import type="net.vonforst.evmap.viewmodel.DonateViewModel" />
|
||||
|
||||
<import type="net.vonforst.evmap.viewmodel.Status" />
|
||||
|
||||
<variable
|
||||
name="vm"
|
||||
type="DonateViewModel" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/toolbar_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView20"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/donations_info"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar_container" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/products_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:data="@{vm.products.data}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView20"
|
||||
tools:listitem="@layout/item_donation" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar3"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/products_list"
|
||||
app:goneUnless="@{vm.products.status == Status.LOADING}" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="item"
|
||||
type="net.vonforst.evmap.viewmodel.DonationItem" />
|
||||
</data>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView15"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{item.sku.title}"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/textView21"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Spende" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView21"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{item.sku.price}"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="1,00 €" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>Google Maps</item>
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string name="donations_info" formatted="false">Findest du EVMap nützlich? Unterstütze die Weiterentwicklung der App mit einer Spende an den Entwickler.\n\nGoogle zieht von der Spende 30% Gebühren ab.</string>
|
||||
</resources>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="pref_map_provider_names">
|
||||
<item>Google Maps</item>
|
||||
<item>OpenStreetMap (Mapbox)</item>
|
||||
</string-array>
|
||||
<string-array name="pref_map_provider_values" tranlatable="false">
|
||||
<item>google</item>
|
||||
<item>mapbox</item>
|
||||
</string-array>
|
||||
<string name="pref_map_provider_default" translatable="false">mapbox</string>
|
||||
<string name="donations_info" formatted="false">Do you find EVMap useful? Support its development by sending a donation to the developer.\n\nGoogle takes 30% off every donation.</string>
|
||||
</resources>
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="net.vonforst.evmap">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<application
|
||||
android:name=".EvMapApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<!--
|
||||
The API key for Google Maps-based APIs is defined as a string resource.
|
||||
(See the file "res/values/apikeys.xml").
|
||||
Note that the API key is linked to the encryption key used to sign the APK.
|
||||
You need a different API key for each encryption key, including the release key that is used to
|
||||
sign the APK for publishing.
|
||||
You can define the keys for the debug and release targets in src/debug/ and src/release/.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="com.google.android.geo.API_KEY"
|
||||
android:value="@string/google_maps_key" />
|
||||
<meta-data
|
||||
android:name="com.mapbox.ACCESS_TOKEN"
|
||||
android:value="@string/mapbox_key" />
|
||||
|
||||
<activity
|
||||
android:name=".MapsActivity"
|
||||
android:label="@string/title_activity_maps"
|
||||
android:theme="@style/AppTheme.LaunchScreen">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.clustering;
|
||||
|
||||
import com.car2go.maps.model.LatLng;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A collection of ClusterItems that are nearby each other.
|
||||
*/
|
||||
public interface Cluster<T extends ClusterItem> {
|
||||
LatLng getPosition();
|
||||
|
||||
Collection<T> getItems();
|
||||
|
||||
int getSize();
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.clustering;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.car2go.maps.model.LatLng;
|
||||
|
||||
/**
|
||||
* ClusterItem represents a marker on the map.
|
||||
*/
|
||||
public interface ClusterItem {
|
||||
|
||||
/**
|
||||
* The position of this marker. This must always return the same value.
|
||||
*/
|
||||
@NonNull
|
||||
LatLng getPosition();
|
||||
|
||||
/**
|
||||
* The title of this marker.
|
||||
*/
|
||||
@Nullable
|
||||
String getTitle();
|
||||
|
||||
/**
|
||||
* The description of this marker.
|
||||
*/
|
||||
@Nullable
|
||||
String getSnippet();
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.google.maps.android.clustering.algo;
|
||||
|
||||
import com.google.maps.android.clustering.ClusterItem;
|
||||
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* Base Algorithm class that implements lock/unlock functionality.
|
||||
*/
|
||||
public abstract class AbstractAlgorithm<T extends ClusterItem> implements Algorithm<T> {
|
||||
|
||||
private final ReadWriteLock mLock = new ReentrantReadWriteLock();
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
mLock.writeLock().lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
mLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.clustering.algo;
|
||||
|
||||
import com.google.maps.android.clustering.Cluster;
|
||||
import com.google.maps.android.clustering.ClusterItem;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Logic for computing clusters
|
||||
*/
|
||||
public interface Algorithm<T extends ClusterItem> {
|
||||
|
||||
/**
|
||||
* Adds an item to the algorithm
|
||||
*
|
||||
* @param item the item to be added
|
||||
* @return true if the algorithm contents changed as a result of the call
|
||||
*/
|
||||
boolean addItem(T item);
|
||||
|
||||
/**
|
||||
* Adds a collection of items to the algorithm
|
||||
*
|
||||
* @param items the items to be added
|
||||
* @return true if the algorithm contents changed as a result of the call
|
||||
*/
|
||||
boolean addItems(Collection<T> items);
|
||||
|
||||
void clearItems();
|
||||
|
||||
/**
|
||||
* Removes an item from the algorithm
|
||||
*
|
||||
* @param item the item to be removed
|
||||
* @return true if this algorithm contained the specified element (or equivalently, if this
|
||||
* algorithm changed as a result of the call).
|
||||
*/
|
||||
boolean removeItem(T item);
|
||||
|
||||
/**
|
||||
* Updates the provided item in the algorithm
|
||||
*
|
||||
* @param item the item to be updated
|
||||
* @return true if the item existed in the algorithm and was updated, or false if the item did
|
||||
* not exist in the algorithm and the algorithm contents remain unchanged.
|
||||
*/
|
||||
boolean updateItem(T item);
|
||||
|
||||
/**
|
||||
* Removes a collection of items from the algorithm
|
||||
*
|
||||
* @param items the items to be removed
|
||||
* @return true if this algorithm contents changed as a result of the call
|
||||
*/
|
||||
boolean removeItems(Collection<T> items);
|
||||
|
||||
Set<? extends Cluster<T>> getClusters(float zoom);
|
||||
|
||||
Collection<T> getItems();
|
||||
|
||||
void setMaxDistanceBetweenClusteredItems(int maxDistance);
|
||||
|
||||
int getMaxDistanceBetweenClusteredItems();
|
||||
|
||||
void lock();
|
||||
|
||||
void unlock();
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.clustering.algo;
|
||||
|
||||
import com.car2go.maps.model.LatLng;
|
||||
import com.google.maps.android.clustering.Cluster;
|
||||
import com.google.maps.android.clustering.ClusterItem;
|
||||
import com.google.maps.android.geometry.Bounds;
|
||||
import com.google.maps.android.geometry.Point;
|
||||
import com.google.maps.android.projection.SphericalMercatorProjection;
|
||||
import com.google.maps.android.quadtree.PointQuadTree;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A simple clustering algorithm with O(nlog n) performance. Resulting clusters are not
|
||||
* hierarchical.
|
||||
* <p/>
|
||||
* High level algorithm:<br>
|
||||
* 1. Iterate over items in the order they were added (candidate clusters).<br>
|
||||
* 2. Create a cluster with the center of the item. <br>
|
||||
* 3. Add all items that are within a certain distance to the cluster. <br>
|
||||
* 4. Move any items out of an existing cluster if they are closer to another cluster. <br>
|
||||
* 5. Remove those items from the list of candidate clusters.
|
||||
* <p/>
|
||||
* Clusters have the center of the first element (not the centroid of the items within it).
|
||||
*/
|
||||
public class NonHierarchicalDistanceBasedAlgorithm<T extends ClusterItem> extends AbstractAlgorithm<T> {
|
||||
private static final int DEFAULT_MAX_DISTANCE_AT_ZOOM = 100; // essentially 100 dp.
|
||||
|
||||
private int mMaxDistance = DEFAULT_MAX_DISTANCE_AT_ZOOM;
|
||||
|
||||
/**
|
||||
* Any modifications should be synchronized on mQuadTree.
|
||||
*/
|
||||
private final Collection<QuadItem<T>> mItems = new LinkedHashSet<>();
|
||||
|
||||
/**
|
||||
* Any modifications should be synchronized on mQuadTree.
|
||||
*/
|
||||
private final PointQuadTree<QuadItem<T>> mQuadTree = new PointQuadTree<>(0, 1, 0, 1);
|
||||
|
||||
private static final SphericalMercatorProjection PROJECTION = new SphericalMercatorProjection(1);
|
||||
|
||||
/**
|
||||
* Adds an item to the algorithm
|
||||
*
|
||||
* @param item the item to be added
|
||||
* @return true if the algorithm contents changed as a result of the call
|
||||
*/
|
||||
@Override
|
||||
public boolean addItem(T item) {
|
||||
boolean result;
|
||||
final QuadItem<T> quadItem = new QuadItem<>(item);
|
||||
synchronized (mQuadTree) {
|
||||
result = mItems.add(quadItem);
|
||||
if (result) {
|
||||
mQuadTree.add(quadItem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of items to the algorithm
|
||||
*
|
||||
* @param items the items to be added
|
||||
* @return true if the algorithm contents changed as a result of the call
|
||||
*/
|
||||
@Override
|
||||
public boolean addItems(Collection<T> items) {
|
||||
boolean result = false;
|
||||
for (T item : items) {
|
||||
boolean individualResult = addItem(item);
|
||||
if (individualResult) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearItems() {
|
||||
synchronized (mQuadTree) {
|
||||
mItems.clear();
|
||||
mQuadTree.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the algorithm
|
||||
*
|
||||
* @param item the item to be removed
|
||||
* @return true if this algorithm contained the specified element (or equivalently, if this
|
||||
* algorithm changed as a result of the call).
|
||||
*/
|
||||
@Override
|
||||
public boolean removeItem(T item) {
|
||||
boolean result;
|
||||
// QuadItem delegates hashcode() and equals() to its item so,
|
||||
// removing any QuadItem to that item will remove the item
|
||||
final QuadItem<T> quadItem = new QuadItem<>(item);
|
||||
synchronized (mQuadTree) {
|
||||
result = mItems.remove(quadItem);
|
||||
if (result) {
|
||||
mQuadTree.remove(quadItem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a collection of items from the algorithm
|
||||
*
|
||||
* @param items the items to be removed
|
||||
* @return true if this algorithm contents changed as a result of the call
|
||||
*/
|
||||
@Override
|
||||
public boolean removeItems(Collection<T> items) {
|
||||
boolean result = false;
|
||||
synchronized (mQuadTree) {
|
||||
for (T item : items) {
|
||||
// QuadItem delegates hashcode() and equals() to its item so,
|
||||
// removing any QuadItem to that item will remove the item
|
||||
final QuadItem<T> quadItem = new QuadItem<>(item);
|
||||
boolean individualResult = mItems.remove(quadItem);
|
||||
if (individualResult) {
|
||||
mQuadTree.remove(quadItem);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the provided item in the algorithm
|
||||
*
|
||||
* @param item the item to be updated
|
||||
* @return true if the item existed in the algorithm and was updated, or false if the item did
|
||||
* not exist in the algorithm and the algorithm contents remain unchanged.
|
||||
*/
|
||||
@Override
|
||||
public boolean updateItem(T item) {
|
||||
// TODO - Can this be optimized to update the item in-place if the location hasn't changed?
|
||||
boolean result;
|
||||
synchronized (mQuadTree) {
|
||||
result = removeItem(item);
|
||||
if (result) {
|
||||
// Only add the item if it was removed (to help prevent accidental duplicates on map)
|
||||
result = addItem(item);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<? extends Cluster<T>> getClusters(float zoom) {
|
||||
final int discreteZoom = (int) zoom;
|
||||
|
||||
final double zoomSpecificSpan = mMaxDistance / Math.pow(2, discreteZoom) / 256;
|
||||
|
||||
final Set<QuadItem<T>> visitedCandidates = new HashSet<>();
|
||||
final Set<Cluster<T>> results = new HashSet<>();
|
||||
final Map<QuadItem<T>, Double> distanceToCluster = new HashMap<>();
|
||||
final Map<QuadItem<T>, StaticCluster<T>> itemToCluster = new HashMap<>();
|
||||
|
||||
synchronized (mQuadTree) {
|
||||
for (QuadItem<T> candidate : getClusteringItems(mQuadTree, zoom)) {
|
||||
if (visitedCandidates.contains(candidate)) {
|
||||
// Candidate is already part of another cluster.
|
||||
continue;
|
||||
}
|
||||
|
||||
Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
|
||||
Collection<QuadItem<T>> clusterItems;
|
||||
clusterItems = mQuadTree.search(searchBounds);
|
||||
if (clusterItems.size() == 1) {
|
||||
// Only the current marker is in range. Just add the single item to the results.
|
||||
results.add(candidate);
|
||||
visitedCandidates.add(candidate);
|
||||
distanceToCluster.put(candidate, 0d);
|
||||
continue;
|
||||
}
|
||||
StaticCluster<T> cluster = new StaticCluster<>(candidate.mClusterItem.getPosition());
|
||||
results.add(cluster);
|
||||
|
||||
for (QuadItem<T> clusterItem : clusterItems) {
|
||||
Double existingDistance = distanceToCluster.get(clusterItem);
|
||||
double distance = distanceSquared(clusterItem.getPoint(), candidate.getPoint());
|
||||
if (existingDistance != null) {
|
||||
// Item already belongs to another cluster. Check if it's closer to this cluster.
|
||||
if (existingDistance < distance) {
|
||||
continue;
|
||||
}
|
||||
// Move item to the closer cluster.
|
||||
itemToCluster.get(clusterItem).remove(clusterItem.mClusterItem);
|
||||
}
|
||||
distanceToCluster.put(clusterItem, distance);
|
||||
cluster.add(clusterItem.mClusterItem);
|
||||
itemToCluster.put(clusterItem, cluster);
|
||||
}
|
||||
visitedCandidates.addAll(clusterItems);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
protected Collection<QuadItem<T>> getClusteringItems(PointQuadTree<QuadItem<T>> quadTree, float zoom) {
|
||||
return mItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> getItems() {
|
||||
final Set<T> items = new LinkedHashSet<>();
|
||||
synchronized (mQuadTree) {
|
||||
for (QuadItem<T> quadItem : mItems) {
|
||||
items.add(quadItem.mClusterItem);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxDistanceBetweenClusteredItems(int maxDistance) {
|
||||
mMaxDistance = maxDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDistanceBetweenClusteredItems() {
|
||||
return mMaxDistance;
|
||||
}
|
||||
|
||||
private double distanceSquared(Point a, Point b) {
|
||||
return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
|
||||
}
|
||||
|
||||
private Bounds createBoundsFromSpan(Point p, double span) {
|
||||
// TODO: Use a span that takes into account the visual size of the marker, not just its
|
||||
// LatLng.
|
||||
double halfSpan = span / 2;
|
||||
return new Bounds(
|
||||
p.x - halfSpan, p.x + halfSpan,
|
||||
p.y - halfSpan, p.y + halfSpan);
|
||||
}
|
||||
|
||||
protected static class QuadItem<T extends ClusterItem> implements PointQuadTree.Item, Cluster<T> {
|
||||
private final T mClusterItem;
|
||||
private final Point mPoint;
|
||||
private final LatLng mPosition;
|
||||
private Set<T> singletonSet;
|
||||
|
||||
private QuadItem(T item) {
|
||||
mClusterItem = item;
|
||||
mPosition = item.getPosition();
|
||||
mPoint = PROJECTION.toPoint(mPosition);
|
||||
singletonSet = Collections.singleton(mClusterItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getPoint() {
|
||||
return mPoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng getPosition() {
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<T> getItems() {
|
||||
return singletonSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mClusterItem.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof QuadItem<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((QuadItem<?>) other).mClusterItem.equals(mClusterItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.clustering.algo;
|
||||
|
||||
import com.car2go.maps.model.LatLng;
|
||||
import com.google.maps.android.clustering.Cluster;
|
||||
import com.google.maps.android.clustering.ClusterItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A cluster whose center is determined upon creation.
|
||||
*/
|
||||
public class StaticCluster<T extends ClusterItem> implements Cluster<T> {
|
||||
private final LatLng mCenter;
|
||||
private final List<T> mItems = new ArrayList<T>();
|
||||
|
||||
public StaticCluster(LatLng center) {
|
||||
mCenter = center;
|
||||
}
|
||||
|
||||
public boolean add(T t) {
|
||||
return mItems.add(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LatLng getPosition() {
|
||||
return mCenter;
|
||||
}
|
||||
|
||||
public boolean remove(T t) {
|
||||
return mItems.remove(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<T> getItems() {
|
||||
return mItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "StaticCluster{" +
|
||||
"mCenter=" + mCenter +
|
||||
", mItems.size=" + mItems.size() +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mCenter.hashCode() + mItems.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof StaticCluster<?>)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((StaticCluster<?>) other).mCenter.equals(mCenter)
|
||||
&& ((StaticCluster<?>) other).mItems.equals(mItems);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.geometry;
|
||||
|
||||
/**
|
||||
* Represents an area in the cartesian plane.
|
||||
*/
|
||||
public class Bounds {
|
||||
public final double minX;
|
||||
public final double minY;
|
||||
|
||||
public final double maxX;
|
||||
public final double maxY;
|
||||
|
||||
public final double midX;
|
||||
public final double midY;
|
||||
|
||||
public Bounds(double minX, double maxX, double minY, double maxY) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
|
||||
midX = (minX + maxX) / 2;
|
||||
midY = (minY + maxY) / 2;
|
||||
}
|
||||
|
||||
public boolean contains(double x, double y) {
|
||||
return minX <= x && x <= maxX && minY <= y && y <= maxY;
|
||||
}
|
||||
|
||||
public boolean contains(Point point) {
|
||||
return contains(point.x, point.y);
|
||||
}
|
||||
|
||||
public boolean intersects(double minX, double maxX, double minY, double maxY) {
|
||||
return minX < this.maxX && this.minX < maxX && minY < this.maxY && this.minY < maxY;
|
||||
}
|
||||
|
||||
public boolean intersects(Bounds bounds) {
|
||||
return intersects(bounds.minX, bounds.maxX, bounds.minY, bounds.maxY);
|
||||
}
|
||||
|
||||
public boolean contains(Bounds bounds) {
|
||||
return bounds.minX >= minX && bounds.maxX <= maxX && bounds.minY >= minY && bounds.maxY <= maxY;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.geometry;
|
||||
|
||||
public class Point {
|
||||
public final double x;
|
||||
public final double y;
|
||||
|
||||
public Point(double x, double y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Point{" +
|
||||
"x=" + x +
|
||||
", y=" + y +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.projection;
|
||||
|
||||
/**
|
||||
* @deprecated since 0.2. Use {@link com.google.maps.android.geometry.Point} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Point extends com.google.maps.android.geometry.Point {
|
||||
public Point(double x, double y) {
|
||||
super(x, y);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.projection;
|
||||
|
||||
import com.car2go.maps.model.LatLng;
|
||||
|
||||
public class SphericalMercatorProjection {
|
||||
final double mWorldWidth;
|
||||
|
||||
public SphericalMercatorProjection(final double worldWidth) {
|
||||
mWorldWidth = worldWidth;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public Point toPoint(final LatLng latLng) {
|
||||
final double x = latLng.longitude / 360 + .5;
|
||||
final double siny = Math.sin(Math.toRadians(latLng.latitude));
|
||||
final double y = 0.5 * Math.log((1 + siny) / (1 - siny)) / -(2 * Math.PI) + .5;
|
||||
|
||||
return new Point(x * mWorldWidth, y * mWorldWidth);
|
||||
}
|
||||
|
||||
public LatLng toLatLng(com.google.maps.android.geometry.Point point) {
|
||||
final double x = point.x / mWorldWidth - 0.5;
|
||||
final double lng = x * 360;
|
||||
|
||||
double y = .5 - (point.y / mWorldWidth);
|
||||
final double lat = 90 - Math.toDegrees(Math.atan(Math.exp(-y * 2 * Math.PI)) * 2);
|
||||
|
||||
return new LatLng(lat, lng);
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.quadtree;
|
||||
|
||||
import com.google.maps.android.geometry.Bounds;
|
||||
import com.google.maps.android.geometry.Point;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A quad tree which tracks items with a Point geometry.
|
||||
* See http://en.wikipedia.org/wiki/Quadtree for details on the data structure.
|
||||
* This class is not thread safe.
|
||||
*/
|
||||
public class PointQuadTree<T extends PointQuadTree.Item> {
|
||||
public interface Item {
|
||||
Point getPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* The bounds of this quad.
|
||||
*/
|
||||
private final Bounds mBounds;
|
||||
|
||||
/**
|
||||
* The depth of this quad in the tree.
|
||||
*/
|
||||
private final int mDepth;
|
||||
|
||||
/**
|
||||
* Maximum number of elements to store in a quad before splitting.
|
||||
*/
|
||||
private final static int MAX_ELEMENTS = 50;
|
||||
|
||||
/**
|
||||
* The elements inside this quad, if any.
|
||||
*/
|
||||
private Set<T> mItems;
|
||||
|
||||
/**
|
||||
* Maximum depth.
|
||||
*/
|
||||
private final static int MAX_DEPTH = 40;
|
||||
|
||||
/**
|
||||
* Child quads.
|
||||
*/
|
||||
private List<PointQuadTree<T>> mChildren = null;
|
||||
|
||||
/**
|
||||
* Creates a new quad tree with specified bounds.
|
||||
*
|
||||
* @param minX
|
||||
* @param maxX
|
||||
* @param minY
|
||||
* @param maxY
|
||||
*/
|
||||
public PointQuadTree(double minX, double maxX, double minY, double maxY) {
|
||||
this(new Bounds(minX, maxX, minY, maxY));
|
||||
}
|
||||
|
||||
public PointQuadTree(Bounds bounds) {
|
||||
this(bounds, 0);
|
||||
}
|
||||
|
||||
private PointQuadTree(double minX, double maxX, double minY, double maxY, int depth) {
|
||||
this(new Bounds(minX, maxX, minY, maxY), depth);
|
||||
}
|
||||
|
||||
private PointQuadTree(Bounds bounds, int depth) {
|
||||
mBounds = bounds;
|
||||
mDepth = depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an item.
|
||||
*/
|
||||
public void add(T item) {
|
||||
Point point = item.getPoint();
|
||||
if (this.mBounds.contains(point.x, point.y)) {
|
||||
insert(point.x, point.y, item);
|
||||
}
|
||||
}
|
||||
|
||||
private void insert(double x, double y, T item) {
|
||||
if (this.mChildren != null) {
|
||||
if (y < mBounds.midY) {
|
||||
if (x < mBounds.midX) { // top left
|
||||
mChildren.get(0).insert(x, y, item);
|
||||
} else { // top right
|
||||
mChildren.get(1).insert(x, y, item);
|
||||
}
|
||||
} else {
|
||||
if (x < mBounds.midX) { // bottom left
|
||||
mChildren.get(2).insert(x, y, item);
|
||||
} else {
|
||||
mChildren.get(3).insert(x, y, item);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (mItems == null) {
|
||||
mItems = new LinkedHashSet<>();
|
||||
}
|
||||
mItems.add(item);
|
||||
if (mItems.size() > MAX_ELEMENTS && mDepth < MAX_DEPTH) {
|
||||
split();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Split this quad.
|
||||
*/
|
||||
private void split() {
|
||||
mChildren = new ArrayList<PointQuadTree<T>>(4);
|
||||
mChildren.add(new PointQuadTree<T>(mBounds.minX, mBounds.midX, mBounds.minY, mBounds.midY, mDepth + 1));
|
||||
mChildren.add(new PointQuadTree<T>(mBounds.midX, mBounds.maxX, mBounds.minY, mBounds.midY, mDepth + 1));
|
||||
mChildren.add(new PointQuadTree<T>(mBounds.minX, mBounds.midX, mBounds.midY, mBounds.maxY, mDepth + 1));
|
||||
mChildren.add(new PointQuadTree<T>(mBounds.midX, mBounds.maxX, mBounds.midY, mBounds.maxY, mDepth + 1));
|
||||
|
||||
Set<T> items = mItems;
|
||||
mItems = null;
|
||||
|
||||
for (T item : items) {
|
||||
// re-insert items into child quads.
|
||||
insert(item.getPoint().x, item.getPoint().y, item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the given item from the set.
|
||||
*
|
||||
* @return whether the item was removed.
|
||||
*/
|
||||
public boolean remove(T item) {
|
||||
Point point = item.getPoint();
|
||||
if (this.mBounds.contains(point.x, point.y)) {
|
||||
return remove(point.x, point.y, item);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean remove(double x, double y, T item) {
|
||||
if (this.mChildren != null) {
|
||||
if (y < mBounds.midY) {
|
||||
if (x < mBounds.midX) { // top left
|
||||
return mChildren.get(0).remove(x, y, item);
|
||||
} else { // top right
|
||||
return mChildren.get(1).remove(x, y, item);
|
||||
}
|
||||
} else {
|
||||
if (x < mBounds.midX) { // bottom left
|
||||
return mChildren.get(2).remove(x, y, item);
|
||||
} else {
|
||||
return mChildren.get(3).remove(x, y, item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mItems == null) {
|
||||
return false;
|
||||
} else {
|
||||
return mItems.remove(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all points from the quadTree
|
||||
*/
|
||||
public void clear() {
|
||||
mChildren = null;
|
||||
if (mItems != null) {
|
||||
mItems.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all items within a given bounds.
|
||||
*/
|
||||
public Collection<T> search(Bounds searchBounds) {
|
||||
final List<T> results = new ArrayList<T>();
|
||||
search(searchBounds, results);
|
||||
return results;
|
||||
}
|
||||
|
||||
private void search(Bounds searchBounds, Collection<T> results) {
|
||||
if (!mBounds.intersects(searchBounds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mChildren != null) {
|
||||
for (PointQuadTree<T> quad : mChildren) {
|
||||
quad.search(searchBounds, results);
|
||||
}
|
||||
} else if (mItems != null) {
|
||||
if (searchBounds.contains(mBounds)) {
|
||||
results.addAll(mItems);
|
||||
} else {
|
||||
for (T item : mItems) {
|
||||
if (searchBounds.contains(item.getPoint())) {
|
||||
results.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,268 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import net.vonforst.evmap.R;
|
||||
|
||||
/**
|
||||
* IconGenerator generates icons that contain text (or custom content) within an info
|
||||
* window-like shape.
|
||||
* <p/>
|
||||
* The icon {@link Bitmap}s generated by the factory should be used in conjunction with a
|
||||
* BitmapDescriptorFactory.
|
||||
* <p/>
|
||||
* This class is not thread safe.
|
||||
*/
|
||||
public class IconGenerator {
|
||||
private final Context mContext;
|
||||
|
||||
private ViewGroup mContainer;
|
||||
private RotationLayout mRotationLayout;
|
||||
private TextView mTextView;
|
||||
private View mContentView;
|
||||
|
||||
private int mRotation;
|
||||
|
||||
private float mAnchorU = 0.5f;
|
||||
private float mAnchorV = 1f;
|
||||
|
||||
/**
|
||||
* Creates a new IconGenerator with the default style.
|
||||
*/
|
||||
public IconGenerator(Context context) {
|
||||
mContext = context;
|
||||
mContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.amu_text_bubble, null);
|
||||
mRotationLayout = (RotationLayout) mContainer.getChildAt(0);
|
||||
mContentView = mTextView = (TextView) mRotationLayout.findViewById(R.id.amu_text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text content, then creates an icon with the current style.
|
||||
*
|
||||
* @param text the text content to display inside the icon.
|
||||
*/
|
||||
public Bitmap makeIcon(CharSequence text) {
|
||||
if (mTextView != null) {
|
||||
mTextView.setText(text);
|
||||
}
|
||||
|
||||
return makeIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an icon with the current content and style.
|
||||
* <p/>
|
||||
* This method is useful if a custom view has previously been set, or if text content is not
|
||||
* applicable.
|
||||
*/
|
||||
public Bitmap makeIcon() {
|
||||
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||
mContainer.measure(measureSpec, measureSpec);
|
||||
|
||||
int measuredWidth = mContainer.getMeasuredWidth();
|
||||
int measuredHeight = mContainer.getMeasuredHeight();
|
||||
|
||||
mContainer.layout(0, 0, measuredWidth, measuredHeight);
|
||||
|
||||
if (mRotation == 1 || mRotation == 3) {
|
||||
measuredHeight = mContainer.getMeasuredWidth();
|
||||
measuredWidth = mContainer.getMeasuredHeight();
|
||||
}
|
||||
|
||||
Bitmap r = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
|
||||
r.eraseColor(Color.TRANSPARENT);
|
||||
|
||||
Canvas canvas = new Canvas(r);
|
||||
|
||||
switch (mRotation) {
|
||||
case 0:
|
||||
// do nothing
|
||||
break;
|
||||
case 1:
|
||||
canvas.translate(measuredWidth, 0);
|
||||
canvas.rotate(90);
|
||||
break;
|
||||
case 2:
|
||||
canvas.rotate(180, measuredWidth / 2, measuredHeight / 2);
|
||||
break;
|
||||
case 3:
|
||||
canvas.translate(0, measuredHeight);
|
||||
canvas.rotate(270);
|
||||
break;
|
||||
}
|
||||
mContainer.draw(canvas);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the child view for the icon.
|
||||
* <p/>
|
||||
* If the view contains a {@link TextView} with the id "text", operations such as {@link
|
||||
* #setTextAppearance} and {@link #makeIcon(CharSequence)} will operate upon that {@link TextView}.
|
||||
*/
|
||||
public void setContentView(View contentView) {
|
||||
mRotationLayout.removeAllViews();
|
||||
mRotationLayout.addView(contentView);
|
||||
mContentView = contentView;
|
||||
final View view = mRotationLayout.findViewById(R.id.amu_text);
|
||||
mTextView = view instanceof TextView ? (TextView) view : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the contents of the icon.
|
||||
*
|
||||
* @param degrees the amount the contents should be rotated, as a multiple of 90 degrees.
|
||||
*/
|
||||
public void setContentRotation(int degrees) {
|
||||
mRotationLayout.setViewRotation(degrees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the icon.
|
||||
*
|
||||
* @param degrees the amount the icon should be rotated, as a multiple of 90 degrees.
|
||||
*/
|
||||
public void setRotation(int degrees) {
|
||||
mRotation = ((degrees + 360) % 360) / 90;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return u coordinate of the anchor, with rotation applied.
|
||||
*/
|
||||
public float getAnchorU() {
|
||||
return rotateAnchor(mAnchorU, mAnchorV);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return v coordinate of the anchor, with rotation applied.
|
||||
*/
|
||||
public float getAnchorV() {
|
||||
return rotateAnchor(mAnchorV, mAnchorU);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the anchor around (u, v) = (0, 0).
|
||||
*/
|
||||
private float rotateAnchor(float u, float v) {
|
||||
switch (mRotation) {
|
||||
case 0:
|
||||
return u;
|
||||
case 1:
|
||||
return 1 - v;
|
||||
case 2:
|
||||
return 1 - u;
|
||||
case 3:
|
||||
return v;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text color, size, style, hint color, and highlight color from the specified
|
||||
* <code>TextAppearance</code> resource.
|
||||
*
|
||||
* @param resid the identifier of the resource.
|
||||
*/
|
||||
public void setTextAppearance(Context context, int resid) {
|
||||
if (mTextView != null) {
|
||||
mTextView.setTextAppearance(context, resid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text color, size, style, hint color, and highlight color from the specified
|
||||
* <code>TextAppearance</code> resource.
|
||||
*
|
||||
* @param resid the identifier of the resource.
|
||||
*/
|
||||
public void setTextAppearance(int resid) {
|
||||
setTextAppearance(mContext, resid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the background to a given Drawable, or remove the background.
|
||||
*
|
||||
* @param background the Drawable to use as the background, or null to remove the background.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
// View#setBackgroundDrawable is compatible with pre-API level 16 (Jelly Bean).
|
||||
public void setBackground(Drawable background) {
|
||||
mContainer.setBackgroundDrawable(background);
|
||||
|
||||
// Force setting of padding.
|
||||
// setBackgroundDrawable does not call setPadding if the background has 0 padding.
|
||||
if (background != null) {
|
||||
Rect rect = new Rect();
|
||||
background.getPadding(rect);
|
||||
mContainer.setPadding(rect.left, rect.top, rect.right, rect.bottom);
|
||||
} else {
|
||||
mContainer.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the padding of the content view. The default padding of the content view (i.e. text
|
||||
* view) is 5dp top/bottom and 10dp left/right.
|
||||
*
|
||||
* @param left the left padding in pixels.
|
||||
* @param top the top padding in pixels.
|
||||
* @param right the right padding in pixels.
|
||||
* @param bottom the bottom padding in pixels.
|
||||
*/
|
||||
public void setContentPadding(int left, int top, int right, int bottom) {
|
||||
mContentView.setPadding(left, top, right, bottom);
|
||||
}
|
||||
|
||||
public static final int STYLE_DEFAULT = 1;
|
||||
public static final int STYLE_WHITE = 2;
|
||||
public static final int STYLE_RED = 3;
|
||||
public static final int STYLE_BLUE = 4;
|
||||
public static final int STYLE_GREEN = 5;
|
||||
public static final int STYLE_PURPLE = 6;
|
||||
public static final int STYLE_ORANGE = 7;
|
||||
|
||||
private static int getStyleColor(int style) {
|
||||
switch (style) {
|
||||
default:
|
||||
case STYLE_DEFAULT:
|
||||
case STYLE_WHITE:
|
||||
return 0xffffffff;
|
||||
case STYLE_RED:
|
||||
return 0xffcc0000;
|
||||
case STYLE_BLUE:
|
||||
return 0xff0099cc;
|
||||
case STYLE_GREEN:
|
||||
return 0xff669900;
|
||||
case STYLE_PURPLE:
|
||||
return 0xff9933cc;
|
||||
case STYLE_ORANGE:
|
||||
return 0xffff8800;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* RotationLayout rotates the contents of the layout by multiples of 90 degrees.
|
||||
* <p/>
|
||||
* May not work with padding.
|
||||
*/
|
||||
public class RotationLayout extends FrameLayout {
|
||||
private int mRotation;
|
||||
|
||||
public RotationLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public RotationLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public RotationLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (mRotation == 1 || mRotation == 3) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
|
||||
} else {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param degrees the rotation, in degrees.
|
||||
*/
|
||||
public void setViewRotation(int degrees) {
|
||||
mRotation = ((degrees + 360) % 360) / 90;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dispatchDraw(Canvas canvas) {
|
||||
if (mRotation == 0) {
|
||||
super.dispatchDraw(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mRotation == 1) {
|
||||
canvas.translate(getWidth(), 0);
|
||||
canvas.rotate(90, getWidth() / 2, 0);
|
||||
canvas.translate(getHeight() / 2, getWidth() / 2);
|
||||
} else if (mRotation == 2) {
|
||||
canvas.rotate(180, getWidth() / 2, getHeight() / 2);
|
||||
} else {
|
||||
canvas.translate(0, getHeight());
|
||||
canvas.rotate(270, getWidth() / 2, 0);
|
||||
canvas.translate(getHeight() / 2, -getWidth() / 2);
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.google.maps.android.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
public class SquareTextView extends AppCompatTextView {
|
||||
private int mOffsetTop = 0;
|
||||
private int mOffsetLeft = 0;
|
||||
|
||||
public SquareTextView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquareTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SquareTextView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int width = getMeasuredWidth();
|
||||
int height = getMeasuredHeight();
|
||||
int dimension = Math.max(width, height);
|
||||
if (width > height) {
|
||||
mOffsetTop = width - height;
|
||||
mOffsetLeft = 0;
|
||||
} else {
|
||||
mOffsetTop = 0;
|
||||
mOffsetLeft = height - width;
|
||||
}
|
||||
setMeasuredDimension(dimension, dimension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.translate(mOffsetLeft / 2, mOffsetTop / 2);
|
||||
super.draw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.vonforst.evmap
|
||||
|
||||
import android.app.Application
|
||||
import com.facebook.stetho.Stetho
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import net.vonforst.evmap.ui.updateNightMode
|
||||
|
||||
class EvMapApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
updateNightMode(PreferenceDataSource(this))
|
||||
Stetho.initializeWithDefaults(this);
|
||||
init(applicationContext)
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package net.vonforst.evmap
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import net.vonforst.evmap.utils.LocaleContextWrapper
|
||||
|
||||
|
||||
const val REQUEST_LOCATION_PERMISSION = 1
|
||||
|
||||
class MapsActivity : AppCompatActivity() {
|
||||
interface FragmentCallback {
|
||||
fun getRootView(): View
|
||||
}
|
||||
|
||||
private var reenterState: Bundle? = null
|
||||
private lateinit var navController: NavController
|
||||
lateinit var appBarConfiguration: AppBarConfiguration
|
||||
var fragmentCallback: FragmentCallback? = null
|
||||
private lateinit var prefs: PreferenceDataSource
|
||||
|
||||
override fun attachBaseContext(newBase: Context) {
|
||||
return super.attachBaseContext(
|
||||
LocaleContextWrapper.wrap(
|
||||
newBase, PreferenceDataSource(newBase).language
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// set theme to AppTheme to end launch screen
|
||||
setTheme(R.style.AppTheme)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_maps)
|
||||
|
||||
navController = findNavController(R.id.nav_host_fragment)
|
||||
appBarConfiguration = AppBarConfiguration(
|
||||
setOf(
|
||||
R.id.map,
|
||||
R.id.favs,
|
||||
R.id.about
|
||||
),
|
||||
findViewById<DrawerLayout>(R.id.drawer_layout)
|
||||
)
|
||||
findViewById<NavigationView>(R.id.nav_view).setupWithNavController(navController)
|
||||
|
||||
prefs = PreferenceDataSource(this)
|
||||
|
||||
checkPlayServices(this)
|
||||
}
|
||||
|
||||
fun navigateTo(charger: ChargeLocation) {
|
||||
// google maps navigation
|
||||
val coord = charger.coordinates
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse("google.navigation:q=${coord.lat},${coord.lng}")
|
||||
if (prefs.navigateUseMaps && intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
// fallback: generic geo intent
|
||||
showLocation(charger)
|
||||
}
|
||||
}
|
||||
|
||||
fun showLocation(charger: ChargeLocation) {
|
||||
val coord = charger.coordinates
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.data = Uri.parse("geo:0,0?q=${coord.lat},${coord.lng}(${charger.name})")
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent);
|
||||
} else {
|
||||
val cb = fragmentCallback ?: return
|
||||
Snackbar.make(
|
||||
cb.getRootView(),
|
||||
R.string.no_maps_app_found,
|
||||
Snackbar.LENGTH_SHORT
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun openUrl(url: String) {
|
||||
val intent = CustomTabsIntent.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(this, R.color.colorPrimary))
|
||||
.build()
|
||||
intent.launchUrl(this, Uri.parse(url))
|
||||
}
|
||||
|
||||
fun shareUrl(url: String) {
|
||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||
setType("text/plain")
|
||||
putExtra(Intent.EXTRA_TEXT, url)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package net.vonforst.evmap
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.text.*
|
||||
import android.text.style.StyleSpan
|
||||
|
||||
fun Bundle.optDouble(name: String): Double? {
|
||||
if (!this.containsKey(name)) return null
|
||||
|
||||
val dbl = this.getDouble(name, Double.NaN)
|
||||
return if (dbl.isNaN()) null else dbl
|
||||
}
|
||||
|
||||
fun Bundle.optLong(name: String): Long? {
|
||||
if (!this.containsKey(name)) return null
|
||||
|
||||
val lng = this.getLong(name, Long.MIN_VALUE)
|
||||
return if (lng == Long.MIN_VALUE) null else lng
|
||||
}
|
||||
|
||||
fun <T> Iterable<T>.joinToSpannedString(
|
||||
separator: CharSequence = ", ",
|
||||
prefix: CharSequence = "",
|
||||
postfix: CharSequence = "",
|
||||
limit: Int = -1,
|
||||
truncated: CharSequence = "...",
|
||||
transform: ((T) -> CharSequence)? = null
|
||||
): CharSequence {
|
||||
return SpannedString(
|
||||
joinTo(
|
||||
SpannableStringBuilder(),
|
||||
separator,
|
||||
prefix,
|
||||
postfix,
|
||||
limit,
|
||||
truncated,
|
||||
transform
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
operator fun CharSequence.plus(other: CharSequence): CharSequence {
|
||||
return TextUtils.concat(this, other)
|
||||
}
|
||||
|
||||
fun String.bold(): CharSequence {
|
||||
return SpannableString(this).apply {
|
||||
setSpan(
|
||||
StyleSpan(Typeface.BOLD), 0, this.length,
|
||||
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package net.vonforst.evmap.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import net.vonforst.evmap.BR
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.api.availability.ChargepointStatus
|
||||
import net.vonforst.evmap.api.goingelectric.Chargepoint
|
||||
import net.vonforst.evmap.viewmodel.FavoritesViewModel
|
||||
|
||||
interface Equatable {
|
||||
override fun equals(other: Any?): Boolean;
|
||||
}
|
||||
|
||||
abstract class DataBindingAdapter<T : Equatable>(getKey: ((T) -> Any)? = null) :
|
||||
ListAdapter<T, DataBindingAdapter.ViewHolder<T>>(DiffCallback(getKey)) {
|
||||
|
||||
var onClickListener: ((T) -> Unit)? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding =
|
||||
DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, viewType, parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) =
|
||||
bind(holder, getItem(position))
|
||||
|
||||
class ViewHolder<T>(val binding: ViewDataBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
}
|
||||
|
||||
open fun bind(holder: ViewHolder<T>, item: T) {
|
||||
holder.binding.setVariable(BR.item, item)
|
||||
holder.binding.executePendingBindings()
|
||||
if (onClickListener != null) {
|
||||
holder.binding.root.setOnClickListener {
|
||||
val listener = onClickListener ?: return@setOnClickListener
|
||||
listener(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DiffCallback<T : Equatable>(val getKey: ((T) -> Any)?) : DiffUtil.ItemCallback<T>() {
|
||||
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean = if (getKey != null) {
|
||||
(getKey)(oldItem) == (getKey)(newItem)
|
||||
} else {
|
||||
oldItem === newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean = oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
fun chargepointWithAvailability(
|
||||
chargepoints: Iterable<Chargepoint>?,
|
||||
availability: Map<Chargepoint, List<ChargepointStatus>>?
|
||||
): List<ConnectorAdapter.ChargepointWithAvailability>? {
|
||||
return chargepoints?.map {
|
||||
val status = availability?.get(it)
|
||||
ConnectorAdapter.ChargepointWithAvailability(it, status)
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectorAdapter : DataBindingAdapter<ConnectorAdapter.ChargepointWithAvailability>() {
|
||||
data class ChargepointWithAvailability(
|
||||
val chargepoint: Chargepoint,
|
||||
val status: List<ChargepointStatus>?
|
||||
) :
|
||||
Equatable
|
||||
|
||||
override fun getItemViewType(position: Int): Int = R.layout.item_connector
|
||||
}
|
||||
|
||||
|
||||
class FavoritesAdapter(val vm: FavoritesViewModel) :
|
||||
DataBindingAdapter<FavoritesViewModel.FavoritesListItem>() {
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int = R.layout.item_favorite
|
||||
|
||||
override fun getItemId(position: Int): Long = getItem(position).charger.id
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package net.vonforst.evmap.adapter
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.text.HtmlCompat
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeCard
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeCardId
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.api.goingelectric.OpeningHoursDays
|
||||
import net.vonforst.evmap.bold
|
||||
import net.vonforst.evmap.joinToSpannedString
|
||||
import net.vonforst.evmap.plus
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
class DetailsAdapter : DataBindingAdapter<DetailsAdapter.Detail>() {
|
||||
data class Detail(
|
||||
val icon: Int,
|
||||
val contentDescription: Int,
|
||||
val text: CharSequence,
|
||||
val detailText: CharSequence? = null,
|
||||
val links: Boolean = true,
|
||||
val clickable: Boolean = false,
|
||||
val hoursDays: OpeningHoursDays? = null
|
||||
) : Equatable
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val item = getItem(position)
|
||||
if (item.hoursDays != null) {
|
||||
return R.layout.item_detail_openinghours
|
||||
} else {
|
||||
return R.layout.item_detail
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buildDetails(
|
||||
loc: ChargeLocation?,
|
||||
chargeCards: Map<Long, ChargeCard>?,
|
||||
filteredChargeCards: Set<Long>?,
|
||||
ctx: Context
|
||||
): List<DetailsAdapter.Detail> {
|
||||
if (loc == null) return emptyList()
|
||||
|
||||
return listOfNotNull(
|
||||
DetailsAdapter.Detail(
|
||||
R.drawable.ic_address,
|
||||
R.string.address,
|
||||
loc.address.toString(),
|
||||
loc.locationDescription
|
||||
),
|
||||
if (loc.operator != null) DetailsAdapter.Detail(
|
||||
R.drawable.ic_operator,
|
||||
R.string.operator,
|
||||
loc.operator
|
||||
) else null,
|
||||
if (loc.network != null) DetailsAdapter.Detail(
|
||||
R.drawable.ic_network,
|
||||
R.string.network,
|
||||
loc.network
|
||||
) else null,
|
||||
if (loc.faultReport != null) DetailsAdapter.Detail(
|
||||
R.drawable.ic_fault_report,
|
||||
R.string.fault_report,
|
||||
loc.faultReport.created?.let {
|
||||
ctx.getString(
|
||||
R.string.fault_report_date,
|
||||
loc.faultReport.created
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT))
|
||||
)
|
||||
} ?: "",
|
||||
loc.faultReport.description?.let {
|
||||
HtmlCompat.fromHtml(it, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
} ?: "",
|
||||
clickable = true
|
||||
) else null,
|
||||
if (loc.openinghours != null && !loc.openinghours.isEmpty) DetailsAdapter.Detail(
|
||||
R.drawable.ic_hours,
|
||||
R.string.hours,
|
||||
loc.openinghours.getStatusText(ctx),
|
||||
loc.openinghours.description,
|
||||
hoursDays = loc.openinghours.days
|
||||
) else null,
|
||||
if (loc.cost != null) DetailsAdapter.Detail(
|
||||
R.drawable.ic_cost,
|
||||
R.string.cost,
|
||||
loc.cost.getStatusText(ctx),
|
||||
loc.cost.descriptionLong ?: loc.cost.descriptionShort
|
||||
)
|
||||
else null,
|
||||
if (loc.chargecards != null && loc.chargecards.isNotEmpty()) DetailsAdapter.Detail(
|
||||
R.drawable.ic_payment,
|
||||
R.string.charge_cards,
|
||||
ctx.resources.getQuantityString(
|
||||
R.plurals.charge_cards_compatible_num,
|
||||
loc.chargecards.size, loc.chargecards.size
|
||||
),
|
||||
formatChargeCards(loc.chargecards, chargeCards, filteredChargeCards, ctx),
|
||||
clickable = true
|
||||
) else null,
|
||||
DetailsAdapter.Detail(
|
||||
R.drawable.ic_location,
|
||||
R.string.coordinates,
|
||||
loc.coordinates.formatDMS(),
|
||||
loc.coordinates.formatDecimal(),
|
||||
links = false,
|
||||
clickable = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun formatChargeCards(
|
||||
chargecards: List<ChargeCardId>,
|
||||
chargecardData: Map<Long, ChargeCard>?,
|
||||
filteredChargeCards: Set<Long>?,
|
||||
ctx: Context
|
||||
): CharSequence {
|
||||
if (chargecardData == null) return ""
|
||||
|
||||
val maxItems = 5
|
||||
var result = chargecards
|
||||
.sortedByDescending { filteredChargeCards?.contains(it.id) }
|
||||
.take(maxItems)
|
||||
.mapNotNull {
|
||||
val name = chargecardData[it.id]?.name ?: return@mapNotNull null
|
||||
if (filteredChargeCards?.contains(it.id) == true) {
|
||||
name.bold()
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}.joinToSpannedString()
|
||||
if (chargecards.size > maxItems) {
|
||||
result += " " + ctx.getString(R.string.and_n_others, chargecards.size - maxItems)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
package net.vonforst.evmap.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.children
|
||||
import androidx.databinding.Observable
|
||||
import com.google.android.material.chip.Chip
|
||||
import net.vonforst.evmap.BR
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.databinding.ItemFilterMultipleChoiceBinding
|
||||
import net.vonforst.evmap.databinding.ItemFilterMultipleChoiceLargeBinding
|
||||
import net.vonforst.evmap.databinding.ItemFilterSliderBinding
|
||||
import net.vonforst.evmap.fragment.MultiSelectDialog
|
||||
import net.vonforst.evmap.viewmodel.*
|
||||
import kotlin.math.max
|
||||
|
||||
class FiltersAdapter : DataBindingAdapter<FilterWithValue<FilterValue>>() {
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
val itemids = mutableMapOf<String, Long>()
|
||||
var maxId = 0L
|
||||
|
||||
override fun getItemViewType(position: Int): Int =
|
||||
when (val filter = getItem(position).filter) {
|
||||
is BooleanFilter -> R.layout.item_filter_boolean
|
||||
is MultipleChoiceFilter -> {
|
||||
if (filter.manyChoices) {
|
||||
R.layout.item_filter_multiple_choice_large
|
||||
} else {
|
||||
R.layout.item_filter_multiple_choice
|
||||
}
|
||||
}
|
||||
is SliderFilter -> R.layout.item_filter_slider
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
holder: ViewHolder<FilterWithValue<FilterValue>>,
|
||||
item: FilterWithValue<FilterValue>
|
||||
) {
|
||||
super.bind(holder, item)
|
||||
when (item.value) {
|
||||
is SliderFilterValue -> {
|
||||
setupSlider(
|
||||
holder.binding as ItemFilterSliderBinding,
|
||||
item.filter as SliderFilter, item.value
|
||||
)
|
||||
}
|
||||
is MultipleChoiceFilterValue -> {
|
||||
val filter = item.filter as MultipleChoiceFilter
|
||||
if (filter.manyChoices) {
|
||||
setupMultipleChoiceMany(
|
||||
holder.binding as ItemFilterMultipleChoiceLargeBinding,
|
||||
filter, item.value
|
||||
)
|
||||
} else {
|
||||
setupMultipleChoice(
|
||||
holder.binding as ItemFilterMultipleChoiceBinding,
|
||||
filter, item.value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupMultipleChoice(
|
||||
binding: ItemFilterMultipleChoiceBinding,
|
||||
filter: MultipleChoiceFilter,
|
||||
value: MultipleChoiceFilterValue
|
||||
) {
|
||||
// TODO: this implementation seems to be buggy
|
||||
val inflater = LayoutInflater.from(binding.root.context)
|
||||
value.values.toList().forEach {
|
||||
// delete values that cannot be selected anymore
|
||||
if (it !in filter.choices.keys) value.values.remove(it)
|
||||
}
|
||||
|
||||
fun updateButtons() {
|
||||
value.all = value.values == filter.choices.keys
|
||||
binding.btnAll.isEnabled = !value.all
|
||||
binding.btnNone.isEnabled = value.values.isNotEmpty()
|
||||
}
|
||||
|
||||
val chips = mutableMapOf<String, Chip>()
|
||||
|
||||
// reuse existing chips in layout
|
||||
val reuseChips = binding.chipGroup.children.filter {
|
||||
it.id != R.id.chipMore
|
||||
}.toMutableList()
|
||||
binding.chipGroup.children.forEach {
|
||||
if (it.id != R.id.chipMore) binding.chipGroup.removeView(it)
|
||||
}
|
||||
filter.choices.entries.sortedByDescending {
|
||||
it.key in value.values
|
||||
}.sortedByDescending {
|
||||
if (filter.commonChoices != null) it.key in filter.commonChoices else false
|
||||
}.forEach { choice ->
|
||||
var reused = false
|
||||
val chip = if (reuseChips.size > 0) {
|
||||
reused = true
|
||||
reuseChips.removeAt(0) as Chip
|
||||
} else {
|
||||
inflater.inflate(
|
||||
R.layout.item_filter_multiple_choice_chip,
|
||||
binding.chipGroup,
|
||||
false
|
||||
) as Chip
|
||||
}
|
||||
chip.text = choice.value
|
||||
chip.isChecked = choice.key in value.values || value.all
|
||||
if (value.all && choice.key !in value.values) value.values.add(choice.key)
|
||||
|
||||
chip.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
value.values.add(choice.key)
|
||||
} else {
|
||||
value.values.remove(choice.key)
|
||||
}
|
||||
updateButtons()
|
||||
}
|
||||
|
||||
if (filter.commonChoices != null && choice.key !in filter.commonChoices
|
||||
&& !(chip.isChecked && !value.all) && !binding.showingAll
|
||||
) {
|
||||
chip.visibility = View.GONE
|
||||
} else {
|
||||
chip.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
if (!reused) binding.chipGroup.addView(chip, binding.chipGroup.childCount - 1)
|
||||
chips[choice.key] = chip
|
||||
}
|
||||
// delete surplus reusable chips
|
||||
reuseChips.forEach {
|
||||
binding.chipGroup.removeView(it)
|
||||
}
|
||||
|
||||
binding.btnAll.setOnClickListener {
|
||||
value.all = true
|
||||
value.values.addAll(filter.choices.keys)
|
||||
chips.values.forEach { it.isChecked = true }
|
||||
updateButtons()
|
||||
}
|
||||
binding.btnNone.setOnClickListener {
|
||||
value.all = true
|
||||
value.values.addAll(filter.choices.keys)
|
||||
chips.values.forEach { it.isChecked = false }
|
||||
updateButtons()
|
||||
}
|
||||
binding.chipMore.setOnClickListener {
|
||||
binding.showingAll = !binding.showingAll
|
||||
chips.forEach { (key, chip) ->
|
||||
if (filter.commonChoices != null && key !in filter.commonChoices
|
||||
&& !(chip.isChecked && !value.all) && !binding.showingAll
|
||||
) {
|
||||
chip.visibility = View.GONE
|
||||
} else {
|
||||
chip.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
updateButtons()
|
||||
}
|
||||
|
||||
private fun setupMultipleChoiceMany(
|
||||
binding: ItemFilterMultipleChoiceLargeBinding,
|
||||
filter: MultipleChoiceFilter,
|
||||
value: MultipleChoiceFilterValue
|
||||
) {
|
||||
if (value.all) {
|
||||
value.values = filter.choices.keys.toMutableSet()
|
||||
binding.notifyPropertyChanged(BR.item)
|
||||
}
|
||||
|
||||
binding.btnEdit.setOnClickListener {
|
||||
val dialog =
|
||||
MultiSelectDialog.getInstance(
|
||||
filter.name,
|
||||
filter.choices,
|
||||
value.values,
|
||||
commonChoices = filter.commonChoices
|
||||
)
|
||||
dialog.okListener = { selected ->
|
||||
value.values = selected.toMutableSet()
|
||||
value.all = value.values == filter.choices.keys
|
||||
binding.item = binding.item
|
||||
}
|
||||
dialog.show((binding.root.context as AppCompatActivity).supportFragmentManager, null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSlider(
|
||||
binding: ItemFilterSliderBinding,
|
||||
filter: SliderFilter,
|
||||
value: SliderFilterValue
|
||||
) {
|
||||
binding.progress =
|
||||
max(filter.inverseMapping(value.value) - filter.min, 0)
|
||||
binding.mappedValue = filter.mapping(binding.progress + filter.min)
|
||||
|
||||
binding.addOnPropertyChangedCallback(object :
|
||||
Observable.OnPropertyChangedCallback() {
|
||||
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
|
||||
when (propertyId) {
|
||||
BR.progress -> {
|
||||
val mapped = filter.mapping(binding.progress + filter.min)
|
||||
value.value = mapped
|
||||
binding.mappedValue = mapped
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
val key = getItem(position).filter.key
|
||||
var value = itemids[key]
|
||||
if (value == null) {
|
||||
maxId++
|
||||
value = maxId
|
||||
itemids[key] = maxId
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package net.vonforst.evmap.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.*
|
||||
import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.ortiz.touchview.TouchImageView
|
||||
import com.squareup.picasso.Callback
|
||||
import com.squareup.picasso.Picasso
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.api.goingelectric.ChargerPhoto
|
||||
|
||||
|
||||
class GalleryAdapter(
|
||||
context: Context,
|
||||
val itemClickListener: ItemClickListener? = null,
|
||||
val detailView: Boolean = false,
|
||||
val pageToLoad: Int? = null,
|
||||
val loadedListener: (() -> Unit)? = null
|
||||
) :
|
||||
ListAdapter<ChargerPhoto, GalleryAdapter.ViewHolder>(ChargerPhotoDiffCallback()) {
|
||||
class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
|
||||
|
||||
interface ItemClickListener {
|
||||
fun onItemClick(view: View, position: Int)
|
||||
}
|
||||
|
||||
val apikey = context.getString(R.string.goingelectric_key)
|
||||
var loaded = false
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view: ImageView
|
||||
if (detailView) {
|
||||
view = inflater.inflate(R.layout.gallery_item_fullscreen, parent, false) as ImageView
|
||||
view.setOnTouchListener { view, event ->
|
||||
var result = true
|
||||
//can scroll horizontally checks if there's still a part of the image
|
||||
//that can be scrolled until you reach the edge
|
||||
if (event.pointerCount >= 2 || view.canScrollHorizontally(1) && view.canScrollHorizontally(
|
||||
-1
|
||||
)
|
||||
) {
|
||||
//multi-touch event
|
||||
result = when (event.action) {
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
|
||||
// Disallow RecyclerView to intercept touch events.
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
// Disable touch on view
|
||||
false
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
// Allow RecyclerView to intercept touch events.
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
true
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
} else {
|
||||
view = inflater.inflate(R.layout.gallery_item, parent, false) as ImageView
|
||||
}
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
if (detailView) {
|
||||
(holder.view as TouchImageView).resetZoom()
|
||||
}
|
||||
Picasso.get()
|
||||
.load(
|
||||
"https://api.goingelectric.de/chargepoints/photo/?key=${apikey}" +
|
||||
"&id=${getItem(position).id}" +
|
||||
if (detailView) {
|
||||
"&size=1000"
|
||||
} else {
|
||||
"&height=${holder.view.height}"
|
||||
}
|
||||
)
|
||||
.into(holder.view, object : Callback {
|
||||
override fun onSuccess() {
|
||||
if (!loaded && loadedListener != null && pageToLoad == position) {
|
||||
holder.view.viewTreeObserver.addOnPreDrawListener(object :
|
||||
ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean {
|
||||
holder.view.viewTreeObserver.removeOnPreDrawListener(this)
|
||||
loadedListener.invoke()
|
||||
return true
|
||||
}
|
||||
})
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Exception?) {
|
||||
if (!loaded && loadedListener != null && pageToLoad == position) {
|
||||
loadedListener.invoke()
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
holder.view.transitionName = galleryTransitionName(position)
|
||||
if (itemClickListener != null) {
|
||||
holder.view.setOnClickListener {
|
||||
itemClickListener.onItemClick(holder.view, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun galleryTransitionName(position: Int) = "gallery_$position"
|
||||
|
||||
class ChargerPhotoDiffCallback : DiffUtil.ItemCallback<ChargerPhoto>() {
|
||||
override fun areItemsTheSame(oldItem: ChargerPhoto, newItem: ChargerPhoto): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: ChargerPhoto, newItem: ChargerPhoto): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package net.vonforst.evmap.api
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import okhttp3.Call
|
||||
import okhttp3.Callback
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import java.io.IOException
|
||||
import kotlin.coroutines.resumeWithException
|
||||
|
||||
operator fun <T> JSONArray.iterator(): Iterator<T> =
|
||||
(0 until length()).asSequence().map { get(it) as T }.iterator()
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
suspend fun Call.await(): Response {
|
||||
return suspendCancellableCoroutine { continuation ->
|
||||
enqueue(object : Callback {
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
continuation.resume(response) {}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
if (continuation.isCancelled) return
|
||||
continuation.resumeWithException(e)
|
||||
}
|
||||
})
|
||||
|
||||
continuation.invokeOnCancellation {
|
||||
try {
|
||||
cancel()
|
||||
} catch (ex: Throwable) {
|
||||
//Ignore cancel exception
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const val earthRadiusKm: Double = 6372.8
|
||||
|
||||
/**
|
||||
* Calculates the distance between two points on Earth in meters.
|
||||
* Latitude and longitude should be given in degrees.
|
||||
*/
|
||||
fun distanceBetween(
|
||||
startLatitude: Double, startLongitude: Double,
|
||||
endLatitude: Double, endLongitude: Double
|
||||
): Double {
|
||||
// see https://rosettacode.org/wiki/Haversine_formula#Java
|
||||
val dLat = Math.toRadians(endLatitude - startLatitude);
|
||||
val dLon = Math.toRadians(endLongitude - startLongitude);
|
||||
val originLat = Math.toRadians(startLatitude);
|
||||
val destinationLat = Math.toRadians(endLatitude);
|
||||
|
||||
val a = Math.pow(Math.sin(dLat / 2), 2.toDouble()) + Math.pow(
|
||||
Math.sin(dLon / 2),
|
||||
2.toDouble()
|
||||
) * Math.cos(originLat) * Math.cos(destinationLat);
|
||||
val c = 2 * Math.asin(Math.sqrt(a));
|
||||
return earthRadiusKm * c * 1000;
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package net.vonforst.evmap.api.availability
|
||||
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.vonforst.evmap.api.await
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.api.goingelectric.Chargepoint
|
||||
import net.vonforst.evmap.viewmodel.Resource
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
interface AvailabilityDetector {
|
||||
suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus
|
||||
}
|
||||
|
||||
abstract class BaseAvailabilityDetector(private val client: OkHttpClient) : AvailabilityDetector {
|
||||
protected val radius = 150 // max radius in meters
|
||||
|
||||
protected suspend fun httpGet(url: String): String {
|
||||
val request = Request.Builder().url(url).build()
|
||||
val response = client.newCall(request).await()
|
||||
|
||||
if (!response.isSuccessful) throw IOException(response.message())
|
||||
|
||||
val str = response.body()!!.string()
|
||||
return str
|
||||
}
|
||||
|
||||
protected fun getCorrespondingChargepoint(
|
||||
cps: Iterable<Chargepoint>, type: String, power: Double
|
||||
): Chargepoint? {
|
||||
var filter = cps.filter {
|
||||
it.type == type
|
||||
}
|
||||
if (filter.size > 1) {
|
||||
filter = filter.filter {
|
||||
if (power > 0) {
|
||||
it.power == power
|
||||
} else true
|
||||
}
|
||||
// TODO: handle not matching powers
|
||||
/*if (filter.isEmpty()) {
|
||||
filter = listOfNotNull(cps.minBy {
|
||||
abs(it.power - power)
|
||||
})
|
||||
}*/
|
||||
}
|
||||
return filter.getOrNull(0)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun matchChargepoints(
|
||||
connectors: Map<Long, Pair<Double, String>>,
|
||||
chargepoints: List<Chargepoint>
|
||||
): Map<Chargepoint, Set<Long>> {
|
||||
// iterate over each connector type
|
||||
val types = connectors.map { it.value.second }.distinct().toSet()
|
||||
val geTypes = chargepoints.map { it.type }.distinct().toSet()
|
||||
if (types != geTypes) throw AvailabilityDetectorException("chargepoints do not match")
|
||||
return types.flatMap { type ->
|
||||
// find connectors of this type
|
||||
val connsOfType = connectors.filter { it.value.second == type }
|
||||
// find powers this connector is available as
|
||||
val powers = connsOfType.map { it.value.first }.distinct().sorted()
|
||||
// find corresponding powers in GE data
|
||||
val gePowers =
|
||||
chargepoints.filter { it.type == type }.map { it.power }.distinct().sorted()
|
||||
|
||||
// if the distinct number of powers is the same, try to match.
|
||||
if (powers.size == gePowers.size) {
|
||||
gePowers.zip(powers).map { (gePower, power) ->
|
||||
val chargepoint =
|
||||
chargepoints.find { it.type == type && it.power == gePower }!!
|
||||
val ids = connsOfType.filter { it.value.first == power }.keys
|
||||
if (chargepoint.count != ids.size) {
|
||||
throw AvailabilityDetectorException("chargepoints do not match")
|
||||
}
|
||||
chargepoint to ids
|
||||
}
|
||||
} else if (powers.size == 1 && gePowers.size == 2
|
||||
&& chargepoints.sumBy { it.count } == connsOfType.size
|
||||
) {
|
||||
// special case: dual charger(s) with load balancing
|
||||
// GoingElectric shows 2 different powers, NewMotion just one
|
||||
val allIds = connsOfType.keys.toList()
|
||||
var i = 0
|
||||
gePowers.map { gePower ->
|
||||
val chargepoint =
|
||||
chargepoints.find { it.type == type && it.power == gePower }!!
|
||||
val ids = allIds.subList(i, i + chargepoint.count).toSet()
|
||||
i += chargepoint.count
|
||||
chargepoint to ids
|
||||
}
|
||||
// TODO: this will not necessarily first fill up the higher-power chargepoint
|
||||
} else {
|
||||
throw AvailabilityDetectorException("chargepoints do not match")
|
||||
}
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ChargeLocationStatus(
|
||||
val status: Map<Chargepoint, List<ChargepointStatus>>,
|
||||
val source: String
|
||||
)
|
||||
|
||||
enum class ChargepointStatus {
|
||||
AVAILABLE, UNKNOWN, CHARGING, OCCUPIED, FAULTED
|
||||
}
|
||||
|
||||
class AvailabilityDetectorException(message: String) : Exception(message)
|
||||
|
||||
private val okhttp = OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(StethoInterceptor())
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
.connectTimeout(10, TimeUnit.SECONDS)
|
||||
.build()
|
||||
val availabilityDetectors = listOf(
|
||||
NewMotionAvailabilityDetector(okhttp)
|
||||
/*ChargecloudAvailabilityDetector(
|
||||
okhttp,
|
||||
"606a0da0dfdd338ee4134605653d4fd8"
|
||||
), // Maingau
|
||||
ChargecloudAvailabilityDetector(
|
||||
okhttp,
|
||||
"6336fe713f2eb7fa04b97ff6651b76f8"
|
||||
) // SW Kiel*/
|
||||
)
|
||||
|
||||
suspend fun getAvailability(charger: ChargeLocation): Resource<ChargeLocationStatus> {
|
||||
var value: Resource<ChargeLocationStatus>? = null
|
||||
withContext(Dispatchers.IO) {
|
||||
for (ad in availabilityDetectors) {
|
||||
try {
|
||||
value = Resource.success(ad.getAvailability(charger))
|
||||
break
|
||||
} catch (e: IOException) {
|
||||
value = Resource.error(e.message, null)
|
||||
e.printStackTrace()
|
||||
} catch (e: HttpException) {
|
||||
value = Resource.error(e.message, null)
|
||||
e.printStackTrace()
|
||||
} catch (e: AvailabilityDetectorException) {
|
||||
value = Resource.error(e.message, null)
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
return value ?: Resource.error(null, null)
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package net.vonforst.evmap.api.availability
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.api.goingelectric.Chargepoint
|
||||
import net.vonforst.evmap.api.iterator
|
||||
import okhttp3.OkHttpClient
|
||||
import org.json.JSONObject
|
||||
import java.io.IOException
|
||||
|
||||
class ChargecloudAvailabilityDetector(
|
||||
client: OkHttpClient,
|
||||
private val operatorId: String
|
||||
) : BaseAvailabilityDetector(client) {
|
||||
@ExperimentalCoroutinesApi
|
||||
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
|
||||
val url =
|
||||
"https://app.chargecloud.de/emobility:ocpi/$operatorId/app/2.0/locations?latitude=${location.coordinates.lat}&longitude=${location.coordinates.lng}&radius=$radius&offset=0&limit=10"
|
||||
val json = JSONObject(httpGet(url))
|
||||
|
||||
val statusMessage = json.getString("status_message")
|
||||
if (statusMessage != "Success") throw IOException(statusMessage)
|
||||
|
||||
val data = json.getJSONArray("data")
|
||||
if (data.length() > 1) throw AvailabilityDetectorException(
|
||||
"found multiple candidates."
|
||||
)
|
||||
if (data.length() == 0) throw AvailabilityDetectorException(
|
||||
"no candidates found."
|
||||
)
|
||||
|
||||
val evses = data.getJSONObject(0).getJSONArray("evses")
|
||||
val chargepointStatus = mutableMapOf<Chargepoint, List<ChargepointStatus>>()
|
||||
evses.iterator<JSONObject>().forEach { evse ->
|
||||
evse.getJSONArray("connectors").iterator<JSONObject>().forEach connector@{ connector ->
|
||||
val type = getType(connector.getString("standard"))
|
||||
val power = connector.getDouble("max_power")
|
||||
val status = ChargepointStatus.valueOf(connector.getString("status"))
|
||||
|
||||
var chargepoint = getCorrespondingChargepoint(chargepointStatus.keys, type, power)
|
||||
val statusList: List<ChargepointStatus>
|
||||
if (chargepoint == null) {
|
||||
// find corresponding chargepoint from goingelectric to get correct power
|
||||
val geChargepoint =
|
||||
getCorrespondingChargepoint(location.chargepointsMerged, type, power)
|
||||
?: throw AvailabilityDetectorException(
|
||||
"Chargepoints from chargecloud API and goingelectric do not match."
|
||||
)
|
||||
chargepoint = Chargepoint(
|
||||
type,
|
||||
geChargepoint.power,
|
||||
1
|
||||
)
|
||||
statusList = listOf(status)
|
||||
} else {
|
||||
val previousStatus = chargepointStatus[chargepoint]!!
|
||||
statusList = previousStatus + listOf(status)
|
||||
chargepointStatus.remove(chargepoint)
|
||||
chargepoint =
|
||||
Chargepoint(
|
||||
chargepoint.type,
|
||||
chargepoint.power,
|
||||
chargepoint.count + 1
|
||||
)
|
||||
}
|
||||
|
||||
chargepointStatus[chargepoint] = statusList
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (chargepointStatus.keys == location.chargepointsMerged.toSet()) {
|
||||
return ChargeLocationStatus(
|
||||
chargepointStatus,
|
||||
"chargecloud.de"
|
||||
)
|
||||
} else {
|
||||
throw AvailabilityDetectorException(
|
||||
"Chargepoints from chargecloud API and goingelectric do not match."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getType(string: String): String {
|
||||
return when (string) {
|
||||
"IEC_62196_T2" -> Chargepoint.TYPE_2
|
||||
"DOMESTIC_F" -> Chargepoint.SCHUKO
|
||||
"IEC_62196_T2_COMBO" -> Chargepoint.CCS
|
||||
"CHADEMO" -> Chargepoint.CHADEMO
|
||||
else -> throw IllegalArgumentException("unrecognized type $string")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
package net.vonforst.evmap.api.availability
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import net.vonforst.evmap.api.distanceBetween
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.api.goingelectric.Chargepoint
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
|
||||
private const val coordRange = 0.1 // range of latitude and longitude for loading the map
|
||||
private const val maxDistance = 15 // max distance between reported positions in meters
|
||||
|
||||
interface NewMotionApi {
|
||||
@GET("markers/{lngMin}/{lngMax}/{latMin}/{latMax}")
|
||||
suspend fun getMarkers(
|
||||
@Path("lngMin") lngMin: Double,
|
||||
@Path("lngMax") lngMax: Double,
|
||||
@Path("latMin") latMin: Double,
|
||||
@Path("latMax") latMax: Double
|
||||
): List<NMMarker>
|
||||
|
||||
@GET("locations/{id}")
|
||||
suspend fun getLocation(@Path("id") id: Long): NMLocation
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMMarker(val coordinates: NMCoordinates, val locationUid: Long)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMCoordinates(val latitude: Double, val longitude: Double)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMLocation(
|
||||
val uid: Long,
|
||||
val coordinates: NMCoordinates,
|
||||
val operatorName: String,
|
||||
val evses: List<NMEvse>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMEvse(val evseId: String?, val status: String, val connectors: List<NMConnector>)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMConnector(
|
||||
val uid: Long,
|
||||
val connectorType: String,
|
||||
val electricalProperties: NMElectricalProperties
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class NMElectricalProperties(val powerType: String, val voltage: Int, val amperage: Int) {
|
||||
fun getPower(): Double {
|
||||
val phases = when (powerType) {
|
||||
"AC1Phase" -> 1
|
||||
"AC3Phase" -> 3
|
||||
else -> 1
|
||||
}
|
||||
val volt = when (voltage) {
|
||||
277 -> 230
|
||||
else -> voltage
|
||||
}
|
||||
val power = volt * amperage * phases
|
||||
return when (power) {
|
||||
3680 -> 3.7
|
||||
11040 -> 11.0
|
||||
22080 -> 22.0
|
||||
43470 -> 43.0
|
||||
else -> power / 1000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(client: OkHttpClient, baseUrl: String? = null): NewMotionApi {
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl(baseUrl ?: "https://my.newmotion.com/api/map/v2/")
|
||||
.addConverterFactory(MoshiConverterFactory.create())
|
||||
.client(client)
|
||||
.build()
|
||||
return retrofit.create(NewMotionApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NewMotionAvailabilityDetector(client: OkHttpClient, baseUrl: String? = null) :
|
||||
BaseAvailabilityDetector(client) {
|
||||
val api = NewMotionApi.create(client, baseUrl)
|
||||
|
||||
override suspend fun getAvailability(location: ChargeLocation): ChargeLocationStatus {
|
||||
val lat = location.coordinates.lat
|
||||
val lng = location.coordinates.lng
|
||||
|
||||
// find nearest station to this position
|
||||
var markers =
|
||||
api.getMarkers(lng - coordRange, lng + coordRange, lat - coordRange, lat + coordRange)
|
||||
val nearest = markers.minBy { marker ->
|
||||
distanceBetween(marker.coordinates.latitude, marker.coordinates.longitude, lat, lng)
|
||||
} ?: throw AvailabilityDetectorException("no candidates found.")
|
||||
|
||||
if (distanceBetween(
|
||||
nearest.coordinates.latitude,
|
||||
nearest.coordinates.longitude,
|
||||
lat,
|
||||
lng
|
||||
) > radius
|
||||
) {
|
||||
throw AvailabilityDetectorException("no candidates found")
|
||||
}
|
||||
|
||||
// combine related stations
|
||||
markers = markers.filter { marker ->
|
||||
distanceBetween(
|
||||
marker.coordinates.latitude,
|
||||
marker.coordinates.longitude,
|
||||
nearest.coordinates.latitude,
|
||||
nearest.coordinates.longitude
|
||||
) < maxDistance
|
||||
}
|
||||
|
||||
// load details
|
||||
var details = markers.map {
|
||||
api.getLocation(it.locationUid)
|
||||
}
|
||||
// only include stations from same operator
|
||||
details = details.filter {
|
||||
it.operatorName == details[0].operatorName
|
||||
}
|
||||
val connectorStatus = details.flatMap { it.evses }.flatMap { evse ->
|
||||
evse.connectors.map { connector ->
|
||||
connector to evse.status
|
||||
}
|
||||
}
|
||||
|
||||
val nmConnectors = mutableMapOf<Long, Pair<Double, String>>()
|
||||
val nmStatus = mutableMapOf<Long, ChargepointStatus>()
|
||||
connectorStatus.forEach { (connector, statusStr) ->
|
||||
val id = connector.uid
|
||||
val power = connector.electricalProperties.getPower()
|
||||
val type = when (connector.connectorType) {
|
||||
"Type3" -> Chargepoint.TYPE_3
|
||||
"Type2" -> Chargepoint.TYPE_2
|
||||
"Type1" -> Chargepoint.TYPE_1
|
||||
"Domestic" -> Chargepoint.SCHUKO
|
||||
"Type2Combo" -> Chargepoint.CCS
|
||||
"TepcoCHAdeMO" -> Chargepoint.CHADEMO
|
||||
"Unspecified" -> "unspecified"
|
||||
else -> throw IllegalArgumentException("unrecognized type ${connector.connectorType}")
|
||||
}
|
||||
val status = when (statusStr) {
|
||||
"Unavailable" -> ChargepointStatus.FAULTED
|
||||
"Available" -> ChargepointStatus.AVAILABLE
|
||||
"Occupied" -> ChargepointStatus.CHARGING
|
||||
"Unspecified" -> ChargepointStatus.UNKNOWN
|
||||
else -> ChargepointStatus.UNKNOWN
|
||||
}
|
||||
nmConnectors.put(id, power to type)
|
||||
nmStatus.put(id, status)
|
||||
}
|
||||
|
||||
val match = matchChargepoints(nmConnectors, location.chargepointsMerged)
|
||||
val chargepointStatus = match.mapValues { entry ->
|
||||
entry.value.map { nmStatus[it]!! }
|
||||
}
|
||||
return ChargeLocationStatus(
|
||||
chargepointStatus,
|
||||
"NewMotion"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
package net.vonforst.evmap.api.goingelectric
|
||||
|
||||
import com.squareup.moshi.*
|
||||
import java.lang.reflect.Type
|
||||
import java.time.Instant
|
||||
import java.time.LocalTime
|
||||
|
||||
|
||||
internal class ChargepointListItemJsonAdapterFactory : JsonAdapter.Factory {
|
||||
override fun create(
|
||||
type: Type,
|
||||
annotations: MutableSet<out Annotation>,
|
||||
moshi: Moshi
|
||||
): JsonAdapter<*>? {
|
||||
if (Types.getRawType(type) == ChargepointListItem::class.java) {
|
||||
return ChargepointListItemJsonAdapter(
|
||||
moshi
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
internal class ChargepointListItemJsonAdapter(val moshi: Moshi) :
|
||||
JsonAdapter<ChargepointListItem>() {
|
||||
private val clusterAdapter =
|
||||
moshi.adapter<ChargeLocationCluster>(
|
||||
ChargeLocationCluster::class.java
|
||||
)
|
||||
|
||||
private val locationAdapter = moshi.adapter<ChargeLocation>(
|
||||
ChargeLocation::class.java
|
||||
)
|
||||
|
||||
@FromJson
|
||||
override fun fromJson(reader: JsonReader): ChargepointListItem {
|
||||
var clustered = false
|
||||
reader.peekJson().use { peeked ->
|
||||
peeked.beginObject()
|
||||
while (peeked.hasNext()) {
|
||||
if (peeked.selectName(CLUSTERED) == 0) {
|
||||
clustered = peeked.nextBoolean()
|
||||
break
|
||||
}
|
||||
peeked.skipName()
|
||||
peeked.skipValue()
|
||||
}
|
||||
}
|
||||
return if (clustered) {
|
||||
clusterAdapter.fromJson(reader)!!
|
||||
} else {
|
||||
locationAdapter.fromJson(reader)!!
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CLUSTERED: JsonReader.Options = JsonReader.Options.of("clustered")
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: ChargepointListItem?) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
internal class JsonObjectOrFalseAdapter<T> private constructor(
|
||||
private val objectDelegate: JsonAdapter<T>,
|
||||
private val clazz: Class<*>
|
||||
) : JsonAdapter<T>() {
|
||||
|
||||
class Factory() : JsonAdapter.Factory {
|
||||
override fun create(
|
||||
type: Type,
|
||||
annotations: Set<Annotation>?,
|
||||
moshi: Moshi
|
||||
): JsonAdapter<Any>? {
|
||||
val clazz = Types.getRawType(type)
|
||||
return when (hasJsonObjectOrFalseAnnotation(
|
||||
annotations
|
||||
)) {
|
||||
false -> null
|
||||
true -> JsonObjectOrFalseAdapter(
|
||||
moshi.adapter(type), clazz
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun fromJson(reader: JsonReader): T? = when (reader.peek()) {
|
||||
JsonReader.Token.BOOLEAN -> when (reader.nextBoolean()) {
|
||||
false -> null // Response was false
|
||||
else -> {
|
||||
if (this.clazz == FaultReport::class.java) {
|
||||
FaultReport(null, null) as T
|
||||
} else {
|
||||
throw IllegalStateException("Non-false boolean for @JsonObjectOrFalse field")
|
||||
}
|
||||
}
|
||||
}
|
||||
JsonReader.Token.BEGIN_OBJECT -> objectDelegate.fromJson(reader)
|
||||
JsonReader.Token.BEGIN_ARRAY -> objectDelegate.fromJson(reader)
|
||||
JsonReader.Token.STRING -> objectDelegate.fromJson(reader)
|
||||
JsonReader.Token.NUMBER -> objectDelegate.fromJson(reader)
|
||||
else ->
|
||||
throw IllegalStateException("Non-object-non-boolean value for @JsonObjectOrFalse field")
|
||||
}
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: T?) = objectDelegate.toJson(writer, value)
|
||||
}
|
||||
|
||||
private fun hasJsonObjectOrFalseAnnotation(annotations: Set<Annotation>?) =
|
||||
annotations?.firstOrNull { it.annotationClass == JsonObjectOrFalse::class } != null
|
||||
|
||||
@JsonQualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
@Target(AnnotationTarget.FIELD)
|
||||
annotation class JsonObjectOrFalse {
|
||||
|
||||
}
|
||||
|
||||
internal class HoursAdapter {
|
||||
private val regex = Regex("from (.*) till (.*)")
|
||||
|
||||
@FromJson
|
||||
fun fromJson(str: String): Hours? {
|
||||
if (str == "closed") {
|
||||
return Hours(null, null)
|
||||
} else {
|
||||
val match = regex.find(str)
|
||||
?: throw IllegalArgumentException("$str does not match hours format")
|
||||
return Hours(
|
||||
LocalTime.parse(match.groupValues[1]),
|
||||
LocalTime.parse(match.groupValues[2])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ToJson
|
||||
fun toJson(value: Hours): String {
|
||||
if (value.start == null || value.end == null) {
|
||||
return "closed"
|
||||
} else {
|
||||
return "from ${value.start} till ${value.end}"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal class InstantAdapter {
|
||||
@FromJson
|
||||
fun fromJson(value: Long?): Instant? = value?.let {
|
||||
Instant.ofEpochSecond(it)
|
||||
}
|
||||
|
||||
@ToJson
|
||||
fun toJson(value: Instant?): Long? = value?.epochSecond
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package net.vonforst.evmap.api.goingelectric
|
||||
|
||||
import android.content.Context
|
||||
import com.facebook.stetho.okhttp3.StethoInterceptor
|
||||
import com.squareup.moshi.Moshi
|
||||
import net.vonforst.evmap.BuildConfig
|
||||
import okhttp3.Cache
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Call
|
||||
import retrofit2.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface GoingElectricApi {
|
||||
@GET("chargepoints/")
|
||||
suspend fun getChargepoints(
|
||||
@Query("sw_lat") swlat: Double, @Query("sw_lng") sw_lng: Double,
|
||||
@Query("ne_lat") ne_lat: Double, @Query("ne_lng") ne_lng: Double,
|
||||
@Query("zoom") zoom: Float,
|
||||
@Query("clustering") clustering: Boolean = false,
|
||||
@Query("cluster_distance") clusterDistance: Int? = null,
|
||||
@Query("freecharging") freecharging: Boolean = false,
|
||||
@Query("freeparking") freeparking: Boolean = false,
|
||||
@Query("min_power") minPower: Int = 0,
|
||||
@Query("plugs") plugs: String? = null,
|
||||
@Query("chargecards") chargecards: String? = null,
|
||||
@Query("networks") networks: String? = null,
|
||||
@Query("startkey") startkey: Int? = null,
|
||||
@Query("open_twentyfourseven") open247: Boolean = false,
|
||||
@Query("barrierfree") barrierfree: Boolean = false,
|
||||
@Query("exclude_faults") excludeFaults: Boolean = false
|
||||
): Response<ChargepointList>
|
||||
|
||||
@GET("chargepoints/")
|
||||
fun getChargepointDetail(@Query("ge_id") id: Long): Call<ChargepointList>
|
||||
|
||||
@GET("chargepoints/pluglist/")
|
||||
suspend fun getPlugs(): Response<StringList>
|
||||
|
||||
@GET("chargepoints/networklist/")
|
||||
suspend fun getNetworks(): Response<StringList>
|
||||
|
||||
@GET("chargepoints/chargecardlist/")
|
||||
suspend fun getChargeCards(): Response<ChargeCardList>
|
||||
|
||||
companion object {
|
||||
private val cacheSize = 10L * 1024 * 1024; // 10MB
|
||||
|
||||
fun create(
|
||||
apikey: String,
|
||||
baseurl: String = "https://api.goingelectric.de",
|
||||
context: Context? = null
|
||||
): GoingElectricApi {
|
||||
val client = OkHttpClient.Builder().apply {
|
||||
addInterceptor { chain ->
|
||||
// add API key to every request
|
||||
var original = chain.request()
|
||||
val url = original.url().newBuilder().addQueryParameter("key", apikey).build()
|
||||
original = original.newBuilder().url(url).build()
|
||||
chain.proceed(original)
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
addNetworkInterceptor(StethoInterceptor())
|
||||
}
|
||||
if (context != null) {
|
||||
cache(Cache(context.getCacheDir(), cacheSize))
|
||||
}
|
||||
}.build()
|
||||
|
||||
val moshi = Moshi.Builder()
|
||||
.add(ChargepointListItemJsonAdapterFactory())
|
||||
.add(JsonObjectOrFalseAdapter.Factory())
|
||||
.add(HoursAdapter())
|
||||
.add(InstantAdapter())
|
||||
.build()
|
||||
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl(baseurl)
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.client(client)
|
||||
.build()
|
||||
return retrofit.create(GoingElectricApi::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
package net.vonforst.evmap.api.goingelectric
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.Equatable
|
||||
import java.time.DayOfWeek
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargepointList(
|
||||
val status: String,
|
||||
val chargelocations: List<ChargepointListItem>,
|
||||
@JsonObjectOrFalse val startkey: Int?
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class StringList(
|
||||
val status: String,
|
||||
val result: List<String>
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargeCardList(
|
||||
val status: String,
|
||||
val result: List<ChargeCard>
|
||||
)
|
||||
|
||||
sealed class ChargepointListItem
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Entity
|
||||
data class ChargeLocation(
|
||||
@Json(name = "ge_id") @PrimaryKey val id: Long,
|
||||
val name: String,
|
||||
@Embedded val coordinates: Coordinate,
|
||||
@Embedded val address: Address,
|
||||
val chargepoints: List<Chargepoint>,
|
||||
@JsonObjectOrFalse val network: String?,
|
||||
val url: String,
|
||||
@Embedded(prefix = "fault_report_") @JsonObjectOrFalse @Json(name = "fault_report") val faultReport: FaultReport?,
|
||||
val verified: Boolean,
|
||||
// only shown in details:
|
||||
@JsonObjectOrFalse val operator: String?,
|
||||
@JsonObjectOrFalse @Json(name = "general_information") val generalInformation: String?,
|
||||
@JsonObjectOrFalse @Json(name = "ladeweile") val amenities: String?,
|
||||
@JsonObjectOrFalse @Json(name = "location_description") val locationDescription: String?,
|
||||
val photos: List<ChargerPhoto>?,
|
||||
@JsonObjectOrFalse val chargecards: List<ChargeCardId>?,
|
||||
@Embedded val openinghours: OpeningHours?,
|
||||
@Embedded val cost: Cost?
|
||||
) : ChargepointListItem(), Equatable {
|
||||
/**
|
||||
* maximum power available from this charger.
|
||||
*/
|
||||
val maxPower: Double
|
||||
get() {
|
||||
return maxPower()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum power available from certain connectors of this charger.
|
||||
*/
|
||||
fun maxPower(connectors: Set<String>? = null): Double {
|
||||
return chargepoints.filter { connectors?.contains(it.type) ?: true }
|
||||
.map { it.power }.max() ?: 0.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges chargepoints if they have the same plug and power
|
||||
*
|
||||
* This occurs e.g. for Type2 sockets and plugs, which are distinct on the GE website, but not
|
||||
* separable in the API
|
||||
*/
|
||||
val chargepointsMerged: List<Chargepoint>
|
||||
get() {
|
||||
val variants = chargepoints.distinctBy { it.power to it.type }
|
||||
return variants.map { variant ->
|
||||
val count = chargepoints
|
||||
.filter { it.type == variant.type && it.power == variant.power }
|
||||
.sumBy { it.count }
|
||||
Chargepoint(variant.type, variant.power, count)
|
||||
}
|
||||
}
|
||||
|
||||
fun formatChargepoints(): String {
|
||||
return chargepointsMerged.map {
|
||||
"${it.count} × ${it.type} ${it.formatPower()}"
|
||||
}.joinToString(" · ")
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Cost(
|
||||
val freecharging: Boolean,
|
||||
val freeparking: Boolean,
|
||||
@JsonObjectOrFalse @Json(name = "description_short") val descriptionShort: String?,
|
||||
@JsonObjectOrFalse @Json(name = "description_long") val descriptionLong: String?
|
||||
) {
|
||||
fun getStatusText(ctx: Context): CharSequence {
|
||||
return HtmlCompat.fromHtml(
|
||||
ctx.getString(
|
||||
R.string.cost_detail,
|
||||
if (freecharging) ctx.getString(R.string.free) else ctx.getString(R.string.paid),
|
||||
if (freeparking) ctx.getString(R.string.free) else ctx.getString(R.string.paid)
|
||||
), 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OpeningHours(
|
||||
@Json(name = "24/7") val twentyfourSeven: Boolean,
|
||||
@JsonObjectOrFalse val description: String?,
|
||||
@Embedded val days: OpeningHoursDays?
|
||||
) {
|
||||
val isEmpty: Boolean
|
||||
get() = description == "Leider noch keine Informationen zu Öffnungszeiten vorhanden."
|
||||
&& days == null && !twentyfourSeven
|
||||
|
||||
fun getStatusText(ctx: Context): CharSequence {
|
||||
if (twentyfourSeven) {
|
||||
return HtmlCompat.fromHtml(ctx.getString(R.string.open_247), 0)
|
||||
} else if (days != null) {
|
||||
val hours = days.getHoursForDate(LocalDate.now())
|
||||
if (hours.start == null || hours.end == null) {
|
||||
return HtmlCompat.fromHtml(ctx.getString(R.string.closed), 0)
|
||||
}
|
||||
|
||||
val now = LocalTime.now()
|
||||
if (hours.start.isBefore(now) && hours.end.isAfter(now)) {
|
||||
return HtmlCompat.fromHtml(
|
||||
ctx.getString(
|
||||
R.string.open_closesat,
|
||||
hours.end.toString()
|
||||
), 0
|
||||
)
|
||||
} else if (hours.end.isBefore(now)) {
|
||||
return HtmlCompat.fromHtml(ctx.getString(R.string.closed), 0)
|
||||
} else {
|
||||
return HtmlCompat.fromHtml(
|
||||
ctx.getString(
|
||||
R.string.closed_opensat,
|
||||
hours.start.toString()
|
||||
), 0
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class OpeningHoursDays(
|
||||
@Embedded(prefix = "mo") val monday: Hours,
|
||||
@Embedded(prefix = "tu") val tuesday: Hours,
|
||||
@Embedded(prefix = "we") val wednesday: Hours,
|
||||
@Embedded(prefix = "th") val thursday: Hours,
|
||||
@Embedded(prefix = "fr") val friday: Hours,
|
||||
@Embedded(prefix = "sa") val saturday: Hours,
|
||||
@Embedded(prefix = "su") val sunday: Hours,
|
||||
@Embedded(prefix = "ho") val holiday: Hours
|
||||
) {
|
||||
fun getHoursForDate(date: LocalDate): Hours {
|
||||
// TODO: check for holidays
|
||||
return getHoursForDayOfWeek(date.dayOfWeek)
|
||||
}
|
||||
|
||||
fun getHoursForDayOfWeek(dayOfWeek: DayOfWeek?): Hours {
|
||||
@Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA")
|
||||
return when (dayOfWeek) {
|
||||
DayOfWeek.MONDAY -> monday
|
||||
DayOfWeek.TUESDAY -> tuesday
|
||||
DayOfWeek.WEDNESDAY -> wednesday
|
||||
DayOfWeek.THURSDAY -> thursday
|
||||
DayOfWeek.FRIDAY -> friday
|
||||
DayOfWeek.SATURDAY -> saturday
|
||||
DayOfWeek.SUNDAY -> sunday
|
||||
null -> holiday
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Hours(
|
||||
val start: LocalTime?,
|
||||
val end: LocalTime?
|
||||
) {
|
||||
override fun toString(): String {
|
||||
if (start != null && end != null) {
|
||||
val fmt = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
|
||||
return "${start.format(fmt)} - ${end.format(fmt)}"
|
||||
} else {
|
||||
return "closed"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Parcelize
|
||||
data class ChargerPhoto(val id: String) : Parcelable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargeLocationCluster(
|
||||
val clusterCount: Int,
|
||||
val coordinates: Coordinate
|
||||
) : ChargepointListItem()
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Coordinate(val lat: Double, val lng: Double) {
|
||||
fun formatDMS(): String {
|
||||
return "${dms(lat, false)}, ${dms(lng, true)}"
|
||||
}
|
||||
|
||||
private fun dms(value: Double, lon: Boolean): String {
|
||||
val hemisphere = if (lon) {
|
||||
if (value >= 0) "E" else "W"
|
||||
} else {
|
||||
if (value >= 0) "N" else "S"
|
||||
}
|
||||
val d = abs(value)
|
||||
val degrees = floor(d).toInt()
|
||||
val minutes = floor((d - degrees) * 60).toInt()
|
||||
val seconds = ((d - degrees) * 60 - minutes) * 60
|
||||
return "%d°%02d'%02.1f\"%s".format(Locale.ENGLISH, degrees, minutes, seconds, hemisphere)
|
||||
}
|
||||
|
||||
fun formatDecimal(): String {
|
||||
return "%.6f, %.6f".format(Locale.ENGLISH, lat, lng)
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Address(
|
||||
@JsonObjectOrFalse val city: String?,
|
||||
@JsonObjectOrFalse val country: String?,
|
||||
@JsonObjectOrFalse val postcode: String?,
|
||||
@JsonObjectOrFalse val street: String?
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "${street ?: ""}, ${postcode ?: ""} ${city ?: ""}"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class Chargepoint(val type: String, val power: Double, val count: Int) : Equatable {
|
||||
fun formatPower(): String {
|
||||
val powerFmt = if (power - power.toInt() == 0.0) {
|
||||
"%.0f".format(power)
|
||||
} else {
|
||||
"%.1f".format(power)
|
||||
}
|
||||
return "$powerFmt kW"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_1 = "Typ1"
|
||||
const val TYPE_2 = "Typ2"
|
||||
const val TYPE_3 = "Typ3"
|
||||
const val CCS = "CCS"
|
||||
const val SCHUKO = "Schuko"
|
||||
const val CHADEMO = "CHAdeMO"
|
||||
const val SUPERCHARGER = "Tesla Supercharger"
|
||||
const val CEE_BLAU = "CEE Blau"
|
||||
const val CEE_ROT = "CEE Rot"
|
||||
}
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class FaultReport(val created: Instant?, val description: String?)
|
||||
|
||||
@Entity
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargeCard(
|
||||
@Json(name = "card_id") @PrimaryKey val id: Long,
|
||||
val name: String,
|
||||
val url: String
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargeCardId(
|
||||
val id: Long
|
||||
)
|
||||
@@ -1,66 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import net.vonforst.evmap.BuildConfig
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
import net.vonforst.evmap.R
|
||||
|
||||
|
||||
class AboutFragment : PreferenceFragmentCompat() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
|
||||
|
||||
val navController = findNavController()
|
||||
toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.about, rootKey)
|
||||
|
||||
findPreference<Preference>("version")?.summary = BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||
return when (preference?.key) {
|
||||
"github_link" -> {
|
||||
(activity as? MapsActivity)?.openUrl(getString(R.string.github_link))
|
||||
true
|
||||
}
|
||||
"privacy" -> {
|
||||
(activity as? MapsActivity)?.openUrl(getString(R.string.privacy_link))
|
||||
true
|
||||
}
|
||||
"faq" -> {
|
||||
(activity as? MapsActivity)?.openUrl(getString(R.string.faq_link))
|
||||
true
|
||||
}
|
||||
"oss_licenses" -> {
|
||||
LibsBuilder()
|
||||
.withLicenseShown(true)
|
||||
.withAboutVersionShown(false)
|
||||
.withAboutIconShown(false)
|
||||
.withActivityTitle(getString(R.string.oss_licenses))
|
||||
.withExcludedLibraries()
|
||||
.start(requireActivity())
|
||||
true
|
||||
}
|
||||
"donate" -> {
|
||||
findNavController().navigate(R.id.action_about_to_donateFragment)
|
||||
true
|
||||
}
|
||||
else -> super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.car2go.maps.model.LatLng
|
||||
import com.mapzen.android.lost.api.LocationServices
|
||||
import com.mapzen.android.lost.api.LostApiClient
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.FavoritesAdapter
|
||||
import net.vonforst.evmap.databinding.FragmentFavoritesBinding
|
||||
import net.vonforst.evmap.viewmodel.FavoritesViewModel
|
||||
import net.vonforst.evmap.viewmodel.viewModelFactory
|
||||
|
||||
class FavoritesFragment : Fragment(), LostApiClient.ConnectionCallbacks {
|
||||
private lateinit var binding: FragmentFavoritesBinding
|
||||
private lateinit var locationClient: LostApiClient
|
||||
|
||||
private val vm: FavoritesViewModel by viewModels(factoryProducer = {
|
||||
viewModelFactory {
|
||||
FavoritesViewModel(
|
||||
requireActivity().application,
|
||||
getString(R.string.goingelectric_key)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(
|
||||
inflater,
|
||||
R.layout.fragment_favorites, container, false
|
||||
)
|
||||
binding.lifecycleOwner = this
|
||||
binding.vm = vm
|
||||
|
||||
locationClient = LostApiClient.Builder(requireContext())
|
||||
.addConnectionCallbacks(this).build()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
|
||||
|
||||
val navController = findNavController()
|
||||
toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
|
||||
val favAdapter = FavoritesAdapter(vm).apply {
|
||||
onClickListener = {
|
||||
navController.navigate(R.id.action_favs_to_map, MapFragment.showCharger(it.charger))
|
||||
}
|
||||
}
|
||||
binding.favsList.apply {
|
||||
adapter = favAdapter
|
||||
layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context, LinearLayoutManager.VERTICAL
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
locationClient.connect()
|
||||
}
|
||||
|
||||
override fun onConnected() {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
Manifest.permission.ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
val location = LocationServices.FusedLocationApi.getLastLocation(locationClient)
|
||||
if (location != null) {
|
||||
vm.location.value = LatLng(location.latitude, location.longitude)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConnectionSuspended() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.coroutines.launch
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.FiltersAdapter
|
||||
import net.vonforst.evmap.databinding.FragmentFilterBinding
|
||||
import net.vonforst.evmap.viewmodel.FilterViewModel
|
||||
import net.vonforst.evmap.viewmodel.viewModelFactory
|
||||
|
||||
class FilterFragment : Fragment() {
|
||||
private lateinit var binding: FragmentFilterBinding
|
||||
private val vm: FilterViewModel by viewModels(factoryProducer = {
|
||||
viewModelFactory {
|
||||
FilterViewModel(
|
||||
requireActivity().application,
|
||||
getString(R.string.goingelectric_key)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_filter, container, false)
|
||||
binding.lifecycleOwner = this
|
||||
binding.vm = vm
|
||||
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
|
||||
(requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
|
||||
|
||||
val navController = findNavController()
|
||||
toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
|
||||
binding.filtersList.apply {
|
||||
adapter = FiltersAdapter()
|
||||
layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context, LinearLayoutManager.VERTICAL
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toolbar.setNavigationOnClickListener {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.filter, menu)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.menu_apply -> {
|
||||
lifecycleScope.launch {
|
||||
vm.saveFilterValues()
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.transition.TransitionInflater
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.ortiz.touchview.TouchImageView
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.GalleryAdapter
|
||||
import net.vonforst.evmap.api.goingelectric.ChargerPhoto
|
||||
import net.vonforst.evmap.databinding.FragmentGalleryBinding
|
||||
import net.vonforst.evmap.viewmodel.GalleryViewModel
|
||||
|
||||
|
||||
class GalleryFragment : Fragment() {
|
||||
companion object {
|
||||
private const val EXTRA_POSITION = "position"
|
||||
private const val EXTRA_PHOTOS = "photos"
|
||||
private const val SAVED_CURRENT_PAGE_POSITION = "current_page_position"
|
||||
|
||||
fun buildArgs(photos: List<ChargerPhoto>, position: Int): Bundle {
|
||||
return Bundle().apply {
|
||||
putParcelableArrayList(EXTRA_PHOTOS, ArrayList(photos))
|
||||
putInt(EXTRA_POSITION, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var binding: FragmentGalleryBinding
|
||||
private var isReturning: Boolean = false
|
||||
private var startingPosition: Int = 0
|
||||
private var currentPosition: Int = 0
|
||||
private lateinit var galleryAdapter: GalleryAdapter
|
||||
private var currentPage: TouchImageView? = null
|
||||
private val galleryVm: GalleryViewModel by activityViewModels()
|
||||
|
||||
private val backPressedCallback = object :
|
||||
OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
val image = currentPage
|
||||
if (image != null && image.currentZoom !in 0.95f..1.05f) {
|
||||
image.setZoomAnimated(1f, 0.5f, 0.5f)
|
||||
} else {
|
||||
isReturning = true
|
||||
galleryVm.galleryPosition.value = currentPosition
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
binding = DataBindingUtil.inflate(
|
||||
inflater,
|
||||
R.layout.fragment_gallery, container, false
|
||||
)
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
val args = requireArguments()
|
||||
startingPosition = args.getInt(EXTRA_POSITION, 0)
|
||||
currentPosition =
|
||||
savedInstanceState?.getInt(SAVED_CURRENT_PAGE_POSITION) ?: startingPosition
|
||||
|
||||
galleryAdapter =
|
||||
GalleryAdapter(requireContext(), detailView = true, pageToLoad = currentPosition) {
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
binding.gallery.setPageTransformer { page, position ->
|
||||
val v = page as TouchImageView
|
||||
currentPage = v
|
||||
}
|
||||
binding.gallery.adapter = galleryAdapter
|
||||
binding.photos = args.getParcelableArrayList(EXTRA_PHOTOS)
|
||||
|
||||
binding.gallery.post {
|
||||
binding.gallery.setCurrentItem(currentPosition, false)
|
||||
binding.gallery.registerOnPageChangeCallback(object :
|
||||
ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
currentPosition = position
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
sharedElementEnterTransition = TransitionInflater.from(context)
|
||||
.inflateTransition(R.transition.image_shared_element_transition)
|
||||
sharedElementReturnTransition = TransitionInflater.from(context)
|
||||
.inflateTransition(R.transition.image_shared_element_transition)
|
||||
setEnterSharedElementCallback(enterElementCallback)
|
||||
if (savedInstanceState == null) {
|
||||
postponeEnterTransition();
|
||||
}
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback(
|
||||
viewLifecycleOwner,
|
||||
backPressedCallback
|
||||
)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(SAVED_CURRENT_PAGE_POSITION, currentPosition)
|
||||
}
|
||||
|
||||
private val enterElementCallback: SharedElementCallback = object : SharedElementCallback() {
|
||||
override fun onMapSharedElements(
|
||||
names: MutableList<String>,
|
||||
sharedElements: MutableMap<String, View>
|
||||
) {
|
||||
if (isReturning) {
|
||||
val currentPage = currentPage ?: return
|
||||
sharedElements[names[0]] = currentPage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,905 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.Manifest.permission.ACCESS_FINE_LOCATION
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.*
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.RequiresPermission
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import androidx.transition.TransitionManager
|
||||
import com.car2go.maps.AnyMap
|
||||
import com.car2go.maps.MapFragment
|
||||
import com.car2go.maps.OnMapReadyCallback
|
||||
import com.car2go.maps.model.BitmapDescriptor
|
||||
import com.car2go.maps.model.LatLng
|
||||
import com.car2go.maps.model.Marker
|
||||
import com.car2go.maps.model.MarkerOptions
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.transition.MaterialArcMotion
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
|
||||
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED
|
||||
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN
|
||||
import com.mahc.custombottomsheetbehavior.MergedAppBarLayoutBehavior
|
||||
import com.mapzen.android.lost.api.LocationServices
|
||||
import com.mapzen.android.lost.api.LostApiClient
|
||||
import io.michaelrocks.bimap.HashBiMap
|
||||
import io.michaelrocks.bimap.MutableBiMap
|
||||
import kotlinx.android.synthetic.main.fragment_map.*
|
||||
import net.vonforst.evmap.*
|
||||
import net.vonforst.evmap.adapter.ConnectorAdapter
|
||||
import net.vonforst.evmap.adapter.DetailsAdapter
|
||||
import net.vonforst.evmap.adapter.GalleryAdapter
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocationCluster
|
||||
import net.vonforst.evmap.api.goingelectric.ChargepointListItem
|
||||
import net.vonforst.evmap.autocomplete.handleAutocompleteResult
|
||||
import net.vonforst.evmap.autocomplete.launchAutocomplete
|
||||
import net.vonforst.evmap.databinding.FragmentMapBinding
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import net.vonforst.evmap.ui.ChargerIconGenerator
|
||||
import net.vonforst.evmap.ui.ClusterIconGenerator
|
||||
import net.vonforst.evmap.ui.MarkerAnimator
|
||||
import net.vonforst.evmap.ui.getMarkerTint
|
||||
import net.vonforst.evmap.viewmodel.*
|
||||
|
||||
const val REQUEST_AUTOCOMPLETE = 2
|
||||
const val ARG_CHARGER_ID = "chargerId"
|
||||
const val ARG_LAT = "lat"
|
||||
const val ARG_LON = "lon"
|
||||
|
||||
class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallback,
|
||||
LostApiClient.ConnectionCallbacks {
|
||||
private lateinit var binding: FragmentMapBinding
|
||||
private val vm: MapViewModel by viewModels(factoryProducer = {
|
||||
viewModelFactory {
|
||||
MapViewModel(
|
||||
requireActivity().application,
|
||||
getString(R.string.goingelectric_key)
|
||||
)
|
||||
}
|
||||
})
|
||||
private val galleryVm: GalleryViewModel by activityViewModels()
|
||||
private lateinit var mapFragment: MapFragment
|
||||
private var map: AnyMap? = null
|
||||
private lateinit var locationClient: LostApiClient
|
||||
private lateinit var bottomSheetBehavior: BottomSheetBehaviorGoogleMapsLike<View>
|
||||
private lateinit var detailAppBarBehavior: MergedAppBarLayoutBehavior
|
||||
private var markers: MutableBiMap<Marker, ChargeLocation> = HashBiMap()
|
||||
private var clusterMarkers: List<Marker> = emptyList()
|
||||
private var searchResultMarker: Marker? = null
|
||||
private var searchResultIcon: BitmapDescriptor? = null
|
||||
private var connectionErrorSnackbar: Snackbar? = null
|
||||
private var previousChargepointIds: Set<Long>? = null
|
||||
|
||||
private lateinit var clusterIconGenerator: ClusterIconGenerator
|
||||
private lateinit var chargerIconGenerator: ChargerIconGenerator
|
||||
private lateinit var animator: MarkerAnimator
|
||||
private lateinit var favToggle: MenuItem
|
||||
private val backPressedCallback = object : OnBackPressedCallback(false) {
|
||||
override fun handleOnBackPressed() {
|
||||
val value = vm.layersMenuOpen.value
|
||||
if (value != null && value) {
|
||||
closeLayersMenu()
|
||||
return
|
||||
}
|
||||
|
||||
val state = bottomSheetBehavior.state
|
||||
if (state != STATE_COLLAPSED && state != STATE_HIDDEN) {
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
} else if (state == STATE_COLLAPSED) {
|
||||
vm.chargerSparse.value = null
|
||||
} else if (state == STATE_HIDDEN) {
|
||||
vm.searchResult.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_map, container, false)
|
||||
binding.lifecycleOwner = this
|
||||
binding.vm = vm
|
||||
|
||||
mapFragment = MapFragment()
|
||||
val provider = PreferenceDataSource(requireContext()).mapProvider
|
||||
mapFragment.setPriority(
|
||||
arrayOf(
|
||||
when (provider) {
|
||||
"mapbox" -> MapFragment.MAPBOX
|
||||
"google" -> MapFragment.GOOGLE
|
||||
else -> null
|
||||
},
|
||||
MapFragment.GOOGLE,
|
||||
MapFragment.MAPBOX
|
||||
)
|
||||
)
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.map, mapFragment)
|
||||
.commit()
|
||||
|
||||
// reset map-related stuff (map provider may have changed)
|
||||
map = null
|
||||
markers.clear()
|
||||
clusterMarkers = emptyList()
|
||||
searchResultMarker = null
|
||||
searchResultIcon = null
|
||||
|
||||
|
||||
locationClient = LostApiClient.Builder(requireContext())
|
||||
.addConnectionCallbacks(this)
|
||||
.build()
|
||||
locationClient.connect()
|
||||
clusterIconGenerator = ClusterIconGenerator(requireContext())
|
||||
|
||||
setHasOptionsMenu(true)
|
||||
postponeEnterTransition()
|
||||
|
||||
binding.root.setOnApplyWindowInsetsListener { v, insets ->
|
||||
binding.detailAppBar.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = insets.systemWindowInsetTop
|
||||
}
|
||||
insets
|
||||
}
|
||||
|
||||
setExitSharedElementCallback(exitElementCallback)
|
||||
exitTransition = TransitionInflater.from(requireContext())
|
||||
.inflateTransition(R.transition.map_exit_transition)
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback(
|
||||
viewLifecycleOwner,
|
||||
backPressedCallback
|
||||
)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
mapFragment.getMapAsync(this)
|
||||
bottomSheetBehavior = BottomSheetBehaviorGoogleMapsLike.from(binding.bottomSheet)
|
||||
detailAppBarBehavior = MergedAppBarLayoutBehavior.from(binding.detailAppBar)
|
||||
|
||||
binding.detailAppBar.toolbar.inflateMenu(R.menu.detail)
|
||||
favToggle = binding.detailAppBar.toolbar.menu.findItem(R.id.menu_fav)
|
||||
|
||||
setupObservers()
|
||||
setupClickListeners()
|
||||
setupAdapters()
|
||||
|
||||
val navController = findNavController()
|
||||
(activity as? MapsActivity)?.setSupportActionBar(binding.toolbar)
|
||||
binding.toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val hostActivity = activity as? MapsActivity ?: return
|
||||
hostActivity.fragmentCallback = this
|
||||
}
|
||||
|
||||
private fun setupClickListeners() {
|
||||
binding.fabLocate.setOnClickListener {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
ACCESS_FINE_LOCATION
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
requestPermissions(
|
||||
arrayOf(ACCESS_FINE_LOCATION),
|
||||
REQUEST_LOCATION_PERMISSION
|
||||
)
|
||||
} else {
|
||||
enableLocation(moveTo = true, animate = true)
|
||||
}
|
||||
}
|
||||
binding.fabDirections.setOnClickListener {
|
||||
val charger = vm.charger.value?.data
|
||||
if (charger != null) {
|
||||
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
(requireActivity() as MapsActivity).navigateTo(charger)
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.fabLayers.setOnClickListener {
|
||||
openLayersMenu()
|
||||
}
|
||||
binding.detailView.goingelectricButton.setOnClickListener {
|
||||
val charger = vm.charger.value?.data
|
||||
if (charger != null) {
|
||||
(activity as? MapsActivity)?.openUrl("https:${charger.url}")
|
||||
}
|
||||
}
|
||||
binding.detailView.btnChargeprice.setOnClickListener {
|
||||
val charger = vm.charger.value?.data ?: return@setOnClickListener
|
||||
(activity as? MapsActivity)?.openUrl(
|
||||
"https://www.chargeprice.app/?poi_id=${charger.id}&poi_source=going_electric")
|
||||
}
|
||||
binding.detailView.topPart.setOnClickListener {
|
||||
bottomSheetBehavior.state = BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT
|
||||
}
|
||||
binding.search.setOnClickListener {
|
||||
launchAutocomplete(this)
|
||||
}
|
||||
binding.detailAppBar.toolbar.setNavigationOnClickListener {
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
}
|
||||
binding.detailAppBar.toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_fav -> {
|
||||
toggleFavorite()
|
||||
true
|
||||
}
|
||||
R.id.menu_share -> {
|
||||
val charger = vm.charger.value?.data
|
||||
if (charger != null) {
|
||||
(activity as? MapsActivity)?.shareUrl("https:${charger.url}")
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openLayersMenu() {
|
||||
binding.fabLayers.tag = false
|
||||
val materialTransform = MaterialContainerTransform().apply {
|
||||
startView = binding.fabLayers
|
||||
endView = binding.layersSheet
|
||||
setPathMotion(MaterialArcMotion())
|
||||
duration = 250
|
||||
scrimColor = Color.TRANSPARENT
|
||||
}
|
||||
TransitionManager.beginDelayedTransition(binding.root, materialTransform)
|
||||
vm.layersMenuOpen.value = true
|
||||
}
|
||||
|
||||
private fun closeLayersMenu() {
|
||||
binding.fabLayers.tag = true
|
||||
val materialTransform = MaterialContainerTransform().apply {
|
||||
startView = binding.layersSheet
|
||||
endView = binding.fabLayers
|
||||
setPathMotion(MaterialArcMotion())
|
||||
duration = 200
|
||||
scrimColor = Color.TRANSPARENT
|
||||
}
|
||||
TransitionManager.beginDelayedTransition(binding.root, materialTransform)
|
||||
vm.layersMenuOpen.value = false
|
||||
}
|
||||
|
||||
private fun toggleFavorite() {
|
||||
val favs = vm.favorites.value ?: return
|
||||
val charger = vm.chargerSparse.value ?: return
|
||||
if (favs.find { it.id == charger.id } != null) {
|
||||
vm.deleteFavorite(charger)
|
||||
} else {
|
||||
vm.insertFavorite(charger)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
bottomSheetBehavior.addBottomSheetCallback(object :
|
||||
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
vm.bottomSheetState.value = newState
|
||||
updateBackPressedCallback()
|
||||
|
||||
if (vm.layersMenuOpen.value!! && newState !in listOf(BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING, BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN, BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED)) {
|
||||
closeLayersMenu()
|
||||
}
|
||||
}
|
||||
})
|
||||
vm.chargerSparse.observe(viewLifecycleOwner, Observer {
|
||||
if (it != null) {
|
||||
if (vm.bottomSheetState.value != BottomSheetBehaviorGoogleMapsLike.STATE_ANCHOR_POINT) {
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
}
|
||||
binding.fabDirections.show()
|
||||
detailAppBarBehavior.setToolbarTitle(it.name)
|
||||
updateFavoriteToggle()
|
||||
highlightMarker(it)
|
||||
} else {
|
||||
bottomSheetBehavior.state = STATE_HIDDEN
|
||||
unhighlightAllMarkers()
|
||||
}
|
||||
})
|
||||
vm.chargepoints.observe(viewLifecycleOwner, Observer { res ->
|
||||
when (res.status) {
|
||||
Status.ERROR -> {
|
||||
val view = view ?: return@Observer
|
||||
|
||||
connectionErrorSnackbar?.dismiss()
|
||||
connectionErrorSnackbar = Snackbar
|
||||
.make(view, R.string.connection_error, Snackbar.LENGTH_INDEFINITE)
|
||||
.setAction(R.string.retry) {
|
||||
connectionErrorSnackbar?.dismiss()
|
||||
vm.reloadChargepoints()
|
||||
}
|
||||
connectionErrorSnackbar!!.show()
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
connectionErrorSnackbar?.dismiss()
|
||||
}
|
||||
Status.LOADING -> {
|
||||
}
|
||||
}
|
||||
|
||||
val chargepoints = res.data
|
||||
if (chargepoints != null) {
|
||||
updateMap(chargepoints)
|
||||
}
|
||||
})
|
||||
vm.favorites.observe(viewLifecycleOwner, Observer {
|
||||
updateFavoriteToggle()
|
||||
})
|
||||
vm.searchResult.observe(viewLifecycleOwner, Observer { place ->
|
||||
val map = this.map ?: return@Observer
|
||||
searchResultMarker?.remove()
|
||||
searchResultMarker = null
|
||||
|
||||
if (place != null) {
|
||||
if (place.viewport != null) {
|
||||
map.animateCamera(map.cameraUpdateFactory.newLatLngBounds(place.viewport, 0))
|
||||
} else {
|
||||
map.animateCamera(map.cameraUpdateFactory.newLatLngZoom(place.latLng, 12f))
|
||||
}
|
||||
|
||||
if (searchResultIcon == null) {
|
||||
searchResultIcon =
|
||||
map.bitmapDescriptorFactory.fromResource(R.drawable.ic_map_marker)
|
||||
}
|
||||
searchResultMarker = map.addMarker(
|
||||
MarkerOptions()
|
||||
.position(place.latLng)
|
||||
.icon(searchResultIcon)
|
||||
.anchor(0.5f, 1f)
|
||||
)
|
||||
}
|
||||
|
||||
updateBackPressedCallback()
|
||||
})
|
||||
vm.layersMenuOpen.observe(viewLifecycleOwner, Observer { open ->
|
||||
binding.fabLayers.visibility = if (open) View.GONE else View.VISIBLE
|
||||
binding.layersSheet.visibility = if (open) View.VISIBLE else View.GONE
|
||||
updateBackPressedCallback()
|
||||
})
|
||||
vm.mapType.observe(viewLifecycleOwner, Observer {
|
||||
map?.setMapType(it)
|
||||
})
|
||||
vm.mapTrafficEnabled.observe(viewLifecycleOwner, Observer {
|
||||
map?.setTrafficEnabled(it)
|
||||
})
|
||||
|
||||
updateBackPressedCallback()
|
||||
}
|
||||
|
||||
private fun updateBackPressedCallback() {
|
||||
backPressedCallback.isEnabled =
|
||||
vm.bottomSheetState.value != null && vm.bottomSheetState.value != STATE_HIDDEN
|
||||
|| vm.searchResult.value != null
|
||||
|| (vm.layersMenuOpen.value ?: false)
|
||||
}
|
||||
|
||||
private fun unhighlightAllMarkers() {
|
||||
markers.forEach { (m, c) ->
|
||||
m.setIcon(
|
||||
chargerIconGenerator.getBitmapDescriptor(
|
||||
getMarkerTint(c, vm.filteredConnectors.value), fault = c.faultReport != null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun highlightMarker(charger: ChargeLocation) {
|
||||
val marker = markers.inverse[charger] ?: return
|
||||
// highlight this marker
|
||||
marker.setIcon(
|
||||
chargerIconGenerator.getBitmapDescriptor(
|
||||
getMarkerTint(charger, vm.filteredConnectors.value),
|
||||
highlight = true,
|
||||
fault = charger.faultReport != null
|
||||
)
|
||||
)
|
||||
animator.animateMarkerBounce(marker)
|
||||
|
||||
// un-highlight all other markers
|
||||
markers.forEach { (m, c) ->
|
||||
if (m != marker) {
|
||||
m.setIcon(
|
||||
chargerIconGenerator.getBitmapDescriptor(
|
||||
getMarkerTint(c, vm.filteredConnectors.value), fault = c.faultReport != null
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFavoriteToggle() {
|
||||
val favs = vm.favorites.value ?: return
|
||||
val charger = vm.chargerSparse.value ?: return
|
||||
if (favs.find { it.id == charger.id } != null) {
|
||||
favToggle.setIcon(R.drawable.ic_fav)
|
||||
} else {
|
||||
favToggle.setIcon(R.drawable.ic_fav_no)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupAdapters() {
|
||||
val galleryClickListener = object : GalleryAdapter.ItemClickListener {
|
||||
override fun onItemClick(view: View, position: Int) {
|
||||
val photos = vm.charger.value?.data?.photos ?: return
|
||||
val extras = FragmentNavigatorExtras(view to view.transitionName)
|
||||
view.findNavController().navigate(
|
||||
R.id.action_map_to_galleryFragment,
|
||||
GalleryFragment.buildArgs(photos, position),
|
||||
null,
|
||||
extras
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val galleryPosition = galleryVm.galleryPosition.value
|
||||
binding.gallery.apply {
|
||||
adapter = GalleryAdapter(context, galleryClickListener, pageToLoad = galleryPosition) {
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
itemAnimator = null
|
||||
layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context, LinearLayoutManager.HORIZONTAL
|
||||
).apply {
|
||||
setDrawable(context.getDrawable(R.drawable.gallery_divider)!!)
|
||||
})
|
||||
}
|
||||
if (galleryPosition == null) {
|
||||
startPostponedEnterTransition()
|
||||
} else {
|
||||
binding.gallery.scrollToPosition(galleryPosition)
|
||||
// make sure that the app does not freeze waiting for a picture to load
|
||||
Handler().postDelayed({
|
||||
startPostponedEnterTransition()
|
||||
}, 500)
|
||||
}
|
||||
|
||||
binding.detailView.connectors.apply {
|
||||
adapter = ConnectorAdapter()
|
||||
itemAnimator = null
|
||||
layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
}
|
||||
|
||||
binding.detailView.details.apply {
|
||||
adapter = DetailsAdapter().apply {
|
||||
onClickListener = {
|
||||
val charger = vm.chargerDetails.value?.data
|
||||
if (charger != null) {
|
||||
when (it.icon) {
|
||||
R.drawable.ic_location -> {
|
||||
(activity as? MapsActivity)?.showLocation(charger)
|
||||
}
|
||||
R.drawable.ic_fault_report -> {
|
||||
(activity as? MapsActivity)?.openUrl("https:${charger.url}")
|
||||
}
|
||||
R.drawable.ic_payment -> {
|
||||
showPaymentMethodsDialog(charger)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
itemAnimator = null
|
||||
layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context,
|
||||
LinearLayoutManager.VERTICAL
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showPaymentMethodsDialog(charger: ChargeLocation) {
|
||||
val activity = activity ?: return
|
||||
val chargecardData = vm.chargeCardMap.value ?: return
|
||||
val chargecards = charger.chargecards ?: return
|
||||
val filteredChargeCards = vm.filteredChargeCards.value
|
||||
|
||||
val data = chargecards.mapNotNull { chargecardData[it.id] }
|
||||
.sortedBy { it.name }
|
||||
.sortedByDescending { filteredChargeCards?.contains(it.id) }
|
||||
val names = data.map {
|
||||
if (filteredChargeCards?.contains(it.id) == true) {
|
||||
it.name.bold()
|
||||
} else {
|
||||
it.name
|
||||
}
|
||||
}
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.charge_cards)
|
||||
.setItems(names.toTypedArray()) { _, i ->
|
||||
val card = data[i]
|
||||
(activity as? MapsActivity)?.openUrl("https:${card.url}")
|
||||
}.show()
|
||||
}
|
||||
|
||||
override fun onMapReady(map: AnyMap) {
|
||||
this.map = map
|
||||
chargerIconGenerator = ChargerIconGenerator(requireContext(), map.bitmapDescriptorFactory)
|
||||
animator = MarkerAnimator(chargerIconGenerator)
|
||||
map.uiSettings.setTiltGesturesEnabled(false)
|
||||
map.setIndoorEnabled(false)
|
||||
map.uiSettings.setIndoorLevelPickerEnabled(false)
|
||||
map.setOnCameraIdleListener {
|
||||
vm.mapPosition.value = MapPosition(
|
||||
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
|
||||
)
|
||||
}
|
||||
map.setOnMarkerClickListener { marker ->
|
||||
when (marker) {
|
||||
in markers -> {
|
||||
vm.chargerSparse.value = markers[marker]
|
||||
true
|
||||
}
|
||||
in clusterMarkers -> {
|
||||
val newZoom = map.cameraPosition.zoom + 2
|
||||
map.animateCamera(
|
||||
map.cameraUpdateFactory.newLatLngZoom(
|
||||
marker.position,
|
||||
newZoom
|
||||
)
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
|
||||
}
|
||||
map.setOnMapClickListener {
|
||||
if (backPressedCallback.isEnabled) {
|
||||
backPressedCallback.handleOnBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
// set padding so that compass is not obstructed by toolbar
|
||||
map.setPadding(0, binding.toolbarContainer.height, 0, 0)
|
||||
|
||||
val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
||||
map.setMapStyle(
|
||||
if (mode == Configuration.UI_MODE_NIGHT_YES) AnyMap.Style.DARK else AnyMap.Style.NORMAL
|
||||
)
|
||||
|
||||
|
||||
val position = vm.mapPosition.value
|
||||
val lat = arguments?.optDouble(ARG_LAT)
|
||||
val lon = arguments?.optDouble(ARG_LON)
|
||||
var positionSet = false
|
||||
|
||||
if (position != null) {
|
||||
val cameraUpdate =
|
||||
map.cameraUpdateFactory.newLatLngZoom(position.bounds.center, position.zoom)
|
||||
map.moveCamera(cameraUpdate)
|
||||
positionSet = true
|
||||
} else if (lat != null && lon != null) {
|
||||
// show given position
|
||||
val cameraUpdate = map.cameraUpdateFactory.newLatLngZoom(LatLng(lat, lon), 16f)
|
||||
map.moveCamera(cameraUpdate)
|
||||
|
||||
// show charger detail after chargers were loaded
|
||||
val chargerId = arguments?.optLong(ARG_CHARGER_ID)
|
||||
vm.chargepoints.observe(
|
||||
viewLifecycleOwner,
|
||||
object : Observer<Resource<List<ChargepointListItem>>> {
|
||||
override fun onChanged(res: Resource<List<ChargepointListItem>>) {
|
||||
if (res.data == null) return
|
||||
for (item in res.data) {
|
||||
if (item is ChargeLocation && item.id == chargerId) {
|
||||
vm.chargerSparse.value = item
|
||||
vm.chargepoints.removeObserver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
positionSet = true
|
||||
}
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
enableLocation(!positionSet, false)
|
||||
positionSet = true
|
||||
}
|
||||
if (!positionSet) {
|
||||
// center the camera on Europe
|
||||
val cameraUpdate =
|
||||
map.cameraUpdateFactory.newLatLngZoom(LatLng(50.113388, 9.252536), 3.5f)
|
||||
map.moveCamera(cameraUpdate)
|
||||
}
|
||||
|
||||
vm.mapPosition.value = MapPosition(
|
||||
map.projection.visibleRegion.latLngBounds, map.cameraPosition.zoom
|
||||
)
|
||||
}
|
||||
|
||||
@RequiresPermission(ACCESS_FINE_LOCATION)
|
||||
private fun enableLocation(moveTo: Boolean, animate: Boolean) {
|
||||
val map = this.map ?: return
|
||||
map.setMyLocationEnabled(true)
|
||||
vm.myLocationEnabled.value = true
|
||||
map.uiSettings.setMyLocationButtonEnabled(false)
|
||||
if (moveTo && locationClient.isConnected) {
|
||||
moveToCurrentLocation(map, animate)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(ACCESS_FINE_LOCATION)
|
||||
private fun moveToCurrentLocation(map: AnyMap, animate: Boolean) {
|
||||
val location = LocationServices.FusedLocationApi.getLastLocation(locationClient)
|
||||
if (location != null) {
|
||||
val latLng = LatLng(location.latitude, location.longitude)
|
||||
val camUpdate = map.cameraUpdateFactory.newLatLngZoom(latLng, 13f)
|
||||
if (animate) {
|
||||
map.animateCamera(camUpdate)
|
||||
} else {
|
||||
map.moveCamera(camUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun updateMap(chargepoints: List<ChargepointListItem>) {
|
||||
val map = this.map ?: return
|
||||
clusterMarkers.forEach { it.remove() }
|
||||
|
||||
val clusters = chargepoints.filterIsInstance<ChargeLocationCluster>()
|
||||
val chargers = chargepoints.filterIsInstance<ChargeLocation>()
|
||||
|
||||
val chargepointIds = chargers.map { it.id }.toSet()
|
||||
|
||||
// update icons of existing markers (connector filter may have changed)
|
||||
for ((marker, charger) in markers) {
|
||||
marker.setIcon(
|
||||
chargerIconGenerator.getBitmapDescriptor(
|
||||
getMarkerTint(charger, vm.filteredConnectors.value),
|
||||
highlight = charger == vm.chargerSparse.value,
|
||||
fault = charger.faultReport != null
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (chargers.toSet() != markers.values) {
|
||||
// remove markers that disappeared
|
||||
val bounds = map.projection.visibleRegion.latLngBounds
|
||||
markers.entries.toList().forEach {
|
||||
val marker = it.key
|
||||
val charger = it.value
|
||||
if (!chargepointIds.contains(charger.id)) {
|
||||
// animate marker if it is visible, otherwise remove immediately
|
||||
if (bounds.contains(marker.position)) {
|
||||
val tint = getMarkerTint(charger, vm.filteredConnectors.value)
|
||||
val highlight = charger == vm.chargerSparse.value
|
||||
val fault = charger.faultReport != null
|
||||
animator.animateMarkerDisappear(marker, tint, highlight, fault)
|
||||
} else {
|
||||
animator.deleteMarker(marker)
|
||||
}
|
||||
markers.remove(marker)
|
||||
}
|
||||
}
|
||||
// add new markers
|
||||
val map1 = markers.values.map { it.id }
|
||||
for (charger in chargers) {
|
||||
if (!map1.contains(charger.id)) {
|
||||
val tint = getMarkerTint(charger, vm.filteredConnectors.value)
|
||||
val highlight = charger == vm.chargerSparse.value
|
||||
val fault = charger.faultReport != null
|
||||
val marker = map.addMarker(
|
||||
MarkerOptions()
|
||||
.position(LatLng(charger.coordinates.lat, charger.coordinates.lng))
|
||||
.icon(
|
||||
chargerIconGenerator.getBitmapDescriptor(
|
||||
tint,
|
||||
0,
|
||||
255,
|
||||
highlight,
|
||||
fault
|
||||
)
|
||||
)
|
||||
.anchor(0.5f, 1f)
|
||||
)
|
||||
animator.animateMarkerAppear(marker, tint, highlight, fault)
|
||||
markers[marker] = charger
|
||||
}
|
||||
}
|
||||
previousChargepointIds = chargepointIds
|
||||
}
|
||||
clusterMarkers = clusters.map { cluster ->
|
||||
map.addMarker(
|
||||
MarkerOptions()
|
||||
.position(LatLng(cluster.coordinates.lat, cluster.coordinates.lng))
|
||||
.icon(
|
||||
map.bitmapDescriptorFactory.fromBitmap(
|
||||
clusterIconGenerator.makeIcon(
|
||||
cluster.clusterCount.toString()
|
||||
)
|
||||
)
|
||||
)
|
||||
.anchor(0.5f, 0.5f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
when (requestCode) {
|
||||
REQUEST_LOCATION_PERMISSION -> {
|
||||
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||
enableLocation(moveTo = true, animate = true)
|
||||
}
|
||||
}
|
||||
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.map, menu)
|
||||
|
||||
val filterItem = menu.findItem(R.id.menu_filter)
|
||||
val filterView = filterItem.actionView
|
||||
|
||||
val filterBadge = filterView?.findViewById<TextView>(R.id.filter_badge)
|
||||
if (filterBadge != null) {
|
||||
// set up badge showing number of active filters
|
||||
vm.filtersCount.observe(viewLifecycleOwner, Observer {
|
||||
filterBadge.visibility = if (it > 0) View.VISIBLE else View.GONE
|
||||
filterBadge.text = it.toString()
|
||||
})
|
||||
}
|
||||
filterView?.setOnClickListener {
|
||||
val popup = PopupMenu(requireContext(), it, Gravity.END)
|
||||
popup.menuInflater.inflate(R.menu.popup_filter, popup.menu)
|
||||
popup.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_edit_filters -> {
|
||||
requireView().findNavController().navigate(
|
||||
R.id.action_map_to_filterFragment
|
||||
)
|
||||
true
|
||||
}
|
||||
R.id.menu_filters_active -> {
|
||||
vm.filtersActive.value = !vm.filtersActive.value!!
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
val checkItem = popup.menu.findItem(R.id.menu_filters_active)
|
||||
vm.filtersActive.observe(viewLifecycleOwner, Observer {
|
||||
checkItem.isChecked = it
|
||||
})
|
||||
popup.show()
|
||||
}
|
||||
|
||||
filterView?.setOnLongClickListener {
|
||||
// enable/disable filters
|
||||
vm.filtersActive.value = !vm.filtersActive.value!!
|
||||
// haptic feedback
|
||||
filterView.performHapticFeedback(
|
||||
HapticFeedbackConstants.LONG_PRESS,
|
||||
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
|
||||
)
|
||||
// show snackbar
|
||||
Snackbar.make(
|
||||
requireView(), if (vm.filtersActive.value!!) {
|
||||
R.string.filters_activated
|
||||
} else {
|
||||
R.string.filters_deactivated
|
||||
}, Snackbar.LENGTH_SHORT
|
||||
).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
REQUEST_AUTOCOMPLETE -> {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
vm.searchResult.value = handleAutocompleteResult(data)
|
||||
}
|
||||
}
|
||||
else -> super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRootView(): View {
|
||||
return root
|
||||
}
|
||||
|
||||
private val exitElementCallback: SharedElementCallback = object : SharedElementCallback() {
|
||||
override fun onMapSharedElements(
|
||||
names: MutableList<String>,
|
||||
sharedElements: MutableMap<String, View>
|
||||
) {
|
||||
// Locate the ViewHolder for the clicked position.
|
||||
val position = galleryVm.galleryPosition.value ?: return
|
||||
|
||||
val vh = binding.gallery.findViewHolderForAdapterPosition(position)
|
||||
if (vh?.itemView == null) return
|
||||
|
||||
// Map the first shared element name to the child ImageView.
|
||||
sharedElements[names[0]] = vh.itemView
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun showCharger(charger: ChargeLocation): Bundle {
|
||||
return Bundle().apply {
|
||||
putLong(ARG_CHARGER_ID, charger.id)
|
||||
putDouble(ARG_LAT, charger.coordinates.lat)
|
||||
putDouble(ARG_LON, charger.coordinates.lng)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConnected() {
|
||||
val map = this.map ?: return
|
||||
if (vm.myLocationEnabled.value == true) {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
requireContext(),
|
||||
ACCESS_FINE_LOCATION
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
moveToCurrentLocation(map, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConnectionSuspended() {
|
||||
}
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatDialogFragment
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.synthetic.main.dialog_multi_select.*
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.adapter.DataBindingAdapter
|
||||
import net.vonforst.evmap.adapter.Equatable
|
||||
import java.util.*
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
|
||||
class MultiSelectDialog : AppCompatDialogFragment() {
|
||||
companion object {
|
||||
fun getInstance(
|
||||
title: String,
|
||||
data: Map<String, String>,
|
||||
selected: Set<String>,
|
||||
commonChoices: Set<String>?
|
||||
): MultiSelectDialog {
|
||||
val dialog = MultiSelectDialog()
|
||||
dialog.arguments = Bundle().apply {
|
||||
putString("title", title)
|
||||
putSerializable("data", HashMap(data))
|
||||
putSerializable("selected", HashSet(selected))
|
||||
if (commonChoices != null) putSerializable("commonChoices", HashSet(commonChoices))
|
||||
}
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
|
||||
var okListener: ((Set<String>) -> Unit)? = null
|
||||
var cancelListener: (() -> Unit)? = null
|
||||
private lateinit var items: List<MultiSelectItem>
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.dialog_multi_select, container)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
// dialog with 95% screen height
|
||||
dialog?.window?.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
(resources.displayMetrics.heightPixels * 0.95).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
val args = requireArguments()
|
||||
val data = args.getSerializable("data") as HashMap<String, String>
|
||||
val selected = args.getSerializable("selected") as HashSet<String>
|
||||
val title = args.getString("title")
|
||||
val commonChoices = if (args.containsKey("commonChoices")) {
|
||||
args.getSerializable("commonChoices") as HashSet<String>
|
||||
} else null
|
||||
|
||||
dialogTitle.text = title
|
||||
val adapter = Adapter()
|
||||
list.adapter = adapter
|
||||
list.layoutManager = LinearLayoutManager(view.context)
|
||||
|
||||
items = data.entries.toList()
|
||||
.sortedBy { it.value }
|
||||
.sortedByDescending { commonChoices?.contains(it.key) == true }
|
||||
.map { MultiSelectItem(it.key, it.value, it.key in selected) }
|
||||
adapter.submitList(items)
|
||||
|
||||
etSearch.doAfterTextChanged { text ->
|
||||
adapter.submitList(search(items, text.toString()))
|
||||
}
|
||||
|
||||
btnCancel.setOnClickListener {
|
||||
cancelListener?.let { listener ->
|
||||
listener()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
btnOK.setOnClickListener {
|
||||
okListener?.let { listener ->
|
||||
val result = items.filter { it.selected }.map { it.key }.toSet()
|
||||
listener(result)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
btnAll.setOnClickListener {
|
||||
items = items.map { MultiSelectItem(it.key, it.name, true) }
|
||||
adapter.submitList(search(items, etSearch.text.toString()))
|
||||
}
|
||||
btnNone.setOnClickListener {
|
||||
items = items.map { MultiSelectItem(it.key, it.name, false) }
|
||||
adapter.submitList(search(items, etSearch.text.toString()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun search(
|
||||
items: List<MultiSelectItem>,
|
||||
text: String
|
||||
): List<MultiSelectItem> {
|
||||
return items.filter { item ->
|
||||
// search for string within name
|
||||
text.toLowerCase(Locale.getDefault()) in item.name.toLowerCase(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
|
||||
class Adapter() : DataBindingAdapter<MultiSelectItem>({ it.key }) {
|
||||
override fun getItemViewType(position: Int) = R.layout.dialog_multi_select_item
|
||||
}
|
||||
|
||||
data class MultiSelectItem(val key: String, val name: String, var selected: Boolean) : Equatable
|
||||
@@ -1,68 +0,0 @@
|
||||
package net.vonforst.evmap.fragment
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import net.vonforst.evmap.MapsActivity
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
import net.vonforst.evmap.ui.updateNightMode
|
||||
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
private lateinit var prefs: PreferenceDataSource
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val toolbar = view.findViewById(R.id.toolbar) as Toolbar
|
||||
prefs = PreferenceDataSource(requireContext())
|
||||
|
||||
val navController = findNavController()
|
||||
toolbar.setupWithNavController(
|
||||
navController,
|
||||
(requireActivity() as MapsActivity).appBarConfiguration
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.settings, rootKey)
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
|
||||
return when (preference?.key) {
|
||||
else -> super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
when (key) {
|
||||
"language" -> {
|
||||
activity?.let {
|
||||
it.finish();
|
||||
it.startActivity(it.intent);
|
||||
}
|
||||
}
|
||||
"darkmode" -> {
|
||||
updateNightMode(prefs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
preferenceManager.sharedPreferences
|
||||
.unregisterOnSharedPreferenceChangeListener(this)
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeCard
|
||||
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
|
||||
import java.io.IOException
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@Dao
|
||||
interface ChargeCardDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(vararg chargeCards: ChargeCard)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(vararg chargeCards: ChargeCard)
|
||||
|
||||
@Query("SELECT * FROM chargeCard")
|
||||
fun getAllChargeCards(): LiveData<List<ChargeCard>>
|
||||
}
|
||||
|
||||
class ChargeCardRepository(
|
||||
private val api: GoingElectricApi, private val scope: CoroutineScope,
|
||||
private val dao: ChargeCardDao, private val prefs: PreferenceDataSource
|
||||
) {
|
||||
fun getChargeCards(): LiveData<List<ChargeCard>> {
|
||||
scope.launch {
|
||||
updateChargeCards()
|
||||
}
|
||||
return dao.getAllChargeCards()
|
||||
}
|
||||
|
||||
private suspend fun updateChargeCards() {
|
||||
if (Duration.between(prefs.lastChargeCardUpdate, Instant.now()) < Duration.ofDays(1)) return
|
||||
|
||||
try {
|
||||
val response = api.getChargeCards()
|
||||
if (!response.isSuccessful) return
|
||||
|
||||
for (card in response.body()!!.result) {
|
||||
dao.insert(card)
|
||||
}
|
||||
|
||||
prefs.lastChargeCardUpdate = Instant.now()
|
||||
} catch (e: IOException) {
|
||||
// ignore, and retry next time
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
|
||||
@Dao
|
||||
interface ChargeLocationsDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(vararg locations: ChargeLocation)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(vararg locations: ChargeLocation)
|
||||
|
||||
@Query("SELECT * FROM chargelocation")
|
||||
fun getAllChargeLocations(): LiveData<List<ChargeLocation>>
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeCard
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.viewmodel.BooleanFilterValue
|
||||
import net.vonforst.evmap.viewmodel.MultipleChoiceFilterValue
|
||||
import net.vonforst.evmap.viewmodel.SliderFilterValue
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
ChargeLocation::class,
|
||||
BooleanFilterValue::class,
|
||||
MultipleChoiceFilterValue::class,
|
||||
SliderFilterValue::class,
|
||||
Plug::class,
|
||||
Network::class,
|
||||
ChargeCard::class
|
||||
], version = 8
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun chargeLocationsDao(): ChargeLocationsDao
|
||||
abstract fun filterValueDao(): FilterValueDao
|
||||
abstract fun plugDao(): PlugDao
|
||||
abstract fun networkDao(): NetworkDao
|
||||
abstract fun chargeCardDao(): ChargeCardDao
|
||||
|
||||
companion object {
|
||||
private lateinit var context: Context
|
||||
private val database: AppDatabase by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
Room.databaseBuilder(context, AppDatabase::class.java, "evmap.db")
|
||||
.addMigrations(
|
||||
MIGRATION_2, MIGRATION_3, MIGRATION_4, MIGRATION_5, MIGRATION_6,
|
||||
MIGRATION_7, MIGRATION_8
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
fun getInstance(context: Context): AppDatabase {
|
||||
this.context = context.applicationContext
|
||||
return database
|
||||
}
|
||||
|
||||
private val MIGRATION_2 = object : Migration(1, 2) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
// SQL for creating tables copied from build/generated/source/kapt/debug/net/vonforst/evmap/storage/AppDatbase_Impl
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `BooleanFilterValue` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))")
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `MultipleChoiceFilterValue` (`key` TEXT NOT NULL, `values` TEXT NOT NULL, `all` INTEGER NOT NULL, PRIMARY KEY(`key`))")
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `SliderFilterValue` (`key` TEXT NOT NULL, `value` INTEGER NOT NULL, PRIMARY KEY(`key`))");
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_3 = object : Migration(2, 3) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
// recreate ChargeLocation table to make postcode nullable
|
||||
db.beginTransaction()
|
||||
try {
|
||||
db.execSQL("CREATE TABLE `ChargeLocationNew` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `chargepoints` TEXT NOT NULL, `network` TEXT, `url` TEXT NOT NULL, `verified` INTEGER NOT NULL, `operator` TEXT, `generalInformation` TEXT, `amenities` TEXT, `locationDescription` TEXT, `photos` TEXT, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `city` TEXT NOT NULL, `country` TEXT NOT NULL, `postcode` TEXT, `street` TEXT NOT NULL, `twentyfourSeven` INTEGER, `description` TEXT, `mostart` TEXT, `moend` TEXT, `tustart` TEXT, `tuend` TEXT, `westart` TEXT, `weend` TEXT, `thstart` TEXT, `thend` TEXT, `frstart` TEXT, `frend` TEXT, `sastart` TEXT, `saend` TEXT, `sustart` TEXT, `suend` TEXT, `hostart` TEXT, `hoend` TEXT, `freecharging` INTEGER, `freeparking` INTEGER, `descriptionShort` TEXT, `descriptionLong` TEXT, PRIMARY KEY(`id`))");
|
||||
db.execSQL("INSERT INTO `ChargeLocationNew` SELECT * FROM `ChargeLocation`");
|
||||
db.execSQL("DROP TABLE `ChargeLocation`")
|
||||
db.execSQL("ALTER TABLE `ChargeLocationNew` RENAME TO `ChargeLocation`")
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_4 = object : Migration(3, 4) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `Plug` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))")
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_5 = object : Migration(4, 5) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
// recreate ChargeLocation table to make other address fields nullable
|
||||
db.beginTransaction()
|
||||
try {
|
||||
db.execSQL("CREATE TABLE `ChargeLocationNew` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `chargepoints` TEXT NOT NULL, `network` TEXT, `url` TEXT NOT NULL, `verified` INTEGER NOT NULL, `operator` TEXT, `generalInformation` TEXT, `amenities` TEXT, `locationDescription` TEXT, `photos` TEXT, `lat` REAL NOT NULL, `lng` REAL NOT NULL, `city` TEXT, `country` TEXT, `postcode` TEXT, `street` TEXT, `twentyfourSeven` INTEGER, `description` TEXT, `mostart` TEXT, `moend` TEXT, `tustart` TEXT, `tuend` TEXT, `westart` TEXT, `weend` TEXT, `thstart` TEXT, `thend` TEXT, `frstart` TEXT, `frend` TEXT, `sastart` TEXT, `saend` TEXT, `sustart` TEXT, `suend` TEXT, `hostart` TEXT, `hoend` TEXT, `freecharging` INTEGER, `freeparking` INTEGER, `descriptionShort` TEXT, `descriptionLong` TEXT, PRIMARY KEY(`id`))");
|
||||
db.execSQL("INSERT INTO `ChargeLocationNew` SELECT * FROM `ChargeLocation`");
|
||||
db.execSQL("DROP TABLE `ChargeLocation`")
|
||||
db.execSQL("ALTER TABLE `ChargeLocationNew` RENAME TO `ChargeLocation`")
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_6 = object : Migration(5, 6) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.beginTransaction()
|
||||
try {
|
||||
db.execSQL("ALTER TABLE `ChargeLocation` ADD `fault_report_created` INTEGER")
|
||||
db.execSQL("ALTER TABLE `ChargeLocation` ADD `fault_report_description` TEXT")
|
||||
db.setTransactionSuccessful()
|
||||
} finally {
|
||||
db.endTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_7 = object : Migration(6, 7) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `Network` (`name` TEXT NOT NULL, PRIMARY KEY(`name`))")
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS `ChargeCard` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`id`))")
|
||||
}
|
||||
}
|
||||
|
||||
private val MIGRATION_8 = object : Migration(7, 8) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE `ChargeLocation` ADD `chargecards` TEXT")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.room.*
|
||||
import net.vonforst.evmap.viewmodel.BooleanFilterValue
|
||||
import net.vonforst.evmap.viewmodel.FilterValue
|
||||
import net.vonforst.evmap.viewmodel.MultipleChoiceFilterValue
|
||||
import net.vonforst.evmap.viewmodel.SliderFilterValue
|
||||
|
||||
@Dao
|
||||
abstract class FilterValueDao {
|
||||
@Query("SELECT * FROM booleanfiltervalue")
|
||||
protected abstract fun getBooleanFilterValues(): LiveData<List<BooleanFilterValue>>
|
||||
|
||||
@Query("SELECT * FROM multiplechoicefiltervalue")
|
||||
protected abstract fun getMultipleChoiceFilterValues(): LiveData<List<MultipleChoiceFilterValue>>
|
||||
|
||||
@Query("SELECT * FROM sliderfiltervalue")
|
||||
protected abstract fun getSliderFilterValues(): LiveData<List<SliderFilterValue>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insert(vararg values: BooleanFilterValue)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insert(vararg values: MultipleChoiceFilterValue)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
protected abstract suspend fun insert(vararg values: SliderFilterValue)
|
||||
|
||||
open fun getFilterValues(): LiveData<List<FilterValue>> =
|
||||
MediatorLiveData<List<FilterValue>>().apply {
|
||||
val sources = listOf(
|
||||
getBooleanFilterValues(),
|
||||
getMultipleChoiceFilterValues(),
|
||||
getSliderFilterValues()
|
||||
)
|
||||
for (source in sources) {
|
||||
addSource(source) {
|
||||
value = sources.mapNotNull { it.value }.flatten()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Transaction
|
||||
open suspend fun insert(vararg values: FilterValue) {
|
||||
values.forEach {
|
||||
when (it) {
|
||||
is BooleanFilterValue -> insert(it)
|
||||
is MultipleChoiceFilterValue -> insert(it)
|
||||
is SliderFilterValue -> insert(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
|
||||
import java.io.IOException
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@Entity
|
||||
data class Network(@PrimaryKey val name: String)
|
||||
|
||||
@Dao
|
||||
interface NetworkDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(vararg networks: Network)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(vararg networks: Network)
|
||||
|
||||
@Query("SELECT * FROM network")
|
||||
fun getAllNetworks(): LiveData<List<Network>>
|
||||
}
|
||||
|
||||
class NetworkRepository(
|
||||
private val api: GoingElectricApi, private val scope: CoroutineScope,
|
||||
private val dao: NetworkDao, private val prefs: PreferenceDataSource
|
||||
) {
|
||||
fun getNetworks(): LiveData<List<Network>> {
|
||||
scope.launch {
|
||||
updateNetworks()
|
||||
}
|
||||
return dao.getAllNetworks()
|
||||
}
|
||||
|
||||
private suspend fun updateNetworks() {
|
||||
if (Duration.between(prefs.lastNetworkUpdate, Instant.now()) < Duration.ofDays(1)) return
|
||||
|
||||
try {
|
||||
val response = api.getNetworks()
|
||||
if (!response.isSuccessful) return
|
||||
|
||||
for (name in response.body()!!.result) {
|
||||
dao.insert(Network(name))
|
||||
}
|
||||
|
||||
prefs.lastNetworkUpdate = Instant.now()
|
||||
} catch (e: IOException) {
|
||||
// ignore, and retry next time
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import net.vonforst.evmap.api.goingelectric.GoingElectricApi
|
||||
import java.io.IOException
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@Entity
|
||||
data class Plug(@PrimaryKey val name: String)
|
||||
|
||||
@Dao
|
||||
interface PlugDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insert(vararg plugs: Plug)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(vararg plugs: Plug)
|
||||
|
||||
@Query("SELECT * FROM plug")
|
||||
fun getAllPlugs(): LiveData<List<Plug>>
|
||||
}
|
||||
|
||||
class PlugRepository(
|
||||
private val api: GoingElectricApi, private val scope: CoroutineScope,
|
||||
private val dao: PlugDao, private val prefs: PreferenceDataSource
|
||||
) {
|
||||
fun getPlugs(): LiveData<List<Plug>> {
|
||||
scope.launch {
|
||||
updatePlugs()
|
||||
}
|
||||
return dao.getAllPlugs()
|
||||
}
|
||||
|
||||
private suspend fun updatePlugs() {
|
||||
if (Duration.between(prefs.lastPlugUpdate, Instant.now()) < Duration.ofDays(1)) return
|
||||
|
||||
try {
|
||||
val response = api.getPlugs()
|
||||
if (!response.isSuccessful) return
|
||||
|
||||
for (name in response.body()!!.result) {
|
||||
dao.insert(Plug(name))
|
||||
}
|
||||
|
||||
prefs.lastPlugUpdate = Instant.now()
|
||||
} catch (e: IOException) {
|
||||
// ignore, and retry next time
|
||||
e.printStackTrace()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
import net.vonforst.evmap.R
|
||||
import java.time.Instant
|
||||
|
||||
class PreferenceDataSource(val context: Context) {
|
||||
val sp = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
var navigateUseMaps: Boolean
|
||||
get() = sp.getBoolean("navigate_use_maps", true)
|
||||
set(value) {
|
||||
sp.edit().putBoolean("navigate_use_maps", value).apply()
|
||||
}
|
||||
|
||||
var lastPlugUpdate: Instant
|
||||
get() = Instant.ofEpochMilli(sp.getLong("last_plug_update", 0L))
|
||||
set(value) {
|
||||
sp.edit().putLong("last_plug_update", value.toEpochMilli()).apply()
|
||||
}
|
||||
|
||||
var lastNetworkUpdate: Instant
|
||||
get() = Instant.ofEpochMilli(sp.getLong("last_network_update", 0L))
|
||||
set(value) {
|
||||
sp.edit().putLong("last_network_update", value.toEpochMilli()).apply()
|
||||
}
|
||||
|
||||
var lastChargeCardUpdate: Instant
|
||||
get() = Instant.ofEpochMilli(sp.getLong("last_chargecard_update", 0L))
|
||||
set(value) {
|
||||
sp.edit().putLong("last_chargecard_update", value.toEpochMilli()).apply()
|
||||
}
|
||||
|
||||
var filtersActive: Boolean
|
||||
get() = sp.getBoolean("filters_active", true)
|
||||
set(value) {
|
||||
sp.edit().putBoolean("filters_active", value).apply()
|
||||
}
|
||||
|
||||
val language: String
|
||||
get() = sp.getString("language", "default")!!
|
||||
|
||||
val darkmode: String
|
||||
get() = sp.getString("darkmode", "default")!!
|
||||
|
||||
val mapProvider: String
|
||||
get() = sp.getString(
|
||||
"map_provider",
|
||||
context.getString(R.string.pref_map_provider_default)
|
||||
)!!
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package net.vonforst.evmap.storage
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeCardId
|
||||
import net.vonforst.evmap.api.goingelectric.Chargepoint
|
||||
import net.vonforst.evmap.api.goingelectric.ChargerPhoto
|
||||
import java.time.Instant
|
||||
import java.time.LocalTime
|
||||
|
||||
class Converters {
|
||||
val moshi = Moshi.Builder().build()
|
||||
private val chargepointListAdapter by lazy {
|
||||
val type = Types.newParameterizedType(List::class.java, Chargepoint::class.java)
|
||||
moshi.adapter<List<Chargepoint>>(type)
|
||||
}
|
||||
private val chargerPhotoListAdapter by lazy {
|
||||
val type = Types.newParameterizedType(List::class.java, ChargerPhoto::class.java)
|
||||
moshi.adapter<List<ChargerPhoto>>(type)
|
||||
}
|
||||
private val chargeCardIdListAdapter by lazy {
|
||||
val type = Types.newParameterizedType(List::class.java, ChargeCardId::class.java)
|
||||
moshi.adapter<List<ChargeCardId>>(type)
|
||||
}
|
||||
private val stringSetAdapter by lazy {
|
||||
val type = Types.newParameterizedType(Set::class.java, String::class.java)
|
||||
moshi.adapter<Set<String>>(type)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromChargepointList(value: List<Chargepoint>?): String {
|
||||
return chargepointListAdapter.toJson(value)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toChargepointList(value: String): List<Chargepoint>? {
|
||||
return chargepointListAdapter.fromJson(value)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromChargerPhotoList(value: List<ChargerPhoto>?): String {
|
||||
return chargerPhotoListAdapter.toJson(value)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toChargerPhotoList(value: String): List<ChargerPhoto>? {
|
||||
return chargerPhotoListAdapter.fromJson(value)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromChargeCardIdList(value: List<ChargeCardId>?): String {
|
||||
return chargeCardIdListAdapter.toJson(value)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toChargeCardIdList(value: String?): List<ChargeCardId>? {
|
||||
return value?.let { chargeCardIdListAdapter.fromJson(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromLocalTime(value: LocalTime?): String? {
|
||||
return value?.toString()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toLocalTime(value: String?): LocalTime? {
|
||||
return value?.let {
|
||||
LocalTime.parse(it)
|
||||
}
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromInstant(value: Instant?): Long? {
|
||||
return value?.toEpochMilli()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toInstant(value: Long?): Instant? {
|
||||
return value?.let {
|
||||
Instant.ofEpochMilli(it)
|
||||
}
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromStringSet(value: Set<String>?): String {
|
||||
return stringSetAdapter.toJson(value)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toStringSet(value: String): Set<String>? {
|
||||
return stringSetAdapter.fromJson(value)
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.view.View
|
||||
import android.view.ViewAnimationUtils
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import kotlin.math.hypot
|
||||
|
||||
fun View.startCircularReveal() {
|
||||
addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
|
||||
override fun onLayoutChange(
|
||||
v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int,
|
||||
oldRight: Int, oldBottom: Int
|
||||
) {
|
||||
v.removeOnLayoutChangeListener(this)
|
||||
val cx = v.right
|
||||
val cy = v.top
|
||||
val radius = hypot(right.toDouble(), bottom.toDouble()).toInt()
|
||||
ViewAnimationUtils.createCircularReveal(v, cx, cy, 0f, radius.toFloat()).apply {
|
||||
interpolator = DecelerateInterpolator(2f)
|
||||
duration = 1000
|
||||
start()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun View.exitCircularReveal(block: () -> Unit) {
|
||||
val startRadius = hypot(this.width.toDouble(), this.height.toDouble())
|
||||
ViewAnimationUtils.createCircularReveal(this, this.width, 0, startRadius.toFloat(), 0f).apply {
|
||||
duration = 350
|
||||
interpolator = DecelerateInterpolator(1f)
|
||||
addListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
visibility = View.INVISIBLE
|
||||
block()
|
||||
super.onAnimationEnd(animation)
|
||||
}
|
||||
})
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the position of the current [View]'s center in the screen
|
||||
*/
|
||||
fun View.findLocationOfCenterOnTheScreen(): IntArray {
|
||||
val positions = intArrayOf(0, 0)
|
||||
getLocationInWindow(positions)
|
||||
// Get the center of the view
|
||||
positions[0] = positions[0] + width / 2
|
||||
positions[1] = positions[1] + height / 2
|
||||
return positions
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.use
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.api.availability.ChargepointStatus
|
||||
import net.vonforst.evmap.api.goingelectric.Chargepoint
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
@BindingAdapter("goneUnless")
|
||||
fun goneUnless(view: View, visible: Boolean) {
|
||||
view.visibility = if (visible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
@BindingAdapter("invisibleUnless")
|
||||
fun invisibleUnless(view: View, visible: Boolean) {
|
||||
view.visibility = if (visible) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
@BindingAdapter("isFabActive")
|
||||
fun isFabActive(view: FloatingActionButton, isColored: Boolean) {
|
||||
val color = view.context.theme.obtainStyledAttributes(
|
||||
intArrayOf(
|
||||
if (isColored) {
|
||||
R.attr.colorAccent
|
||||
} else {
|
||||
R.attr.colorControlNormal
|
||||
}
|
||||
)
|
||||
)
|
||||
view.imageTintList = ColorStateList.valueOf(color.getColor(0, 0))
|
||||
}
|
||||
|
||||
@BindingAdapter("data")
|
||||
fun <T> setRecyclerViewData(recyclerView: RecyclerView, items: List<T>?) {
|
||||
if (recyclerView.adapter is ListAdapter<*, *>) {
|
||||
(recyclerView.adapter as ListAdapter<T, *>).submitList(items)
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("data")
|
||||
fun <T> setRecyclerViewData(recyclerView: ViewPager2, items: List<T>?) {
|
||||
if (recyclerView.adapter is ListAdapter<*, *>) {
|
||||
(recyclerView.adapter as ListAdapter<T, *>).submitList(items)
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("connectorIcon")
|
||||
fun getConnectorItem(view: ImageView, type: String) {
|
||||
view.setImageResource(
|
||||
when (type) {
|
||||
Chargepoint.CCS -> R.drawable.ic_connector_ccs
|
||||
Chargepoint.CHADEMO -> R.drawable.ic_connector_chademo
|
||||
Chargepoint.SCHUKO -> R.drawable.ic_connector_schuko
|
||||
Chargepoint.SUPERCHARGER -> R.drawable.ic_connector_supercharger
|
||||
Chargepoint.TYPE_2 -> R.drawable.ic_connector_typ2
|
||||
Chargepoint.CEE_BLAU -> R.drawable.ic_connector_cee_blau
|
||||
Chargepoint.CEE_ROT -> R.drawable.ic_connector_cee_rot
|
||||
Chargepoint.TYPE_1 -> R.drawable.ic_connector_typ1
|
||||
// TODO: add other connectors
|
||||
else -> 0
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@BindingAdapter("srcCompat")
|
||||
fun setImageResource(imageView: ImageView, resource: Int) {
|
||||
imageView.setImageResource(resource)
|
||||
}
|
||||
|
||||
@BindingAdapter("android:contentDescription")
|
||||
fun setContentDescriptionResource(imageView: ImageView, resource: Int) {
|
||||
imageView.contentDescription = imageView.context.getString(resource)
|
||||
}
|
||||
|
||||
@BindingAdapter("tintAvailability")
|
||||
fun setImageTintAvailability(view: ImageView, available: List<ChargepointStatus>?) {
|
||||
view.imageTintList = ColorStateList.valueOf(availabilityColor(available, view.context))
|
||||
}
|
||||
|
||||
@BindingAdapter("textColorAvailability")
|
||||
fun setTextColorAvailability(view: TextView, available: List<ChargepointStatus>?) {
|
||||
view.setTextColor(availabilityColor(available, view.context))
|
||||
}
|
||||
|
||||
@BindingAdapter("backgroundTintAvailability")
|
||||
fun setBackgroundTintAvailability(view: View, available: List<ChargepointStatus>?) {
|
||||
view.backgroundTintList = ColorStateList.valueOf(availabilityColor(available, view.context))
|
||||
}
|
||||
|
||||
@BindingAdapter("selectableItemBackground")
|
||||
fun applySelectableItemBackground(view: View, apply: Boolean) {
|
||||
if (apply) {
|
||||
view.context.obtainStyledAttributes(intArrayOf(R.attr.selectableItemBackground)).use {
|
||||
view.background = it.getDrawable(0)
|
||||
}
|
||||
} else {
|
||||
view.background = null
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("htmlText")
|
||||
fun setHtmlTextValue(textView: TextView, htmlText: String?) {
|
||||
if (htmlText == null) {
|
||||
textView.text = null
|
||||
} else {
|
||||
textView.text = HtmlCompat.fromHtml(htmlText, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("android:layout_marginTop")
|
||||
fun setTopMargin(view: View, topMargin: Float) {
|
||||
val layoutParams = view.layoutParams as MarginLayoutParams
|
||||
layoutParams.setMargins(
|
||||
layoutParams.leftMargin, topMargin.roundToInt(),
|
||||
layoutParams.rightMargin, layoutParams.bottomMargin
|
||||
)
|
||||
view.layoutParams = layoutParams
|
||||
}
|
||||
|
||||
private fun availabilityColor(
|
||||
status: List<ChargepointStatus>?,
|
||||
context: Context
|
||||
): Int = if (status != null) {
|
||||
val unknown = status.any { it == ChargepointStatus.UNKNOWN }
|
||||
val available = status.count { it == ChargepointStatus.AVAILABLE }
|
||||
|
||||
if (unknown) {
|
||||
ContextCompat.getColor(context, R.color.unknown)
|
||||
} else if (available > 0) {
|
||||
ContextCompat.getColor(context, R.color.available)
|
||||
} else {
|
||||
ContextCompat.getColor(context, R.color.unavailable)
|
||||
}
|
||||
} else {
|
||||
val ta = context.theme.obtainStyledAttributes(intArrayOf(R.attr.colorControlNormal))
|
||||
ta.getColor(0, 0)
|
||||
}
|
||||
|
||||
fun availabilityText(status: List<ChargepointStatus>?): String? {
|
||||
if (status == null) return null
|
||||
|
||||
val total = status.size
|
||||
val unknown = status.count { it == ChargepointStatus.UNKNOWN }
|
||||
val available = status.count { it == ChargepointStatus.AVAILABLE }
|
||||
|
||||
return if (unknown > 0) {
|
||||
if (unknown == total) "?" else "$available?"
|
||||
} else available.toString()
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package net.vonforst.evmap.ui;
|
||||
|
||||
import com.car2go.maps.model.LatLng
|
||||
import com.google.maps.android.clustering.ClusterItem
|
||||
import com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocationCluster
|
||||
import net.vonforst.evmap.api.goingelectric.ChargepointListItem
|
||||
import net.vonforst.evmap.api.goingelectric.Coordinate
|
||||
|
||||
|
||||
fun cluster(
|
||||
result: List<ChargepointListItem>,
|
||||
zoom: Float,
|
||||
clusterDistance: Int
|
||||
): List<ChargepointListItem> {
|
||||
val clusters = result.filterIsInstance<ChargeLocationCluster>()
|
||||
val locations = result.filterIsInstance<ChargeLocation>()
|
||||
|
||||
val clusterItems = locations.map { ChargepointClusterItem(it) }
|
||||
|
||||
val algo = NonHierarchicalDistanceBasedAlgorithm<ChargepointClusterItem>()
|
||||
algo.maxDistanceBetweenClusteredItems = clusterDistance
|
||||
algo.addItems(clusterItems)
|
||||
return algo.getClusters(zoom).map {
|
||||
if (it.size == 1) {
|
||||
it.items.first().charger
|
||||
} else {
|
||||
ChargeLocationCluster(it.size, Coordinate(it.position.latitude, it.position.longitude))
|
||||
}
|
||||
} + clusters
|
||||
}
|
||||
|
||||
private class ChargepointClusterItem(val charger: ChargeLocation) : ClusterItem {
|
||||
override fun getSnippet(): String? = null
|
||||
|
||||
override fun getTitle(): String? = charger.name
|
||||
|
||||
override fun getPosition(): LatLng = LatLng(charger.coordinates.lat, charger.coordinates.lng)
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
|
||||
|
||||
|
||||
class HideOnExpandFabBehavior(context: Context, attrs: AttributeSet) :
|
||||
FloatingActionButton.Behavior(context, attrs) {
|
||||
|
||||
override fun onStartNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
directTargetChild: View,
|
||||
target: View,
|
||||
axes: Int,
|
||||
type: Int
|
||||
): Boolean {
|
||||
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(
|
||||
coordinatorLayout,
|
||||
child,
|
||||
directTargetChild,
|
||||
target,
|
||||
axes,
|
||||
type
|
||||
)
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(
|
||||
parent: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
dependency: View
|
||||
): Boolean {
|
||||
if (dependency is NestedScrollView) {
|
||||
try {
|
||||
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
|
||||
behavior.addBottomSheetCallback(object :
|
||||
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
onDependentViewChanged(parent, child, dependency)
|
||||
}
|
||||
})
|
||||
return true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onDependentViewChanged(
|
||||
parent: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
dependency: View
|
||||
): Boolean {
|
||||
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
|
||||
when (behavior.state) {
|
||||
BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING -> {
|
||||
|
||||
}
|
||||
BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN -> {
|
||||
if (child.tag as? Boolean != false) child.show()
|
||||
}
|
||||
BottomSheetBehaviorGoogleMapsLike.STATE_COLLAPSED -> {
|
||||
if (child.tag as? Boolean != false) child.show()
|
||||
}
|
||||
else -> {
|
||||
child.hide()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
target: View,
|
||||
dxConsumed: Int,
|
||||
dyConsumed: Int,
|
||||
dxUnconsumed: Int,
|
||||
dyUnconsumed: Int,
|
||||
type: Int,
|
||||
consumed: IntArray
|
||||
) {
|
||||
super.onNestedScroll(
|
||||
coordinatorLayout,
|
||||
child,
|
||||
target,
|
||||
dxConsumed,
|
||||
dyConsumed,
|
||||
dxUnconsumed,
|
||||
dyUnconsumed,
|
||||
type,
|
||||
consumed
|
||||
)
|
||||
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
|
||||
// User scrolled down and the FAB is currently visible -> hide the FAB
|
||||
child.hide();
|
||||
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
|
||||
// User scrolled up and the FAB is currently not visible -> show the FAB
|
||||
child.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.mahc.custombottomsheetbehavior.BottomSheetBehaviorGoogleMapsLike
|
||||
|
||||
|
||||
class HideOnScrollFabBehavior(context: Context, attrs: AttributeSet) :
|
||||
FloatingActionButton.Behavior(context, attrs) {
|
||||
|
||||
override fun onStartNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
directTargetChild: View,
|
||||
target: View,
|
||||
axes: Int,
|
||||
type: Int
|
||||
): Boolean {
|
||||
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(
|
||||
coordinatorLayout,
|
||||
child,
|
||||
directTargetChild,
|
||||
target,
|
||||
axes,
|
||||
type
|
||||
)
|
||||
}
|
||||
|
||||
override fun layoutDependsOn(
|
||||
parent: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
dependency: View
|
||||
): Boolean {
|
||||
if (dependency is NestedScrollView) {
|
||||
try {
|
||||
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
|
||||
behavior.addBottomSheetCallback(object :
|
||||
BottomSheetBehaviorGoogleMapsLike.BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
onDependentViewChanged(parent, child, dependency)
|
||||
}
|
||||
})
|
||||
return true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onDependentViewChanged(
|
||||
parent: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
dependency: View
|
||||
): Boolean {
|
||||
val behavior = BottomSheetBehaviorGoogleMapsLike.from<View>(dependency)
|
||||
when (behavior.state) {
|
||||
BottomSheetBehaviorGoogleMapsLike.STATE_SETTLING -> {
|
||||
|
||||
}
|
||||
BottomSheetBehaviorGoogleMapsLike.STATE_HIDDEN -> {
|
||||
child.show()
|
||||
}
|
||||
else -> {
|
||||
child.hide()
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
target: View,
|
||||
dxConsumed: Int,
|
||||
dyConsumed: Int,
|
||||
dxUnconsumed: Int,
|
||||
dyUnconsumed: Int,
|
||||
type: Int,
|
||||
consumed: IntArray
|
||||
) {
|
||||
super.onNestedScroll(
|
||||
coordinatorLayout,
|
||||
child,
|
||||
target,
|
||||
dxConsumed,
|
||||
dyConsumed,
|
||||
dxUnconsumed,
|
||||
dyUnconsumed,
|
||||
type,
|
||||
consumed
|
||||
)
|
||||
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
|
||||
// User scrolled down and the FAB is currently visible -> hide the FAB
|
||||
child.hide();
|
||||
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
|
||||
// User scrolled up and the FAB is currently not visible -> show the FAB
|
||||
child.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.LruCache
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import com.car2go.maps.BitmapDescriptorFactory
|
||||
import com.car2go.maps.model.BitmapDescriptor
|
||||
import com.google.maps.android.ui.IconGenerator
|
||||
import com.google.maps.android.ui.SquareTextView
|
||||
import net.vonforst.evmap.R
|
||||
|
||||
class ClusterIconGenerator(context: Context) : IconGenerator(context) {
|
||||
init {
|
||||
setBackground(context.getDrawable(R.drawable.marker_cluster_bg))
|
||||
setContentView(makeSquareTextView(context))
|
||||
}
|
||||
|
||||
private fun makeSquareTextView(context: Context): SquareTextView? {
|
||||
val density = context.resources.displayMetrics.density
|
||||
val twelveDpi = (12.0f * density).toInt()
|
||||
|
||||
return SquareTextView(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
id = R.id.amu_text
|
||||
setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi)
|
||||
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_AppCompat)
|
||||
setTextColor(ContextCompat.getColor(context, android.R.color.white))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ChargerIconGenerator(val context: Context, val factory: BitmapDescriptorFactory) {
|
||||
data class BitmapData(
|
||||
val tint: Int,
|
||||
val scale: Int,
|
||||
val alpha: Int,
|
||||
val highlight: Boolean,
|
||||
val fault: Boolean
|
||||
)
|
||||
|
||||
val cacheSize = 420; // 420 items: 21 sizes, 5 colors, highlight on/off, fault on/off
|
||||
val cache = LruCache<BitmapData, BitmapDescriptor>(cacheSize)
|
||||
val oversize = 1.4f // increase to add padding for fault icon or scale > 1
|
||||
val icon = R.drawable.ic_map_marker_charging
|
||||
val highlightIcon = R.drawable.ic_map_marker_highlight
|
||||
val faultIcon = R.drawable.ic_map_marker_fault
|
||||
|
||||
init {
|
||||
preloadCache()
|
||||
}
|
||||
|
||||
private fun preloadCache() {
|
||||
// pre-generates images for scale from 0 to 255 for all possible tint colors
|
||||
val tints = listOf(
|
||||
R.color.charger_100kw,
|
||||
R.color.charger_43kw,
|
||||
R.color.charger_20kw,
|
||||
R.color.charger_11kw,
|
||||
R.color.charger_low
|
||||
)
|
||||
for (fault in listOf(false, true)) {
|
||||
for (highlight in listOf(false, true)) {
|
||||
for (tint in tints) {
|
||||
for (scale in 0..20) {
|
||||
getBitmapDescriptor(tint, scale, 255, highlight, fault)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBitmapDescriptor(
|
||||
@ColorRes tint: Int,
|
||||
scale: Int = 20,
|
||||
alpha: Int = 255,
|
||||
highlight: Boolean = false,
|
||||
fault: Boolean = false
|
||||
): BitmapDescriptor? {
|
||||
val data = BitmapData(tint, scale, alpha, highlight, fault)
|
||||
val cachedImg = cache[data]
|
||||
return if (cachedImg != null) {
|
||||
cachedImg
|
||||
} else {
|
||||
val bitmap = generateBitmap(data)
|
||||
val bmd = factory.fromBitmap(bitmap)
|
||||
cache.put(data, bmd)
|
||||
bmd
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateBitmap(data: BitmapData): Bitmap {
|
||||
val vd: Drawable = context.getDrawable(icon)!!
|
||||
|
||||
DrawableCompat.setTint(vd, ContextCompat.getColor(context, data.tint));
|
||||
DrawableCompat.setTintMode(vd, PorterDuff.Mode.MULTIPLY);
|
||||
|
||||
val leftPadding = vd.intrinsicWidth * (oversize - 1) / 2
|
||||
val topPadding = vd.intrinsicWidth * (oversize - 1)
|
||||
vd.setBounds(
|
||||
leftPadding.toInt(), topPadding.toInt(),
|
||||
leftPadding.toInt() + vd.intrinsicWidth,
|
||||
topPadding.toInt() + vd.intrinsicHeight
|
||||
)
|
||||
vd.alpha = data.alpha
|
||||
|
||||
val bm = Bitmap.createBitmap(
|
||||
(vd.intrinsicWidth * oversize).toInt(), (vd.intrinsicHeight * oversize).toInt(),
|
||||
Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val canvas = Canvas(bm)
|
||||
|
||||
val scale = data.scale / 20f
|
||||
canvas.scale(
|
||||
scale,
|
||||
scale,
|
||||
leftPadding + vd.intrinsicWidth / 2f,
|
||||
topPadding + vd.intrinsicHeight.toFloat()
|
||||
)
|
||||
|
||||
vd.draw(canvas)
|
||||
|
||||
if (data.highlight) {
|
||||
val highlightDrawable = context.getDrawable(highlightIcon)!!
|
||||
highlightDrawable.setBounds(
|
||||
leftPadding.toInt(), topPadding.toInt(),
|
||||
leftPadding.toInt() + vd.intrinsicWidth,
|
||||
topPadding.toInt() + vd.intrinsicHeight
|
||||
)
|
||||
highlightDrawable.alpha = data.alpha
|
||||
highlightDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
if (data.fault) {
|
||||
val faultDrawable = context.getDrawable(faultIcon)!!
|
||||
val faultSize = 0.75
|
||||
val faultShift = 0.25
|
||||
val base = vd.intrinsicWidth
|
||||
faultDrawable.setBounds(
|
||||
(leftPadding.toInt() + base * (1 - faultSize + faultShift)).toInt(),
|
||||
(topPadding.toInt() - base * faultShift).toInt(),
|
||||
(leftPadding.toInt() + base * (1 + faultShift)).toInt(),
|
||||
(topPadding.toInt() + base * (faultSize - faultShift)).toInt()
|
||||
)
|
||||
faultDrawable.alpha = data.alpha
|
||||
faultDrawable.draw(canvas)
|
||||
}
|
||||
|
||||
return bm
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.animation.BounceInterpolator
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.interpolator.view.animation.FastOutLinearInInterpolator
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
|
||||
import com.car2go.maps.model.Marker
|
||||
import net.vonforst.evmap.R
|
||||
import net.vonforst.evmap.api.goingelectric.ChargeLocation
|
||||
import kotlin.math.max
|
||||
|
||||
fun getMarkerTint(
|
||||
charger: ChargeLocation,
|
||||
connectors: Set<String>?
|
||||
): Int = when {
|
||||
charger.maxPower(connectors) >= 100 -> R.color.charger_100kw
|
||||
charger.maxPower(connectors) >= 43 -> R.color.charger_43kw
|
||||
charger.maxPower(connectors) >= 20 -> R.color.charger_20kw
|
||||
charger.maxPower(connectors) >= 11 -> R.color.charger_11kw
|
||||
else -> R.color.charger_low
|
||||
}
|
||||
|
||||
class MarkerAnimator(val gen: ChargerIconGenerator) {
|
||||
private val animatingMarkers = hashMapOf<Marker, ValueAnimator>()
|
||||
|
||||
fun animateMarkerAppear(
|
||||
marker: Marker,
|
||||
tint: Int,
|
||||
highlight: Boolean,
|
||||
fault: Boolean
|
||||
) {
|
||||
animatingMarkers[marker]?.let {
|
||||
it.cancel()
|
||||
animatingMarkers.remove(marker)
|
||||
}
|
||||
|
||||
val anim = ValueAnimator.ofInt(0, 20).apply {
|
||||
duration = 250
|
||||
interpolator = LinearOutSlowInInterpolator()
|
||||
addUpdateListener { animationState ->
|
||||
val scale = animationState.animatedValue as Int
|
||||
marker.setIcon(
|
||||
gen.getBitmapDescriptor(
|
||||
tint,
|
||||
scale = scale,
|
||||
highlight = highlight,
|
||||
fault = fault
|
||||
)
|
||||
)
|
||||
}
|
||||
addListener(onEnd = {
|
||||
animatingMarkers.remove(marker)
|
||||
}, onCancel = {
|
||||
animatingMarkers.remove(marker)
|
||||
})
|
||||
}
|
||||
animatingMarkers[marker] = anim
|
||||
anim.start()
|
||||
}
|
||||
|
||||
fun animateMarkerDisappear(
|
||||
marker: Marker,
|
||||
tint: Int,
|
||||
highlight: Boolean,
|
||||
fault: Boolean
|
||||
) {
|
||||
animatingMarkers[marker]?.let {
|
||||
it.cancel()
|
||||
animatingMarkers.remove(marker)
|
||||
}
|
||||
|
||||
val anim = ValueAnimator.ofInt(20, 0).apply {
|
||||
duration = 200
|
||||
interpolator = FastOutLinearInInterpolator()
|
||||
addUpdateListener { animationState ->
|
||||
val scale = animationState.animatedValue as Int
|
||||
marker.setIcon(
|
||||
gen.getBitmapDescriptor(
|
||||
tint,
|
||||
scale = scale,
|
||||
highlight = highlight,
|
||||
fault = fault
|
||||
)
|
||||
)
|
||||
}
|
||||
addListener(onEnd = {
|
||||
marker.remove()
|
||||
animatingMarkers.remove(marker)
|
||||
}, onCancel = {
|
||||
marker.remove()
|
||||
animatingMarkers.remove(marker)
|
||||
})
|
||||
}
|
||||
animatingMarkers[marker] = anim
|
||||
anim.start()
|
||||
}
|
||||
|
||||
fun deleteMarker(marker: Marker) {
|
||||
animatingMarkers[marker]?.let {
|
||||
it.cancel()
|
||||
animatingMarkers.remove(marker)
|
||||
}
|
||||
marker.remove()
|
||||
}
|
||||
|
||||
fun animateMarkerBounce(marker: Marker) {
|
||||
animatingMarkers[marker]?.let {
|
||||
it.cancel()
|
||||
animatingMarkers.remove(marker)
|
||||
}
|
||||
|
||||
val anim = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||
duration = 700
|
||||
interpolator = BounceInterpolator()
|
||||
addUpdateListener { state ->
|
||||
val t = max(1f - state.animatedValue as Float, 0f) / 2
|
||||
marker.setAnchor(0.5f, 1.0f + t)
|
||||
}
|
||||
addListener(onEnd = {
|
||||
animatingMarkers.remove(marker)
|
||||
}, onCancel = {
|
||||
animatingMarkers.remove(marker)
|
||||
})
|
||||
}
|
||||
animatingMarkers[marker] = anim
|
||||
anim.start()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package net.vonforst.evmap.ui
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import net.vonforst.evmap.storage.PreferenceDataSource
|
||||
|
||||
fun updateNightMode(prefs: PreferenceDataSource) {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
when (prefs.darkmode) {
|
||||
"on" -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
"off" -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
)
|
||||
}
|
||||