├── .gitignore ├── LICENSE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── graphhopper │ │ └── navigation │ │ └── example │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── graphhopper │ │ │ └── navigation │ │ │ └── example │ │ │ ├── FetchGeocodingConfig.java │ │ │ ├── FetchGeocodingTask.java │ │ │ ├── FetchGeocodingTaskCallbackInterface.java │ │ │ ├── FetchSolutionConfig.java │ │ │ ├── FetchSolutionTask.java │ │ │ ├── FetchSolutionTaskCallbackInterface.java │ │ │ ├── GHAttributionDialogManager.java │ │ │ ├── GeocodingInputDialog.java │ │ │ ├── NavigationLauncherActivity.java │ │ │ ├── NavigationViewSettingsActivity.java │ │ │ ├── SimplifiedCallback.java │ │ │ └── SolutionInputDialog.java │ └── res │ │ ├── drawable-hdpi │ │ └── ic_map_marker.png │ │ ├── drawable-mdpi │ │ └── ic_map_marker.png │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ └── ic_map_marker.png │ │ ├── drawable-xxhdpi │ │ └── ic_map_marker.png │ │ ├── drawable-xxxhdpi │ │ └── ic_map_marker.png │ │ ├── drawable │ │ ├── ic_cancel_black_24dp.xml │ │ ├── ic_cloud_download_black_24dp.xml │ │ ├── ic_help_black_24dp.xml │ │ ├── ic_navigation_black_24dp.xml │ │ ├── ic_search_black_24dp.xml │ │ └── ic_settings_applications_black_24dp.xml │ │ ├── layout │ │ ├── activity_navigation_launcher.xml │ │ ├── attribution_list_item.xml │ │ ├── geocoding_input.xml │ │ └── solution_input.xml │ │ ├── menu │ │ └── navigation_view_activity_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── .gitignore │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── developer-config.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── fragment_navigation_view_preferences.xml │ └── test │ └── java │ └── com │ └── graphhopper │ └── navigation │ └── ExampleUnitTest.java ├── build.gradle ├── files ├── gh-nav1.png ├── gh-nav2.png ├── gh-nav3.png ├── graphhopper-navigation-example.png ├── graphhopper-navigation-example.xcf └── graphhopper.png ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *~ 3 | TODO.txt 4 | nbactions*.xml 5 | maven/ 6 | */nbactions.xml 7 | .idea/ 8 | *.iml 9 | .*# 10 | /nbactions.xml 11 | tools/nb-configuration.xml 12 | .settings/ 13 | .classpath 14 | .project 15 | .DS_Store 16 | package-lock.json 17 | .gradle 18 | /local.properties 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Update to MapLibre Navigation SDK for Android 2 | 3 | We recommend that you use the successor of the GraphHopper Navigation SDK, which is the [MapLibre Navigation SDK](https://github.com/maplibre/maplibre-navigation-android). 4 | An example where GraphHopper routing is integration is available [here](https://github.com/graphhopper/graphhopper-navigation-example). 5 | 6 | --------- 7 | 8 | # Legacy Readme 9 | 10 | ## GraphHopper Navigation Sample 11 | 12 | This Android example application showcases navigation based on instructions returned from the [GraphHopper Navigation](https://github.com/graphhopper/graphhopper/tree/master/navigation) component. 13 | 14 | ## Try 15 | 16 | [Download from Google Play](https://play.google.com/store/apps/details?id=com.graphhopper.navigation.example). 17 | 18 | ## Features 19 | 20 | ![features](./files/graphhopper-navigation-example.png "Feature Screenshot") 21 | 22 | - Turn-by-turn navigation including spoken turn instruction 23 | - Route planning (from A to B via intermediate points) 24 | - Load route optimization solutions from the [GraphHopper Route Optimization API](https://graphhopper.com/api/1/docs/route-optimization/) 25 | - Load routes planned on [GraphHopper Maps](https://graphhopper.com/maps/) 26 | - Search for places using the [GraphHopper Geocoding API](https://graphhopper.com/api/1/docs/geocoding/) 27 | - 100% open source 28 | - no dependency to Mapbox i.e. no Mapbox contract required. The Mapbox key can be a random string. 29 | 30 | ## Getting Started 31 | 32 | Building your own turn-by-turn navigation app based on GraphHopper is easy. Just sign up for the [GraphHopper Directions API](https://www.graphhopper.com/products/). 33 | 34 | GraphHopper does not provide map tiles. The navigation sdk is compatible with MVT vector tiles and raster tiles. There are several map providers, including: 35 | - [Mapilion](https://mapilion.com/) 36 | - [MapTiler](https://www.maptiler.com/cloud/) 37 | - [ThunderForest](http://thunderforest.com/) 38 | 39 | *Note: while it's possible to use raster tiles, we recommend vector tiles for mobile applications.* 40 | 41 | Enter your GraphHopper API key and map style url in the developer config: `app/src/main/res/values/developer-config.xml` 42 | 43 | You can fork this project and adapt it to your needs, or use it as orientation when developing your own application. 44 | 45 | ## License 46 | 47 | This project stands under the Apache License. 48 | 49 | It is a fork of the [Mapbox Sample App](https://github.com/mapbox/mapbox-navigation-android/tree/master/app), 50 | which is licensed under the [MIT](https://github.com/mapbox/mapbox-navigation-android/blob/master/LICENSE) license. 51 | 52 | ## Updating the developer-config file 53 | 54 | ``` 55 | Ignore: 56 | git update-index --assume-unchanged app/src/main/res/values/developer-config.xml 57 | Unignore: 58 | git update-index --no-assume-unchanged app/src/main/res/values/developer-config.xml 59 | ``` 60 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | 4 | 5 | android { 6 | compileSdkVersion 27 7 | defaultConfig { 8 | applicationId "com.graphhopper.navigation.example" 9 | minSdkVersion 23 10 | targetSdkVersion 27 11 | versionCode 9 12 | versionName "1.0.0-pre5" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled true 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | compileOptions { 22 | targetCompatibility 1.8 23 | sourceCompatibility 1.8 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(include: ['*.jar'], dir: 'libs') 29 | implementation 'com.android.support:appcompat-v7:27.1.1' 30 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 31 | implementation 'com.graphhopper:graphhopper-navigation-android-ui:0.1.0-SNAPSHOT' 32 | implementation 'com.graphhopper:directions-api-client:0.10.1-4' 33 | implementation 'com.jakewharton:butterknife:8.8.1' 34 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 37 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 38 | } 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/graphhopper/navigation/example/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("graphhopper.com.graphhopper_navigation_example", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 40 | 41 | 42 | 43 | 46 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/FetchGeocodingConfig.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | public class FetchGeocodingConfig { 4 | 5 | final String query; 6 | final String locale; 7 | final int limit; 8 | final boolean reverse; 9 | final String point; 10 | final String provider; 11 | 12 | FetchGeocodingConfig(String query, String locale, int limit, boolean reverse, String point, String provider) { 13 | this.query = query; 14 | this.locale = locale; 15 | this.limit = limit; 16 | this.reverse = reverse; 17 | this.point = point; 18 | this.provider = provider; 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/FetchGeocodingTask.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import com.graphhopper.directions.api.client.ApiException; 6 | import com.graphhopper.directions.api.client.api.GeocodingApi; 7 | import com.graphhopper.directions.api.client.model.GeocodingLocation; 8 | import com.graphhopper.directions.api.client.model.GeocodingResponse; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import timber.log.Timber; 14 | 15 | public class FetchGeocodingTask extends AsyncTask> { 16 | 17 | private final String ghKey; 18 | private final FetchGeocodingTaskCallbackInterface callbackInterface; 19 | 20 | FetchGeocodingTask(FetchGeocodingTaskCallbackInterface callbackInterface, String ghKey) { 21 | this.callbackInterface = callbackInterface; 22 | this.ghKey = ghKey; 23 | } 24 | 25 | @Override 26 | protected List doInBackground(FetchGeocodingConfig... geocodingConfigs) { 27 | 28 | if (geocodingConfigs.length != 1) 29 | throw new IllegalArgumentException("It's only possible to fetch one geocoding at a time"); 30 | 31 | List locations = new ArrayList<>(); 32 | GeocodingApi api = new GeocodingApi(); 33 | 34 | try { 35 | FetchGeocodingConfig geocodingConfig = geocodingConfigs[0]; 36 | GeocodingResponse res = api.geocodeGet(ghKey, geocodingConfig.query, geocodingConfig.locale, geocodingConfig.limit, geocodingConfig.reverse, geocodingConfig.point, geocodingConfig.provider); 37 | locations = res.getHits(); 38 | 39 | if (locations.isEmpty()) 40 | callbackInterface.onError(R.string.error_location_not_found); 41 | 42 | } catch (ApiException e) { 43 | callbackInterface.onError(R.string.error_fetching_geocoding); 44 | Timber.e(e, "An exception occured when fetching geocoding results for %s", geocodingConfigs[0].query); 45 | } 46 | 47 | return locations; 48 | } 49 | 50 | @Override 51 | protected void onPostExecute(List locations) { 52 | callbackInterface.onPostExecuteGeocodingSearch(locations); 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/FetchGeocodingTaskCallbackInterface.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import com.graphhopper.directions.api.client.model.GeocodingLocation; 4 | 5 | import java.util.List; 6 | 7 | public interface FetchGeocodingTaskCallbackInterface { 8 | 9 | void onError(int message); 10 | 11 | void onPostExecuteGeocodingSearch(List points); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/FetchSolutionConfig.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | public class FetchSolutionConfig { 4 | 5 | final String jobId; 6 | final String vehicleId; 7 | 8 | FetchSolutionConfig(String jobId, String vehicleId) { 9 | this.jobId = jobId; 10 | this.vehicleId = vehicleId; 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/FetchSolutionTask.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.os.AsyncTask; 4 | 5 | import com.graphhopper.directions.api.client.ApiException; 6 | import com.graphhopper.directions.api.client.api.SolutionApi; 7 | import com.graphhopper.directions.api.client.model.Activity; 8 | import com.graphhopper.directions.api.client.model.Address; 9 | import com.graphhopper.directions.api.client.model.Route; 10 | import com.mapbox.geojson.Point; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import timber.log.Timber; 16 | 17 | public class FetchSolutionTask extends AsyncTask> { 18 | 19 | private final String ghKey; 20 | private final FetchSolutionTaskCallbackInterface callbackInterface; 21 | 22 | FetchSolutionTask(FetchSolutionTaskCallbackInterface callbackInterface, String ghKey) { 23 | this.callbackInterface = callbackInterface; 24 | this.ghKey = ghKey; 25 | } 26 | 27 | @Override 28 | protected List doInBackground(FetchSolutionConfig... solutions) { 29 | 30 | if (solutions.length != 1) 31 | throw new IllegalArgumentException("It's only possible to fetch one solution at a time"); 32 | 33 | List points = new ArrayList<>(); 34 | SolutionApi api = new SolutionApi(); 35 | 36 | try { 37 | com.graphhopper.directions.api.client.model.Response res = api.getSolution(ghKey, solutions[0].jobId); 38 | List routes = res.getSolution().getRoutes(); 39 | 40 | for (Route route : routes) { 41 | if (route.getVehicleId().equals(solutions[0].vehicleId) || solutions[0].vehicleId == null) { 42 | // Found the right vehicle 43 | List activities = route.getActivities(); 44 | for (int i = 0; i < activities.size(); i++) { 45 | Activity activity = activities.get(i); 46 | Address address = activity.getAddress(); 47 | points.add(Point.fromLngLat(address.getLon(), address.getLat())); 48 | } 49 | break; 50 | } 51 | } 52 | 53 | if (points.isEmpty()) 54 | callbackInterface.onError(R.string.error_vehicle_not_found); 55 | 56 | } catch (ApiException e) { 57 | callbackInterface.onError(R.string.error_fetching_solution); 58 | Timber.e(e, "An exception occured when fetching a solution with jobId %s", solutions[0].jobId); 59 | } 60 | 61 | return points; 62 | } 63 | 64 | @Override 65 | protected void onPostExecute(List points) { 66 | callbackInterface.onPostExecute(points); 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/FetchSolutionTaskCallbackInterface.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import com.mapbox.geojson.Point; 4 | 5 | import java.util.List; 6 | 7 | public interface FetchSolutionTaskCallbackInterface { 8 | 9 | void onError(int message); 10 | 11 | void onPostExecute(List points); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/GHAttributionDialogManager.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.net.Uri; 10 | import android.support.annotation.NonNull; 11 | import android.view.View; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.Toast; 14 | 15 | import com.mapbox.mapboxsdk.attribution.Attribution; 16 | import com.mapbox.mapboxsdk.attribution.AttributionParser; 17 | import com.mapbox.mapboxsdk.maps.AttributionDialogManager; 18 | import com.mapbox.mapboxsdk.maps.MapboxMap; 19 | import com.mapbox.mapboxsdk.style.sources.Source; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | import java.util.Set; 24 | 25 | /** 26 | * This class is mostly based on {@link AttributionDialogManager}, but adds GraphHopper as 27 | * additional attribution. 28 | */ 29 | public class GHAttributionDialogManager extends AttributionDialogManager { 30 | 31 | private final Context context; 32 | private final MapboxMap mapboxMap; 33 | private Set attributionSet; 34 | 35 | public GHAttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) { 36 | super(context, mapboxMap); 37 | this.context = context; 38 | this.mapboxMap = mapboxMap; 39 | } 40 | 41 | // Called when someone presses the attribution icon on the map 42 | @Override 43 | public void onClick(View view) { 44 | attributionSet = new GHAttributionDialogManager.AttributionBuilder(mapboxMap).build(); 45 | 46 | boolean isActivityFinishing = false; 47 | if (context instanceof Activity) { 48 | isActivityFinishing = ((Activity) context).isFinishing(); 49 | } 50 | 51 | // check is hosting activity isn't finishing 52 | // https://github.com/mapbox/mapbox-gl-native/issues/11238 53 | if (!isActivityFinishing) { 54 | showAttributionDialog(getAttributionTitles()); 55 | } 56 | } 57 | 58 | // Called when someone selects an attribution or telemetry settings from the dialog 59 | @Override 60 | public void onClick(DialogInterface dialog, int which) { 61 | showMapFeedbackWebPage(which); 62 | } 63 | 64 | private void showMapFeedbackWebPage(int which) { 65 | Attribution[] attributions = attributionSet.toArray(new Attribution[attributionSet.size()]); 66 | String url = attributions[which].getUrl(); 67 | showWebPage(url); 68 | } 69 | 70 | private void showWebPage(@NonNull String url) { 71 | try { 72 | Intent intent = new Intent(Intent.ACTION_VIEW); 73 | intent.setData(Uri.parse(url)); 74 | context.startActivity(intent); 75 | } catch (ActivityNotFoundException exception) { 76 | // explicitly handling if the device hasn't have a web browser installed. #8899 77 | Toast.makeText(context, R.string.error_no_browser_installed, Toast.LENGTH_LONG).show(); 78 | } 79 | } 80 | 81 | 82 | protected void showAttributionDialog(String[] attributionTitles) { 83 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 84 | builder.setTitle(R.string.app_name); 85 | builder.setAdapter(new ArrayAdapter<>(context, R.layout.attribution_list_item, attributionTitles), this); 86 | builder.show(); 87 | } 88 | 89 | private String[] getAttributionTitles() { 90 | List titles = new ArrayList<>(); 91 | for (Attribution attribution : attributionSet) { 92 | titles.add(attribution.getTitle()); 93 | } 94 | return titles.toArray(new String[titles.size()]); 95 | } 96 | 97 | 98 | private static class AttributionBuilder { 99 | 100 | private final MapboxMap mapboxMap; 101 | 102 | AttributionBuilder(MapboxMap mapboxMap) { 103 | this.mapboxMap = mapboxMap; 104 | } 105 | 106 | private Set build() { 107 | List attributions = new ArrayList<>(); 108 | attributions.add("© GraphHopper API"); 109 | for (Source source : mapboxMap.getSources()) { 110 | attributions.add(source.getAttribution()); 111 | } 112 | 113 | return new AttributionParser.Options() 114 | .withCopyrightSign(true) 115 | // TODO when using Mapbox as Tilesource we should keep this, should we automatically remove it otherwise? 116 | .withImproveMap(true) 117 | .withAttributionData(attributions.toArray(new String[attributions.size()])) 118 | .build().getAttributions(); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/GeocodingInputDialog.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.app.Dialog; 6 | import android.app.DialogFragment; 7 | import android.content.DialogInterface; 8 | import android.os.Bundle; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.widget.EditText; 12 | 13 | public class GeocodingInputDialog extends DialogFragment { 14 | 15 | private String geocodingInput = ""; 16 | 17 | public void setGeocodingInput(String geocodingInput) { 18 | this.geocodingInput = geocodingInput; 19 | } 20 | 21 | public interface NoticeDialogListener { 22 | public void onDialogPositiveClick(DialogFragment dialog); 23 | } 24 | 25 | NoticeDialogListener mListener; 26 | 27 | @Override 28 | public void onAttach(Activity activity) { 29 | super.onAttach(activity); 30 | // Verify that the host activity implements the callback interface 31 | try { 32 | // Instantiate the NoticeDialogListener so we can send events to the host 33 | mListener = (NoticeDialogListener) activity; 34 | } catch (ClassCastException e) { 35 | // The activity doesn't implement the interface, throw exception 36 | throw new ClassCastException(activity.toString() 37 | + " must implement NoticeDialogListener"); 38 | } 39 | } 40 | 41 | @Override 42 | public Dialog onCreateDialog(Bundle savedInstanceState) { 43 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 44 | // Get the layout inflater 45 | LayoutInflater inflater = getActivity().getLayoutInflater(); 46 | 47 | View modifiedView = inflater.inflate(R.layout.geocoding_input, null); 48 | if (!geocodingInput.isEmpty()) { 49 | EditText jobIdEditText = (EditText) modifiedView.findViewById(R.id.geocoding_input_id); 50 | jobIdEditText.setText(geocodingInput); 51 | } 52 | 53 | // Inflate and set the layout for the dialog 54 | // Pass null as the parent view because its going in the dialog layout 55 | builder.setView(modifiedView) 56 | // Add action buttons 57 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 58 | @Override 59 | public void onClick(DialogInterface dialog, int id) { 60 | mListener.onDialogPositiveClick(GeocodingInputDialog.this); 61 | } 62 | }) 63 | .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 64 | public void onClick(DialogInterface dialog, int id) { 65 | GeocodingInputDialog.this.getDialog().cancel(); 66 | } 67 | }); 68 | return builder.create(); 69 | } 70 | 71 | } 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/NavigationLauncherActivity.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.AlertDialog; 5 | import android.app.DialogFragment; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.content.SharedPreferences; 9 | import android.location.Location; 10 | import android.net.Uri; 11 | import android.os.Bundle; 12 | import android.preference.PreferenceManager; 13 | import android.support.annotation.NonNull; 14 | import android.support.annotation.Nullable; 15 | import android.support.design.widget.Snackbar; 16 | import android.support.v7.app.AppCompatActivity; 17 | import android.text.Html; 18 | import android.view.Menu; 19 | import android.view.MenuItem; 20 | import android.view.View; 21 | import android.widget.EditText; 22 | import android.widget.ProgressBar; 23 | import android.widget.Toast; 24 | 25 | import com.graphhopper.directions.api.client.model.GeocodingLocation; 26 | import com.graphhopper.directions.api.client.model.GeocodingPoint; 27 | import com.mapbox.android.core.permissions.PermissionsListener; 28 | import com.mapbox.android.core.permissions.PermissionsManager; 29 | import com.mapbox.api.directions.v5.DirectionsCriteria; 30 | import com.mapbox.api.directions.v5.models.DirectionsResponse; 31 | import com.mapbox.api.directions.v5.models.DirectionsRoute; 32 | import com.mapbox.core.constants.Constants; 33 | import com.mapbox.geojson.LineString; 34 | import com.mapbox.geojson.Point; 35 | import com.mapbox.mapboxsdk.Mapbox; 36 | import com.mapbox.mapboxsdk.annotations.IconFactory; 37 | import com.mapbox.mapboxsdk.annotations.Marker; 38 | import com.mapbox.mapboxsdk.annotations.MarkerOptions; 39 | import com.mapbox.mapboxsdk.camera.CameraPosition; 40 | import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; 41 | import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException; 42 | import com.mapbox.mapboxsdk.geometry.LatLng; 43 | import com.mapbox.mapboxsdk.geometry.LatLngBounds; 44 | import com.mapbox.mapboxsdk.maps.MapView; 45 | import com.mapbox.mapboxsdk.maps.MapboxMap; 46 | import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; 47 | import com.mapbox.mapboxsdk.maps.Telemetry; 48 | import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; 49 | import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode; 50 | import com.mapbox.services.android.navigation.ui.v5.NavigationLauncher; 51 | import com.mapbox.services.android.navigation.ui.v5.NavigationLauncherOptions; 52 | import com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute; 53 | import com.mapbox.services.android.navigation.ui.v5.route.OnRouteSelectionChangeListener; 54 | import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute; 55 | import com.mapbox.services.android.navigation.v5.utils.LocaleUtils; 56 | 57 | import java.util.ArrayList; 58 | import java.util.List; 59 | import java.util.Locale; 60 | 61 | import butterknife.BindView; 62 | import butterknife.ButterKnife; 63 | import retrofit2.Call; 64 | import retrofit2.Response; 65 | 66 | public class NavigationLauncherActivity extends AppCompatActivity implements OnMapReadyCallback, 67 | MapboxMap.OnMapLongClickListener, OnRouteSelectionChangeListener, 68 | SolutionInputDialog.NoticeDialogListener, FetchSolutionTaskCallbackInterface, 69 | FetchGeocodingTaskCallbackInterface, GeocodingInputDialog.NoticeDialogListener, 70 | PermissionsListener { 71 | 72 | private static final int CAMERA_ANIMATION_DURATION = 1000; 73 | private static final int DEFAULT_CAMERA_ZOOM = 16; 74 | private static final int CHANGE_SETTING_REQUEST_CODE = 1; 75 | // If you change the first start dialog (help message), increase this number, all users will be shown the message again 76 | private static final int FIRST_START_DIALOG_VERSION = 1; 77 | private static final int FIRST_NAVIGATION_DIALOG_VERSION = 1; 78 | 79 | private LocationLayerPlugin locationLayer; 80 | private NavigationMapRoute mapRoute; 81 | private MapboxMap mapboxMap; 82 | private PermissionsManager permissionsManager; 83 | 84 | @BindView(R.id.mapView) 85 | MapView mapView; 86 | @BindView(R.id.loading) 87 | ProgressBar loading; 88 | 89 | private Marker currentMarker; 90 | private List markers; 91 | private List waypoints = new ArrayList<>(); 92 | private DirectionsRoute route; 93 | private LocaleUtils localeUtils; 94 | 95 | private final int[] padding = new int[]{50, 50, 50, 50}; 96 | 97 | private String currentJobId = ""; 98 | private String currentVehicleId = ""; 99 | private String currentGeocodingInput = ""; 100 | 101 | @Override 102 | protected void onCreate(@Nullable Bundle savedInstanceState) { 103 | super.onCreate(savedInstanceState); 104 | setContentView(R.layout.activity_navigation_launcher); 105 | Mapbox.getInstance(this.getApplicationContext(), getString(R.string.mapbox_access_token)); 106 | Telemetry.disableOnUserRequest(); 107 | ButterKnife.bind(this); 108 | mapView.setStyleUrl(getString(R.string.map_view_styleUrl)); 109 | mapView.onCreate(savedInstanceState); 110 | mapView.getMapAsync(this); 111 | localeUtils = new LocaleUtils(); 112 | if (getSupportActionBar() != null) { 113 | getSupportActionBar().setTitle(""); 114 | } 115 | showFirstStartIfNecessary(); 116 | } 117 | 118 | @Override 119 | public boolean onCreateOptionsMenu(Menu menu) { 120 | // Inflate the menu; this adds items to the action bar if it is present. 121 | getMenuInflater().inflate(R.menu.navigation_view_activity_menu, menu); 122 | return true; 123 | } 124 | 125 | @Override 126 | public boolean onOptionsItemSelected(MenuItem item) { 127 | switch (item.getItemId()) { 128 | case R.id.settings: 129 | showSettings(); 130 | return true; 131 | case R.id.navigate_btn: 132 | launchNavigationWithRoute(); 133 | return true; 134 | case R.id.reset_route_btn: 135 | clearRoute(); 136 | return true; 137 | case R.id.fetch_solution_btn: 138 | showSolutionInputDialog(); 139 | return true; 140 | case R.id.geocoding_search_btn: 141 | showGeocodingInputDialog(); 142 | return true; 143 | case R.id.help: 144 | showHelpDialog(); 145 | return true; 146 | default: 147 | return super.onOptionsItemSelected(item); 148 | } 149 | } 150 | 151 | private void showSettings() { 152 | startActivityForResult(new Intent(this, NavigationViewSettingsActivity.class), CHANGE_SETTING_REQUEST_CODE); 153 | } 154 | 155 | @Override 156 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 157 | super.onActivityResult(requestCode, resultCode, data); 158 | if (requestCode == CHANGE_SETTING_REQUEST_CODE && resultCode == RESULT_OK) { 159 | boolean shouldRefetch = data.getBooleanExtra(NavigationViewSettingsActivity.UNIT_TYPE_CHANGED, false) 160 | || data.getBooleanExtra(NavigationViewSettingsActivity.LANGUAGE_CHANGED, false) 161 | || data.getBooleanExtra(NavigationViewSettingsActivity.PROFILE_CHANGED, false); 162 | if (waypoints.size() > 0 && shouldRefetch) { 163 | fetchRoute(); 164 | } 165 | } 166 | } 167 | 168 | @SuppressWarnings({"MissingPermission"}) 169 | @Override 170 | protected void onStart() { 171 | super.onStart(); 172 | mapView.onStart(); 173 | if (locationLayer != null) { 174 | locationLayer.onStart(); 175 | } 176 | } 177 | 178 | @SuppressWarnings({"MissingPermission"}) 179 | @Override 180 | public void onResume() { 181 | super.onResume(); 182 | mapView.onResume(); 183 | handleIntent(getIntent()); 184 | } 185 | 186 | @Override 187 | protected void onNewIntent(Intent intent) { 188 | super.onNewIntent(intent); 189 | // getIntent() should always return the most recent 190 | setIntent(intent); 191 | } 192 | 193 | private void handleIntent(Intent intent) { 194 | if (intent != null) { 195 | Uri data = intent.getData(); 196 | if (data != null && "graphhopper.com".equals(data.getHost())) { 197 | if (data.getPath() != null) { 198 | if (this.mapboxMap == null) { 199 | //this happens when onResume is called at the initial start and we will call this method again in onMapReady 200 | return; 201 | } 202 | if (data.getPath().contains("maps")) { 203 | clearRoute(); 204 | //Open Map Url 205 | setRouteProfileToSharedPreferences(data.getQueryParameter("vehicle")); 206 | 207 | List points = data.getQueryParameters("point"); 208 | for (String point : points) { 209 | String[] pointArr = point.split(","); 210 | addPointToRoute(Double.parseDouble(pointArr[0]), Double.parseDouble(pointArr[1])); 211 | } 212 | 213 | setStartFromLocationToSharedPreferences(false); 214 | updateRouteAfterWaypointChange(); 215 | } 216 | // https://graphhopper.com/api/1/vrp/solution/e7fb8a9b-e441-4ec2-a487-20788e591bb3?vehicle_id=1&key=[KEY] 217 | if (data.getPath().contains("api/1/vrp/solution")) { 218 | clearRoute(); 219 | //Open Vrp Url 220 | List pathSegments = data.getPathSegments(); 221 | fetchVrpSolution(pathSegments.get(pathSegments.size() - 1), data.getQueryParameter("vehicle_id")); 222 | } 223 | } 224 | 225 | } 226 | } 227 | } 228 | 229 | private void showFirstStartIfNecessary() { 230 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 231 | if (sharedPreferences.getInt(getString(R.string.first_start_dialog_key), -1) < FIRST_START_DIALOG_VERSION) { 232 | showHelpDialog(); 233 | } 234 | } 235 | 236 | private void showHelpDialog() { 237 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 238 | builder.setTitle(R.string.help_message_title); 239 | builder.setMessage(Html.fromHtml("1. Please note, this is a demo and not a full featured application. The purpose of this application is to provide an easy starting point for developers to create a navigation application with GraphHopper
2. You should enable your GPS/location
3.You can either search for a location using the magnifier icon or by long pressing on the map
4. Start the navigation by tapping the arrow button

This project is 100% open source, contributions are welcome.

Please drive carefully and always abide local law and signs. Roads might be impassible due to construction projects, traffic, weather, or other events.")); 240 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 241 | builder.setPositiveButton(R.string.agree, new DialogInterface.OnClickListener() { 242 | public void onClick(DialogInterface dialog, int id) { 243 | SharedPreferences.Editor editor = sharedPreferences.edit(); 244 | editor.putInt(getString(R.string.first_start_dialog_key), FIRST_START_DIALOG_VERSION); 245 | editor.apply(); 246 | } 247 | }); 248 | builder.setNeutralButton(R.string.github, new DialogInterface.OnClickListener() { 249 | public void onClick(DialogInterface dialog, int id) { 250 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/graphhopper/graphhopper-navigation-example"))); 251 | } 252 | }); 253 | 254 | AlertDialog dialog = builder.create(); 255 | dialog.show(); 256 | } 257 | 258 | private void showNavigationDialog() { 259 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 260 | builder.setTitle(R.string.legal_title); 261 | builder.setMessage(Html.fromHtml("You are required to comply with applicable laws.
When using mapping data, directions or other data from GraphHopper / OpenStreetMap, it is possible that the results differ from the actual situation. You should therefore act at your own discretion. Use of GraphHopper / OpenStreetMap is at your own risk. You are responsible for your own behavior and consequences at all times.")); 262 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 263 | builder.setPositiveButton(R.string.agree, new DialogInterface.OnClickListener() { 264 | public void onClick(DialogInterface dialog, int id) { 265 | SharedPreferences.Editor editor = sharedPreferences.edit(); 266 | editor.putInt(getString(R.string.first_navigation_dialog_key), FIRST_NAVIGATION_DIALOG_VERSION); 267 | editor.apply(); 268 | launchNavigationWithRoute(); 269 | } 270 | }); 271 | 272 | AlertDialog dialog = builder.create(); 273 | dialog.show(); 274 | } 275 | 276 | private void fetchVrpSolution(String jobId, String vehicleId) { 277 | currentJobId = jobId; 278 | currentVehicleId = vehicleId; 279 | 280 | showLoading(); 281 | new FetchSolutionTask(this, getString(R.string.gh_key)).execute(new FetchSolutionConfig(currentJobId, currentVehicleId)); 282 | } 283 | 284 | @Override 285 | public void onPause() { 286 | super.onPause(); 287 | mapView.onPause(); 288 | } 289 | 290 | @Override 291 | public void onLowMemory() { 292 | super.onLowMemory(); 293 | mapView.onLowMemory(); 294 | } 295 | 296 | @Override 297 | protected void onStop() { 298 | super.onStop(); 299 | mapView.onStop(); 300 | if (locationLayer != null) { 301 | locationLayer.onStop(); 302 | } 303 | } 304 | 305 | @Override 306 | protected void onDestroy() { 307 | super.onDestroy(); 308 | mapView.onDestroy(); 309 | } 310 | 311 | @Override 312 | protected void onSaveInstanceState(Bundle outState) { 313 | super.onSaveInstanceState(outState); 314 | mapView.onSaveInstanceState(outState); 315 | } 316 | 317 | private void clearRoute() { 318 | waypoints.clear(); 319 | mapRoute.removeRoute(); 320 | route = null; 321 | if (currentMarker != null) { 322 | mapboxMap.removeMarker(currentMarker); 323 | currentMarker = null; 324 | } 325 | } 326 | 327 | private void clearGeocodingResults() { 328 | if (markers != null) { 329 | for (Marker marker : markers) { 330 | this.mapboxMap.removeMarker(marker); 331 | } 332 | markers.clear(); 333 | } 334 | } 335 | 336 | @Override 337 | public void onMapReady(MapboxMap mapboxMap) { 338 | this.mapboxMap = mapboxMap; 339 | this.mapboxMap.getUiSettings().setAttributionDialogManager(new GHAttributionDialogManager(this.mapView.getContext(), this.mapboxMap)); 340 | this.mapboxMap.addOnMapLongClickListener(this); 341 | initMapRoute(); 342 | 343 | this.mapboxMap.setOnInfoWindowClickListener(new MapboxMap.OnInfoWindowClickListener() { 344 | @Override 345 | public boolean onInfoWindowClick(@NonNull Marker marker) { 346 | for (Marker geocodingMarker : markers) { 347 | if (geocodingMarker.getId() == marker.getId()) { 348 | LatLng position = geocodingMarker.getPosition(); 349 | addPointToRoute(position.getLatitude(), position.getLongitude()); 350 | updateRouteAfterWaypointChange(); 351 | marker.hideInfoWindow(); 352 | return true; 353 | } 354 | } 355 | return true; 356 | } 357 | }); 358 | 359 | // Check for location permission 360 | permissionsManager = new PermissionsManager(this); 361 | if (!PermissionsManager.areLocationPermissionsGranted(this)) { 362 | permissionsManager.requestLocationPermissions(this); 363 | } else { 364 | initLocationLayer(); 365 | } 366 | 367 | handleIntent(getIntent()); 368 | } 369 | 370 | @Override 371 | public void onMapLongClick(@NonNull LatLng point) { 372 | addPointToRoute(point.getLatitude(), point.getLongitude()); 373 | updateRouteAfterWaypointChange(); 374 | } 375 | 376 | private void addPointToRoute(double lat, double lng) { 377 | waypoints.add(Point.fromLngLat(lng, lat)); 378 | } 379 | 380 | @Override 381 | public void onNewPrimaryRouteSelected(DirectionsRoute directionsRoute) { 382 | route = directionsRoute; 383 | } 384 | 385 | @SuppressWarnings({"MissingPermission"}) 386 | private void initLocationLayer() { 387 | locationLayer = new LocationLayerPlugin(mapView, mapboxMap); 388 | locationLayer.setRenderMode(RenderMode.COMPASS); 389 | Location lastKnownLocation = getLastKnownLocation(); 390 | if (lastKnownLocation != null) { 391 | // TODO we could think about zoom to the user location later on as well 392 | animateCamera(new LatLng(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); 393 | } 394 | } 395 | 396 | private void initMapRoute() { 397 | mapRoute = new NavigationMapRoute(mapView, mapboxMap); 398 | mapRoute.setOnRouteSelectionChangeListener(this); 399 | } 400 | 401 | private void fetchRoute() { 402 | NavigationRoute.Builder builder = NavigationRoute.builder(this) 403 | .accessToken("pk." + getString(R.string.gh_key)) 404 | .baseUrl(getString(R.string.base_url)) 405 | .user("gh") 406 | .alternatives(true); 407 | 408 | boolean startFromLocation = getStartFromLocationFromSharedPreferences(); 409 | 410 | if (!startFromLocation && waypoints.size() < 2 || startFromLocation && waypoints.size() < 1) { 411 | onError(R.string.error_not_enough_waypoints); 412 | return; 413 | } 414 | 415 | if (startFromLocation) { 416 | Location lastKnownLocation = getLastKnownLocation(); 417 | if (lastKnownLocation == null) { 418 | onError(R.string.error_location_not_found); 419 | return; 420 | } else { 421 | Point location = Point.fromLngLat(lastKnownLocation.getLongitude(), lastKnownLocation.getLatitude()); 422 | if (lastKnownLocation.hasBearing()) 423 | // 90 seems to be the default tolerance of the SDK 424 | builder.origin(location, (double) lastKnownLocation.getBearing(), 90.0); 425 | else 426 | builder.origin(location); 427 | } 428 | } 429 | 430 | for (int i = 0; i < waypoints.size(); i++) { 431 | Point p = waypoints.get(i); 432 | if (i == 0 && !startFromLocation) { 433 | builder.origin(p); 434 | } else if (i < waypoints.size() - 1) { 435 | builder.addWaypoint(p); 436 | } else { 437 | builder.destination(p); 438 | } 439 | } 440 | 441 | showLoading(); 442 | 443 | setFieldsFromSharedPreferences(builder); 444 | builder.build().getRoute(new SimplifiedCallback() { 445 | @Override 446 | public void onResponse(Call call, Response response) { 447 | if (validRouteResponse(response)) { 448 | route = response.body().routes().get(0); 449 | mapRoute.addRoutes(response.body().routes()); 450 | boundCameraToRoute(); 451 | } else { 452 | Snackbar.make(mapView, R.string.error_calculating_route, Snackbar.LENGTH_LONG).show(); 453 | } 454 | hideLoading(); 455 | } 456 | 457 | @Override 458 | public void onFailure(Call call, Throwable throwable) { 459 | super.onFailure(call, throwable); 460 | Snackbar.make(mapView, R.string.error_calculating_route, Snackbar.LENGTH_LONG).show(); 461 | hideLoading(); 462 | } 463 | }); 464 | } 465 | 466 | @SuppressLint("MissingPermission") 467 | private Location getLastKnownLocation() { 468 | if (locationLayer != null) { 469 | return locationLayer.getLastKnownLocation(); 470 | } 471 | return null; 472 | } 473 | 474 | private void updateRouteAfterWaypointChange() { 475 | if (this.waypoints.isEmpty()) { 476 | hideLoading(); 477 | } else { 478 | Point lastPoint = this.waypoints.get(this.waypoints.size() - 1); 479 | LatLng latLng = new LatLng(lastPoint.latitude(), lastPoint.longitude()); 480 | setCurrentMarkerPosition(latLng); 481 | if (this.waypoints.size() > 0) { 482 | fetchRoute(); 483 | } else { 484 | hideLoading(); 485 | } 486 | } 487 | } 488 | 489 | private void setFieldsFromSharedPreferences(NavigationRoute.Builder builder) { 490 | builder 491 | .language(getLanguageFromSharedPreferences()) 492 | .voiceUnits(getUnitTypeFromSharedPreferences()) 493 | .profile(getRouteProfileFromSharedPreferences()); 494 | } 495 | 496 | private String getUnitTypeFromSharedPreferences() { 497 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 498 | String defaultUnitType = getString(R.string.default_unit_type); 499 | String unitType = sharedPreferences.getString(getString(R.string.unit_type_key), defaultUnitType); 500 | if (unitType.equals(defaultUnitType)) { 501 | unitType = localeUtils.getUnitTypeForDeviceLocale(this); 502 | } 503 | 504 | return unitType; 505 | } 506 | 507 | private Locale getLanguageFromSharedPreferences() { 508 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 509 | String defaultLanguage = getString(R.string.default_locale); 510 | String language = sharedPreferences.getString(getString(R.string.language_key), defaultLanguage); 511 | if (language.equals(defaultLanguage)) { 512 | return localeUtils.inferDeviceLocale(this); 513 | } else { 514 | return new Locale(language); 515 | } 516 | } 517 | 518 | private boolean getShouldSimulateRouteFromSharedPreferences() { 519 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 520 | return sharedPreferences.getBoolean(getString(R.string.simulate_route_key), false); 521 | } 522 | 523 | private boolean getStartFromLocationFromSharedPreferences() { 524 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 525 | return sharedPreferences.getBoolean(getString(R.string.start_from_location_key), true); 526 | } 527 | 528 | private void setStartFromLocationToSharedPreferences(boolean setStartFromLocation) { 529 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 530 | SharedPreferences.Editor editor = sharedPreferences.edit(); 531 | editor.putBoolean(getString(R.string.start_from_location_key), setStartFromLocation); 532 | editor.apply(); 533 | } 534 | 535 | private void setRouteProfileToSharedPreferences(String ghVehicle) { 536 | if (ghVehicle == null) 537 | return; 538 | 539 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 540 | SharedPreferences.Editor editor = sharedPreferences.edit(); 541 | String routeProfile; 542 | switch (ghVehicle) { 543 | case "foot": 544 | case "hike": 545 | routeProfile = "walking"; 546 | break; 547 | case "bike": 548 | case "mtb": 549 | case "racingbike": 550 | routeProfile = "cycling"; 551 | break; 552 | default: 553 | routeProfile = "driving"; 554 | } 555 | editor.putString(getString(R.string.route_profile_key), routeProfile); 556 | editor.apply(); 557 | } 558 | 559 | private String getRouteProfileFromSharedPreferences() { 560 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 561 | return sharedPreferences.getString( 562 | getString(R.string.route_profile_key), DirectionsCriteria.PROFILE_DRIVING 563 | ); 564 | } 565 | 566 | private void launchNavigationWithRoute() { 567 | if (route == null) { 568 | Snackbar.make(mapView, R.string.error_route_not_available, Snackbar.LENGTH_SHORT).show(); 569 | return; 570 | } 571 | 572 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 573 | if (sharedPreferences.getInt(getString(R.string.first_navigation_dialog_key), -1) < FIRST_NAVIGATION_DIALOG_VERSION) { 574 | showNavigationDialog(); 575 | return; 576 | } 577 | 578 | Location lastKnownLocation = getLastKnownLocation(); 579 | if (lastKnownLocation != null && waypoints.size() > 1) { 580 | float[] distance = new float[1]; 581 | Location.distanceBetween(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude(), waypoints.get(0).latitude(), waypoints.get(0).longitude(), distance); 582 | 583 | //Ask the user if he would like to recalculate the route from his current positions 584 | if (distance[0] > 100) { 585 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 586 | builder.setTitle(R.string.error_too_far_from_start_title); 587 | builder.setMessage(R.string.error_too_far_from_start_message); 588 | builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 589 | public void onClick(DialogInterface dialog, int id) { 590 | waypoints.set(0, Point.fromLngLat(lastKnownLocation.getLongitude(), lastKnownLocation.getLatitude())); 591 | fetchRoute(); 592 | } 593 | }); 594 | builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 595 | public void onClick(DialogInterface dialog, int id) { 596 | _launchNavigationWithRoute(); 597 | } 598 | }); 599 | 600 | AlertDialog dialog = builder.create(); 601 | dialog.show(); 602 | } else { 603 | _launchNavigationWithRoute(); 604 | } 605 | } else { 606 | _launchNavigationWithRoute(); 607 | } 608 | 609 | } 610 | 611 | private void _launchNavigationWithRoute() { 612 | NavigationLauncherOptions.Builder optionsBuilder = NavigationLauncherOptions.builder() 613 | .shouldSimulateRoute(getShouldSimulateRouteFromSharedPreferences()) 614 | .directionsProfile(getRouteProfileFromSharedPreferences()) 615 | .waynameChipEnabled(false); 616 | 617 | optionsBuilder.directionsRoute(route); 618 | 619 | NavigationLauncher.startNavigation(this, optionsBuilder.build()); 620 | } 621 | 622 | private boolean validRouteResponse(Response response) { 623 | return response.body() != null && !response.body().routes().isEmpty(); 624 | } 625 | 626 | private void hideLoading() { 627 | if (loading.getVisibility() == View.VISIBLE) { 628 | loading.setVisibility(View.INVISIBLE); 629 | } 630 | } 631 | 632 | private void showLoading() { 633 | if (loading.getVisibility() == View.INVISIBLE) { 634 | loading.setVisibility(View.VISIBLE); 635 | } 636 | } 637 | 638 | private void boundCameraToRoute() { 639 | if (route != null) { 640 | List routeCoords = LineString.fromPolyline(route.geometry(), 641 | Constants.PRECISION_6).coordinates(); 642 | List bboxPoints = new ArrayList<>(); 643 | for (Point point : routeCoords) { 644 | bboxPoints.add(new LatLng(point.latitude(), point.longitude())); 645 | } 646 | if (bboxPoints.size() > 1) { 647 | try { 648 | LatLngBounds bounds = new LatLngBounds.Builder().includes(bboxPoints).build(); 649 | // left, top, right, bottom 650 | animateCameraBbox(bounds, CAMERA_ANIMATION_DURATION, padding); 651 | } catch (InvalidLatLngBoundsException exception) { 652 | Toast.makeText(this, R.string.error_valid_route_not_found, Toast.LENGTH_SHORT).show(); 653 | } 654 | } 655 | } 656 | } 657 | 658 | private void animateCameraBbox(LatLngBounds bounds, int animationTime, int[] padding) { 659 | CameraPosition position = mapboxMap.getCameraForLatLngBounds(bounds, padding); 660 | mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), animationTime); 661 | } 662 | 663 | private void animateCamera(LatLng point) { 664 | mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(point, DEFAULT_CAMERA_ZOOM), CAMERA_ANIMATION_DURATION); 665 | } 666 | 667 | private void setCurrentMarkerPosition(LatLng position) { 668 | if (position != null) { 669 | if (currentMarker == null) { 670 | MarkerOptions markerOptions = new MarkerOptions() 671 | .position(position); 672 | currentMarker = mapboxMap.addMarker(markerOptions); 673 | } else { 674 | currentMarker.setPosition(position); 675 | } 676 | } 677 | } 678 | 679 | private void updateWaypoints(List points) { 680 | if (points.size() > 24) { 681 | onError(R.string.error_too_many_waypoints); 682 | return; 683 | } 684 | clearRoute(); 685 | this.waypoints = points; 686 | updateRouteAfterWaypointChange(); 687 | } 688 | 689 | private void showSolutionInputDialog() { 690 | // Create an instance of the dialog fragment and show it 691 | SolutionInputDialog dialog = new SolutionInputDialog(); 692 | dialog.setJobId(currentJobId); 693 | dialog.setVehicleId(currentVehicleId); 694 | dialog.show(getFragmentManager(), "gh-example"); 695 | } 696 | 697 | private void showGeocodingInputDialog() { 698 | // Create an instance of the dialog fragment and show it 699 | GeocodingInputDialog dialog = new GeocodingInputDialog(); 700 | dialog.setGeocodingInput(currentGeocodingInput); 701 | dialog.show(getFragmentManager(), "gh-example"); 702 | } 703 | 704 | @Override 705 | public void onDialogPositiveClick(DialogFragment dialog) { 706 | EditText jobId = dialog.getDialog().findViewById(R.id.job_id); 707 | // Check if it's a solution fetch 708 | if (jobId != null) { 709 | EditText vehicleId = dialog.getDialog().findViewById(R.id.vehicle_id); 710 | 711 | fetchVrpSolution(jobId.getText().toString(), vehicleId.getText().toString()); 712 | } 713 | // Check if it's a geocoding search 714 | EditText search = dialog.getDialog().findViewById(R.id.geocoding_input_id); 715 | if (search != null) { 716 | currentGeocodingInput = search.getText().toString(); 717 | 718 | showLoading(); 719 | String point = null; 720 | LatLng pointLatLng = this.mapboxMap.getCameraPosition().target; 721 | if (pointLatLng != null) 722 | point = pointLatLng.getLatitude() + "," + pointLatLng.getLongitude(); 723 | new FetchGeocodingTask(this, getString(R.string.gh_key)).execute(new FetchGeocodingConfig(currentGeocodingInput, getLanguageFromSharedPreferences().getLanguage(), 5, false, point, "default")); 724 | } 725 | 726 | } 727 | 728 | @Override 729 | public void onError(int message) { 730 | Snackbar.make(mapView, message, Snackbar.LENGTH_LONG).show(); 731 | } 732 | 733 | @Override 734 | public void onPostExecuteGeocodingSearch(List locations) { 735 | clearGeocodingResults(); 736 | markers = new ArrayList<>(locations.size()); 737 | 738 | if (locations.isEmpty()) { 739 | onError(R.string.error_geocoding_no_location); 740 | return; 741 | } 742 | 743 | List bounds = new ArrayList<>(); 744 | Location lastKnownLocation = getLastKnownLocation(); 745 | if (lastKnownLocation != null) 746 | bounds.add(new LatLng(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); 747 | 748 | for (GeocodingLocation location : locations) { 749 | GeocodingPoint point = location.getPoint(); 750 | MarkerOptions markerOptions = new MarkerOptions(); 751 | LatLng latLng = new LatLng(point.getLat(), point.getLng()); 752 | markerOptions.position(latLng); 753 | bounds.add(latLng); 754 | markerOptions.title(location.getName()); 755 | String snippet = ""; 756 | if (location.getStreet() != null) { 757 | snippet += location.getStreet(); 758 | if (location.getHousenumber() != null) 759 | snippet += " " + location.getHousenumber(); 760 | snippet += "\n"; 761 | } 762 | if (location.getCity() != null) { 763 | if (location.getPostcode() != null) 764 | snippet += location.getPostcode() + " "; 765 | snippet += location.getCity() + "\n"; 766 | } 767 | if (location.getCountry() != null) 768 | snippet += location.getCountry() + "\n"; 769 | if (location.getOsmId() != null) { 770 | snippet += "OSM-Id: " + location.getOsmId() + "\n"; 771 | if (location.getOsmKey() != null) 772 | snippet += "OSM-Key: " + location.getOsmKey() + "\n"; 773 | if (location.getOsmType() != null) 774 | snippet += "OSM-Type: " + location.getOsmType() + "\n"; 775 | } 776 | snippet += "\n\n Tap on info window\n to add point to route"; 777 | if (!snippet.isEmpty()) 778 | markerOptions.snippet(snippet); 779 | markerOptions.icon(IconFactory.getInstance(this.getApplicationContext()).fromResource(R.drawable.ic_map_marker)); 780 | markers.add(mapboxMap.addMarker(markerOptions)); 781 | } 782 | 783 | // For bounds we need at least 2 entries 784 | if (bounds.size() >= 2) { 785 | LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder(); 786 | boundsBuilder.includes(bounds); 787 | animateCameraBbox(boundsBuilder.build(), CAMERA_ANIMATION_DURATION, padding); 788 | } else if (bounds.size() == 1) { 789 | // If there is only 1 result (=>current location unknown), we just zoom to that result 790 | animateCamera(bounds.get(0)); 791 | } 792 | hideLoading(); 793 | } 794 | 795 | @Override 796 | public void onPostExecute(List points) { 797 | if (getStartFromLocationFromSharedPreferences() && !points.isEmpty()) { 798 | // Remove the first point if we want to start from the current location 799 | points.remove(0); 800 | } 801 | updateWaypoints(points); 802 | } 803 | 804 | @Override 805 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 806 | @NonNull int[] grantResults) { 807 | permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults); 808 | } 809 | 810 | @Override 811 | public void onExplanationNeeded(List permissionsToExplain) { 812 | Toast.makeText(this, "This app needs location permissions to work properly.", 813 | Toast.LENGTH_LONG).show(); 814 | } 815 | 816 | @Override 817 | public void onPermissionResult(boolean granted) { 818 | if (granted) { 819 | initLocationLayer(); 820 | } else { 821 | Toast.makeText(this, "You didn't grant location permissions.", 822 | Toast.LENGTH_LONG).show(); 823 | } 824 | } 825 | } 826 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/NavigationViewSettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | 4 | import android.content.Intent; 5 | import android.content.SharedPreferences; 6 | import android.os.Bundle; 7 | import android.preference.PreferenceActivity; 8 | import android.preference.PreferenceFragment; 9 | import android.preference.PreferenceManager; 10 | 11 | public class NavigationViewSettingsActivity extends PreferenceActivity { 12 | private SharedPreferences.OnSharedPreferenceChangeListener listener; 13 | static final String UNIT_TYPE_CHANGED = "unit_type_changed"; 14 | static final String LANGUAGE_CHANGED = "language_changed"; 15 | static final String PROFILE_CHANGED = "profile_changed"; 16 | 17 | @Override 18 | public void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | listener = (sharedPreferences, key) -> { 21 | Intent resultIntent = new Intent(); 22 | resultIntent.putExtra(UNIT_TYPE_CHANGED, key.equals(getString(R.string.unit_type_key))); 23 | resultIntent.putExtra(LANGUAGE_CHANGED, key.equals(getString(R.string.language_key))); 24 | resultIntent.putExtra(PROFILE_CHANGED, key.equals(getString(R.string.route_profile_key))); 25 | setResult(RESULT_OK, resultIntent); 26 | }; 27 | PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(listener); 28 | getFragmentManager().beginTransaction().replace( 29 | android.R.id.content, new NavigationViewPreferenceFragment()).commit(); 30 | } 31 | 32 | @Override 33 | protected boolean isValidFragment(String fragmentName) { 34 | return super.isValidFragment(fragmentName); 35 | } 36 | 37 | public static class NavigationViewPreferenceFragment extends PreferenceFragment { 38 | @Override 39 | public void onCreate(final Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | addPreferencesFromResource(R.xml.fragment_navigation_view_preferences); 42 | PreferenceManager.setDefaultValues(getActivity(), R.xml.fragment_navigation_view_preferences, false); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/SimplifiedCallback.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import com.mapbox.api.directions.v5.models.DirectionsResponse; 4 | 5 | import retrofit2.Call; 6 | import retrofit2.Callback; 7 | import timber.log.Timber; 8 | 9 | /** 10 | * Helper class to reduce redundant logging code when no other action is taken in onFailure 11 | */ 12 | public abstract class SimplifiedCallback implements Callback { 13 | @Override 14 | public void onFailure(Call call, Throwable throwable) { 15 | Timber.e(throwable, throwable.getMessage()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/graphhopper/navigation/example/SolutionInputDialog.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation.example; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.app.Dialog; 6 | import android.app.DialogFragment; 7 | import android.content.DialogInterface; 8 | import android.os.Bundle; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.widget.EditText; 12 | 13 | public class SolutionInputDialog extends DialogFragment { 14 | 15 | private String jobId = ""; 16 | private String vehicleId = ""; 17 | 18 | public void setVehicleId(String vehicleId) { 19 | this.vehicleId = vehicleId; 20 | } 21 | 22 | public void setJobId(String jobId) { 23 | this.jobId = jobId; 24 | } 25 | 26 | public interface NoticeDialogListener { 27 | public void onDialogPositiveClick(DialogFragment dialog); 28 | } 29 | 30 | NoticeDialogListener mListener; 31 | 32 | @Override 33 | public void onAttach(Activity activity) { 34 | super.onAttach(activity); 35 | // Verify that the host activity implements the callback interface 36 | try { 37 | // Instantiate the NoticeDialogListener so we can send events to the host 38 | mListener = (NoticeDialogListener) activity; 39 | } catch (ClassCastException e) { 40 | // The activity doesn't implement the interface, throw exception 41 | throw new ClassCastException(activity.toString() 42 | + " must implement NoticeDialogListener"); 43 | } 44 | } 45 | 46 | @Override 47 | public Dialog onCreateDialog(Bundle savedInstanceState) { 48 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 49 | // Get the layout inflater 50 | LayoutInflater inflater = getActivity().getLayoutInflater(); 51 | 52 | View modifiedView = inflater.inflate(R.layout.solution_input, null); 53 | if (!jobId.isEmpty()) { 54 | EditText jobIdEditText = (EditText) modifiedView.findViewById(R.id.job_id); 55 | jobIdEditText.setText(jobId); 56 | } 57 | if (!vehicleId.isEmpty()) { 58 | EditText vehicleIdEditText = (EditText) modifiedView.findViewById(R.id.vehicle_id); 59 | vehicleIdEditText.setText(vehicleId); 60 | } 61 | 62 | 63 | // Inflate and set the layout for the dialog 64 | // Pass null as the parent view because its going in the dialog layout 65 | builder.setView(modifiedView) 66 | // Add action buttons 67 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 68 | @Override 69 | public void onClick(DialogInterface dialog, int id) { 70 | mListener.onDialogPositiveClick(SolutionInputDialog.this); 71 | } 72 | }) 73 | .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 74 | public void onClick(DialogInterface dialog, int id) { 75 | SolutionInputDialog.this.getDialog().cancel(); 76 | } 77 | }); 78 | 79 | return builder.create(); 80 | } 81 | 82 | } 83 | 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_map_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/drawable-hdpi/ic_map_marker.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_map_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/drawable-mdpi/ic_map_marker.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_map_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/drawable-xhdpi/ic_map_marker.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_map_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/drawable-xxhdpi/ic_map_marker.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_map_marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/drawable-xxxhdpi/ic_map_marker.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cancel_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_download_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_help_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_navigation_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_applications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_navigation_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 15 | 16 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/attribution_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/geocoding_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/solution_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 24 | 25 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/menu/navigation_view_activity_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | 34 | 35 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/.gitignore: -------------------------------------------------------------------------------- 1 | developer-config.xml 2 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Default for Device 6 | English 7 | French 8 | German 9 | Indonesian 10 | 11 | 12 | 13 | @string/default_locale 14 | en 15 | fr 16 | de 17 | in 18 | 19 | 20 | 21 | Default for Device 22 | Metric 23 | Imperial 24 | 25 | 26 | 27 | @string/default_unit_type 28 | metric 29 | imperial 30 | 31 | 32 | 33 | Driving 34 | Cycling 35 | MTB 36 | Racing bike 37 | Walking 38 | Hike 39 | Scooter 40 | Small truck 41 | Truck 42 | 43 | 44 | 45 | driving 46 | cycling 47 | mtb 48 | racingbike 49 | walking 50 | hike 51 | scooter 52 | small_truck 53 | truck 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2F2F2F 4 | #2F2F2F 5 | #00f148 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/developer-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pk.test 5 | 6 | [GH_API_KEY] 7 | 8 | https://graphhopper.com/api/1/navigate/ 9 | 10 | 11 | 12 | https://tiles.mapilion.com/assets/osm-bright/style.json?key=[MAP_API_KEY] 13 | 14 | https://tiles.mapilion.com/assets/osm-bright/style.json?key=[MAP_API__KEY] 15 | 16 | https://tiles.mapilion.com/assets/osm-bright/style.json?key=[MAP_API_KEY] 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #00F148 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | GraphHopper Demo 3 | 4 | Settings 5 | Help 6 | Simulate Route 7 | Start route from current location 8 | Language 9 | Unit Type 10 | Route Profile 11 | 12 | JobId 13 | VehicleId 14 | Search… 15 | OK 16 | Cancel 17 | 18 | unit_type 19 | simulate_route 20 | start_from_location 21 | language 22 | route_profile 23 | first_start_dialog 24 | first_navigation_dialog 25 | default_for_device 26 | default_for_device 27 | current_night_mode 28 | 29 | Current route is not available 30 | Please select a longer route 31 | Valid route not found. 32 | There was an error calculating the route 33 | There was an error fetching the solution 34 | There was an error fetching results from the geocoding API 35 | Vehicle not found 36 | Location not found 37 | No location found 38 | A route cannot have more than 25 waypoints 39 | A route needs at least 2 waypoints 40 | No browser installed 41 | Too far from start 42 | Would you like to recalculate the route from your current location? 43 | Long press map to place waypoint 44 | Launch Navigation 45 | Reset Route 46 | Fetch Solution 47 | Search 48 | Getting Started 49 | Legal Notice 50 | I agree 51 | Contribute on Github 52 | 53 | Open Route from GH Maps 54 | Open VRP Solution 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/xml/fragment_navigation_view_preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 9 | 13 | 19 | 25 | 30 | -------------------------------------------------------------------------------- /app/src/test/java/com/graphhopper/navigation/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.graphhopper.navigation; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | mavenCentral() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.3.0' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | maven { url "https://oss.sonatype.org/content/repositories/snapshots" } 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /files/gh-nav1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/files/gh-nav1.png -------------------------------------------------------------------------------- /files/gh-nav2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/files/gh-nav2.png -------------------------------------------------------------------------------- /files/gh-nav3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/files/gh-nav3.png -------------------------------------------------------------------------------- /files/graphhopper-navigation-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/files/graphhopper-navigation-example.png -------------------------------------------------------------------------------- /files/graphhopper-navigation-example.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/files/graphhopper-navigation-example.xcf -------------------------------------------------------------------------------- /files/graphhopper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/files/graphhopper.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/graphhopper/graphhopper-navigation-example-legacy/25fd91368a4c3b507eed0e9f323f6b818f0ddc46/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Nov 14 11:23:19 CET 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------