Skip to content

Commit 54d6211

Browse files
committed
sample init
1 parent 5b9d068 commit 54d6211

30 files changed

+735
-0
lines changed

find-route/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

find-route/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Find route

find-route/README.metadata.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{
2+
}

find-route/build.gradle

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
apply plugin: 'com.android.application'
2+
apply plugin: 'org.jetbrains.kotlin.android'
3+
4+
android {
5+
compileSdkVersion rootProject.ext.compileSdkVersion
6+
7+
defaultConfig {
8+
applicationId "com.esri.arcgismaps.sample.findroute"
9+
minSdkVersion rootProject.ext.minSdkVersion
10+
targetSdkVersion rootProject.ext.targetSdkVersion
11+
versionCode rootProject.ext.versionCode
12+
versionName rootProject.ext.versionName
13+
buildConfigField("String", "API_KEY", API_KEY)
14+
}
15+
16+
buildTypes {
17+
release {
18+
minifyEnabled false
19+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20+
}
21+
}
22+
23+
buildFeatures {
24+
dataBinding true
25+
}
26+
}
27+
28+
dependencies {
29+
// lib dependencies from rootProject build.gradle
30+
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
31+
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
32+
implementation "com.google.android.material:material:$materialVersion"
33+
}

find-route/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.esri.arcgismaps.sample.findroute">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
7+
<application
8+
android:allowBackup="true"
9+
android:icon="@mipmap/ic_launcher"
10+
android:label="@string/app_name"
11+
android:roundIcon="@mipmap/ic_launcher_round"
12+
android:supportsRtl="true"
13+
android:theme="@style/AppTheme">
14+
<activity
15+
android:exported="true"
16+
android:name=".MainActivity"
17+
android:label="@string/app_name">
18+
<intent-filter>
19+
<action android:name="android.intent.action.MAIN" />
20+
21+
<category android:name="android.intent.category.LAUNCHER" />
22+
</intent-filter>
23+
</activity>
24+
</application>
25+
26+
</manifest>
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/* Copyright 2022 Esri
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*
15+
*/
16+
17+
package com.esri.arcgismaps.sample.findroute
18+
19+
import android.graphics.drawable.BitmapDrawable
20+
import android.os.Bundle
21+
import android.util.Log
22+
import android.view.View
23+
import android.widget.AdapterView
24+
import android.widget.ArrayAdapter
25+
import android.widget.ImageView
26+
import android.widget.LinearLayout
27+
import android.widget.ListView
28+
import android.widget.ProgressBar
29+
import androidx.appcompat.app.AppCompatActivity
30+
import androidx.constraintlayout.widget.ConstraintLayout
31+
import androidx.coordinatorlayout.widget.CoordinatorLayout
32+
import androidx.core.content.ContextCompat
33+
import androidx.databinding.DataBindingUtil
34+
import androidx.lifecycle.lifecycleScope
35+
import com.arcgismaps.ApiKey
36+
import com.arcgismaps.ArcGISEnvironment
37+
import com.arcgismaps.Color
38+
import com.arcgismaps.geometry.Point
39+
import com.arcgismaps.geometry.SpatialReference
40+
import com.arcgismaps.mapping.ArcGISMap
41+
import com.arcgismaps.mapping.BasemapStyle
42+
import com.arcgismaps.mapping.Viewpoint
43+
import com.arcgismaps.mapping.symbology.PictureMarkerSymbol
44+
import com.arcgismaps.mapping.symbology.SimpleLineSymbol
45+
import com.arcgismaps.mapping.symbology.SimpleLineSymbolStyle
46+
import com.arcgismaps.mapping.view.Graphic
47+
import com.arcgismaps.mapping.view.GraphicsOverlay
48+
import com.arcgismaps.mapping.view.MapView
49+
import com.arcgismaps.tasks.networkanalysis.DirectionManeuver
50+
import com.arcgismaps.tasks.networkanalysis.Route
51+
import com.arcgismaps.tasks.networkanalysis.RouteParameters
52+
import com.arcgismaps.tasks.networkanalysis.RouteResult
53+
import com.arcgismaps.tasks.networkanalysis.RouteTask
54+
import com.arcgismaps.tasks.networkanalysis.Stop
55+
import com.esri.arcgismaps.sample.findroute.databinding.ActivityMainBinding
56+
import com.google.android.material.bottomsheet.BottomSheetBehavior
57+
import com.google.android.material.floatingactionbutton.FloatingActionButton
58+
import com.google.android.material.snackbar.Snackbar
59+
import kotlinx.coroutines.launch
60+
61+
class MainActivity : AppCompatActivity() {
62+
63+
private val TAG = MainActivity::class.java.simpleName
64+
65+
// set up data binding for the activity
66+
private val activityMainBinding: ActivityMainBinding by lazy {
67+
DataBindingUtil.setContentView(this, R.layout.activity_main)
68+
}
69+
70+
private val graphicsOverlay: GraphicsOverlay by lazy { GraphicsOverlay() }
71+
72+
private val mapView: MapView by lazy {
73+
activityMainBinding.mapView
74+
}
75+
76+
private val mainContainer: ConstraintLayout by lazy {
77+
activityMainBinding.mainContainer
78+
}
79+
80+
private val mainProgressBar: ProgressBar by lazy {
81+
activityMainBinding.mainProgressBar
82+
}
83+
84+
private val directionFab: FloatingActionButton by lazy {
85+
activityMainBinding.directionFab
86+
}
87+
88+
private val bottomSheet: LinearLayout by lazy {
89+
activityMainBinding.bottomSheet.bottomSheetLayout
90+
}
91+
92+
private val header: ConstraintLayout by lazy {
93+
activityMainBinding.bottomSheet.header
94+
}
95+
96+
private val imageView: ImageView by lazy {
97+
activityMainBinding.bottomSheet.imageView
98+
}
99+
100+
private val directionsListView: ListView by lazy {
101+
activityMainBinding.bottomSheet.directionsListView
102+
}
103+
104+
override fun onCreate(savedInstanceState: Bundle?) {
105+
super.onCreate(savedInstanceState)
106+
107+
// authentication with an API key or named user is
108+
// required to access basemaps and other location services
109+
ArcGISEnvironment.apiKey = ApiKey.create(BuildConfig.API_KEY)
110+
lifecycle.addObserver(activityMainBinding.mapView)
111+
112+
mapView.apply {
113+
// set the map to a new map with the navigation base map
114+
map = ArcGISMap(BasemapStyle.ArcGISNavigation)
115+
// set initial viewpoint to San Diego
116+
setViewpoint(Viewpoint(32.7157, -117.1611, 200000.0))
117+
mapView.graphicsOverlays.add(graphicsOverlay)
118+
}
119+
120+
// create the symbols for the route
121+
setupSymbols()
122+
123+
// hide the bottom sheet and make the map view span the whole screen
124+
bottomSheet.visibility = View.INVISIBLE
125+
(mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 0
126+
127+
// solve the route and display the bottom sheet when the FAB is clicked
128+
directionFab.setOnClickListener { lifecycleScope.launch { solveRoute() } }
129+
}
130+
131+
/**
132+
* Solves the route using a Route Task, populates the navigation drawer with the directions,
133+
* and displays a graphic of the route on the map.
134+
*/
135+
private suspend fun solveRoute() {
136+
// create a route task instance
137+
val routeTask =
138+
RouteTask(
139+
"https://route-api.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World"
140+
)
141+
142+
// show the progress bar
143+
mainProgressBar.visibility = View.VISIBLE
144+
routeTask.createDefaultParameters().onSuccess { routeParams ->
145+
// create stops
146+
val stops = arrayListOf(
147+
Stop(Point(-117.1508, 32.7411, SpatialReference.wgs84())),
148+
Stop(Point(-117.1555, 32.7033, SpatialReference.wgs84()))
149+
)
150+
151+
routeParams.apply {
152+
setStops(stops)
153+
// set return directions as true to return turn-by-turn directions in the route's directionManeuvers
154+
returnDirections = true
155+
}
156+
157+
// solve the route
158+
val routeResult = routeTask.solveRoute(routeParams).getOrElse {
159+
showError(it.message.toString())
160+
} as RouteResult
161+
val route = routeResult.routes[0]
162+
// create a simple line symbol for the route
163+
val routeSymbol = SimpleLineSymbol(SimpleLineSymbolStyle.Solid, Color.blue, 5f)
164+
165+
// create a graphic for the route and add it to the graphics overlay
166+
graphicsOverlay.graphics.add(Graphic(route.routeGeometry, routeSymbol))
167+
// get the list of direction maneuvers and display it
168+
// NOTE: to get turn-by-turn directions the route parameters
169+
// must have the isReturnDirections parameter set to true.
170+
val directions = route.directionManeuvers
171+
setupBottomSheet(directions)
172+
173+
// when the route is solved, hide the FAB and the progress bar
174+
directionFab.visibility = View.GONE
175+
mainProgressBar.visibility = View.GONE
176+
}.onFailure {
177+
showError(it.message.toString())
178+
mainProgressBar.visibility = View.GONE
179+
}
180+
}
181+
182+
/** Creates a bottom sheet to display a list of direction maneuvers.
183+
* [directions] a list of DirectionManeuver which represents the route
184+
*/
185+
private fun setupBottomSheet(directions: List<DirectionManeuver>) {
186+
// create a bottom sheet behavior from the bottom sheet view in the main layout
187+
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet).apply {
188+
// expand the bottom sheet, and ensure it is displayed on the screen when collapsed
189+
state = BottomSheetBehavior.STATE_EXPANDED
190+
peekHeight = header.height
191+
// animate the arrow when the bottom sheet slides
192+
addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
193+
override fun onSlide(bottomSheet: View, slideOffset: Float) {
194+
imageView.rotation = slideOffset * 180f
195+
}
196+
197+
override fun onStateChanged(bottomSheet: View, newState: Int) {
198+
imageView.rotation = when (newState) {
199+
BottomSheetBehavior.STATE_EXPANDED -> 180f
200+
else -> imageView.rotation
201+
}
202+
}
203+
})
204+
}
205+
206+
bottomSheet.apply {
207+
visibility = View.VISIBLE
208+
// expand or collapse the bottom sheet when the header is clicked
209+
header.setOnClickListener {
210+
bottomSheetBehavior.state = when (bottomSheetBehavior.state) {
211+
BottomSheetBehavior.STATE_COLLAPSED -> BottomSheetBehavior.STATE_EXPANDED
212+
else -> BottomSheetBehavior.STATE_COLLAPSED
213+
}
214+
}
215+
// rotate the arrow so it starts off in the correct rotation
216+
imageView.rotation = 180f
217+
}
218+
219+
directionsListView.apply {
220+
// Set the adapter for the list view
221+
adapter = ArrayAdapter(
222+
this@MainActivity,
223+
android.R.layout.simple_list_item_1,
224+
directions.map { it.directionText }
225+
)
226+
// when the user taps a maneuver, set the viewpoint to that portion of the route
227+
onItemClickListener =
228+
AdapterView.OnItemClickListener { _, _, position, _ ->
229+
// remove any graphics that are not the two stops and the route graphic
230+
if (graphicsOverlay.graphics.size > 3) {
231+
graphicsOverlay.graphics.removeAt(graphicsOverlay.graphics.size - 1)
232+
}
233+
// set the viewpoint to the selected maneuver
234+
val geometry = directions[position].geometry
235+
if (geometry != null) {
236+
mapView.setViewpoint(
237+
Viewpoint(geometry.extent, 20.0)
238+
)
239+
}
240+
// create a graphic with a symbol for the maneuver and add it to the graphics overlay
241+
val selectedRouteSymbol = SimpleLineSymbol(
242+
SimpleLineSymbolStyle.Solid,
243+
Color.green, 5f
244+
)
245+
graphicsOverlay.graphics.add(Graphic(geometry, selectedRouteSymbol))
246+
// collapse the bottom sheet
247+
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
248+
}
249+
}
250+
251+
// shrink the map view so it is not hidden under the bottom sheet header
252+
(mainContainer.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin =
253+
header.height
254+
}
255+
256+
/**
257+
* Set up the source, destination and route symbols.
258+
*/
259+
private fun setupSymbols() {
260+
val startDrawable =
261+
ContextCompat.getDrawable(this, R.drawable.ic_source) as BitmapDrawable
262+
val pinSourceSymbol = PictureMarkerSymbol(startDrawable).apply {
263+
// make the graphic smaller
264+
width = 30f
265+
height = 30f
266+
offsetY = 20f
267+
}
268+
// create a point for the new graphic
269+
val sourcePoint = Point(
270+
-117.1508, 32.7411, SpatialReference.wgs84()
271+
)
272+
// create a graphic and it to the graphics overlay
273+
graphicsOverlay.graphics.add(Graphic(sourcePoint, pinSourceSymbol))
274+
275+
val endDrawable =
276+
ContextCompat.getDrawable(this, R.drawable.ic_destination) as BitmapDrawable
277+
278+
endDrawable.let {
279+
val pinDestinationSymbol =
280+
PictureMarkerSymbol(endDrawable).apply {
281+
// make the graphic smaller
282+
width = 30f
283+
height = 30f
284+
offsetY = 20f
285+
}
286+
// create a point for the new graphic
287+
val destinationPoint = Point(-117.1555, 32.7033, SpatialReference.wgs84())
288+
// create a graphic and add it to the graphics overlay
289+
graphicsOverlay.graphics.add(Graphic(destinationPoint, pinDestinationSymbol))
290+
}
291+
}
292+
293+
private fun showError(message: String) {
294+
Log.e(TAG, message)
295+
Snackbar.make(mapView, message, Snackbar.LENGTH_SHORT).show()
296+
}
297+
298+
private val Color.Companion.blue: Color
299+
get() {
300+
return fromRgba(0, 0, 255, 255)
301+
}
302+
303+
}

0 commit comments

Comments
 (0)