diff --git a/ApiDemos/kotlin/app/build.gradle b/ApiDemos/kotlin/app/build.gradle
index d7aba0ca..da49aef1 100644
--- a/ApiDemos/kotlin/app/build.gradle
+++ b/ApiDemos/kotlin/app/build.gradle
@@ -30,5 +30,9 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
-
+ // Below is the Google Play Services dependency required for using the Google Maps Android API
+ implementation 'com.google.android.gms:play-services-maps:11.8.0'
+ // Below is used to run the easypermissions library to manage location permissions
+ // EasyPermissions is needed to help us request for permission to access location
+ implementation 'pub.devrel:easypermissions:1.1.1'
}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/AndroidManifest.xml b/ApiDemos/kotlin/app/src/main/AndroidManifest.xml
index 2d16c4c9..980b5815 100644
--- a/ApiDemos/kotlin/app/src/main/AndroidManifest.xml
+++ b/ApiDemos/kotlin/app/src/main/AndroidManifest.xml
@@ -16,6 +16,11 @@
+
+
+
+
+
@@ -30,6 +38,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/CameraDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/CameraDemoActivity.kt
new file mode 100644
index 00000000..93197ed4
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/CameraDemoActivity.kt
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import android.graphics.Color
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.util.Log
+import android.view.View
+import android.widget.CompoundButton
+import android.widget.SeekBar
+import android.widget.Toast
+import com.google.android.gms.maps.CameraUpdate
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.GoogleMap.CancelableCallback
+import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener
+import com.google.android.gms.maps.GoogleMap.OnCameraMoveCanceledListener
+import com.google.android.gms.maps.GoogleMap.OnCameraMoveListener
+import com.google.android.gms.maps.GoogleMap.OnCameraMoveStartedListener
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.CameraPosition
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.PolylineOptions
+
+/**
+ * This shows how to change the camera position for the map.
+ */
+class CameraDemoActivity :
+ AppCompatActivity(),
+ OnCameraMoveStartedListener,
+ OnCameraMoveListener,
+ OnCameraMoveCanceledListener,
+ OnCameraIdleListener,
+ OnMapReadyCallback {
+
+ /**
+ * The amount by which to scroll the camera. Note that this amount is in raw pixels, not dp
+ * (density-independent pixels).
+ */
+ private val SCROLL_BY_PX = 100
+ private val TAG = CameraDemoActivity::class.java.name
+ private val sydneyLatLng = LatLng(-33.87365, 151.20689)
+ private val bondiLocation: CameraPosition = CameraPosition.Builder().
+ target(LatLng(-33.891614, 151.276417))
+ .zoom(15.5f)
+ .bearing(300f)
+ .tilt(50f)
+ .build()
+
+ private val sydneyLocation: CameraPosition = CameraPosition.Builder().
+ target(LatLng(-33.87365, 151.20689))
+ .zoom(15.5f)
+ .bearing(0f)
+ .tilt(25f)
+ .build()
+
+
+ private lateinit var map: GoogleMap
+
+ private lateinit var animateToggle: CompoundButton
+ private lateinit var customDurationToggle: CompoundButton
+ private lateinit var customDurationBar: SeekBar
+ private var currPolylineOptions: PolylineOptions? = null
+ private var isCanceled = false
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.camera_demo)
+
+ animateToggle = findViewById(R.id.animate)
+ customDurationToggle = findViewById(R.id.duration_toggle)
+ customDurationBar = findViewById(R.id.duration_bar)
+
+ updateEnabledState()
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ mapFragment.getMapAsync(this)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ updateEnabledState()
+ }
+
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // return early if the map was not initialised properly
+ map = googleMap ?: return
+
+ with(googleMap) {
+ setOnCameraIdleListener(this@CameraDemoActivity)
+ setOnCameraMoveStartedListener(this@CameraDemoActivity)
+ setOnCameraMoveListener(this@CameraDemoActivity)
+ setOnCameraMoveCanceledListener(this@CameraDemoActivity)
+
+ // We will provide our own zoom controls.
+ uiSettings.isZoomControlsEnabled = false
+ uiSettings.isMyLocationButtonEnabled = true
+
+ // Show Sydney
+ moveCamera(CameraUpdateFactory.newLatLngZoom(sydneyLatLng, 10f))
+ }
+ }
+
+ /**
+ * When the map is not ready the CameraUpdateFactory cannot be used. This should be used to wrap
+ * all entry points that call methods on the Google Maps API.
+ *
+ * @param stuffToDo the code to be executed if the map is initialised
+ */
+ private fun checkReadyThen(stuffToDo: () -> Unit) {
+ if (!::map.isInitialized) {
+ Toast.makeText(this, R.string.map_not_ready, Toast.LENGTH_SHORT).show()
+ } else {
+ stuffToDo()
+ }
+ }
+
+ /**
+ * Called when the Go To Bondi button is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onGoToBondi(view: View) {
+ checkReadyThen {
+ changeCamera(CameraUpdateFactory.newCameraPosition(bondiLocation))
+ }
+ }
+
+ /**
+ * Called when the Animate To Sydney button is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onGoToSydney(view: View) {
+ checkReadyThen {
+ changeCamera(CameraUpdateFactory.newCameraPosition(sydneyLocation),
+ object : CancelableCallback {
+ override fun onFinish() {
+ Toast.makeText(baseContext, "Animation to Sydney complete",
+ Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onCancel() {
+ Toast.makeText(baseContext, "Animation to Sydney canceled",
+ Toast.LENGTH_SHORT).show()
+ }
+ })
+ }
+ }
+
+ /**
+ * Called when the stop button is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onStopAnimation(view: View) = checkReadyThen { map.stopAnimation() }
+
+ /**
+ * Called when the zoom in button (the one with the +) is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onZoomIn(view: View) = checkReadyThen { changeCamera(CameraUpdateFactory.zoomIn()) }
+
+ /**
+ * Called when the zoom out button (the one with the -) is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onZoomOut(view: View) = checkReadyThen { changeCamera(CameraUpdateFactory.zoomOut()) }
+
+ /**
+ * Called when the tilt more button (the one with the /) is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onTiltMore(view: View) {
+ checkReadyThen {
+
+ val newTilt = Math.min(map.cameraPosition.tilt + 10, 90F)
+ val cameraPosition = CameraPosition.Builder(map.cameraPosition).tilt(newTilt).build()
+
+ changeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
+ }
+ }
+
+ /**
+ * Called when the tilt less button (the one with the \) is clicked.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onTiltLess(view: View) {
+ checkReadyThen {
+
+ val newTilt = Math.max(map.cameraPosition.tilt - 10, 0F)
+ val cameraPosition = CameraPosition.Builder(map.cameraPosition).tilt(newTilt).build()
+
+ changeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
+ }
+ }
+
+ /**
+ * Called when the left arrow button is clicked. This causes the camera to move to the left
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onScrollLeft(view: View) {
+ checkReadyThen {
+ changeCamera(CameraUpdateFactory.scrollBy((-SCROLL_BY_PX).toFloat(),0f))
+ }
+ }
+
+ /**
+ * Called when the right arrow button is clicked. This causes the camera to move to the right.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onScrollRight(view: View) {
+ checkReadyThen {
+ changeCamera(CameraUpdateFactory.scrollBy(SCROLL_BY_PX.toFloat(), 0f))
+ }
+ }
+
+ /**
+ * Called when the up arrow button is clicked. The causes the camera to move up.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onScrollUp(view: View) {
+ checkReadyThen {
+ changeCamera(CameraUpdateFactory.scrollBy(0f, (-SCROLL_BY_PX).toFloat()))
+ }
+ }
+
+ /**
+ * Called when the down arrow button is clicked. This causes the camera to move down.
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onScrollDown(view: View) {
+ checkReadyThen {
+ changeCamera(CameraUpdateFactory.scrollBy(0f, SCROLL_BY_PX.toFloat()))
+ }
+ }
+
+ /**
+ * Called when the animate button is toggled
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onToggleAnimate(view: View) = updateEnabledState()
+
+ /**
+ * Called when the custom duration checkbox is toggled
+ */
+ @Suppress("UNUSED_PARAMETER")
+ fun onToggleCustomDuration(view: View) = updateEnabledState()
+
+ /**
+ * Update the enabled state of the custom duration controls.
+ */
+ private fun updateEnabledState() {
+ customDurationToggle.isEnabled = animateToggle.isChecked
+ customDurationBar.isEnabled = animateToggle.isChecked && customDurationToggle.isChecked
+ }
+
+ /**
+ * Change the camera position by moving or animating the camera depending on the state of the
+ * animate toggle button.
+ */
+ private fun changeCamera(update: CameraUpdate, callback: CancelableCallback? = null) {
+ if (animateToggle.isChecked) {
+ if (customDurationToggle.isChecked) {
+ // The duration must be strictly positive so we make it at least 1.
+ map.animateCamera(update, Math.max(customDurationBar.progress, 1), callback)
+ } else {
+ map.animateCamera(update, callback)
+ }
+ } else {
+ map.moveCamera(update)
+ }
+ }
+
+ override fun onCameraMoveStarted(reason: Int) {
+ if (!isCanceled) map.clear()
+
+
+ var reasonText = "UNKNOWN_REASON"
+ currPolylineOptions = PolylineOptions().width(5f)
+ when (reason) {
+ OnCameraMoveStartedListener.REASON_GESTURE -> {
+ currPolylineOptions?.color(Color.BLUE)
+ reasonText = "GESTURE"
+ }
+ OnCameraMoveStartedListener.REASON_API_ANIMATION -> {
+ currPolylineOptions?.color(Color.RED)
+ reasonText = "API_ANIMATION"
+ }
+ OnCameraMoveStartedListener.REASON_DEVELOPER_ANIMATION -> {
+ currPolylineOptions?.color(Color.GREEN)
+ reasonText = "DEVELOPER_ANIMATION"
+ }
+ }
+ Log.i(TAG, "onCameraMoveStarted($reasonText)")
+ addCameraTargetToPath()
+ }
+
+ /**
+ * Ensures that currPolyLine options is not null before accessing it
+ *
+ * @param stuffToDo the code to be executed if currPolylineOptions is not null
+ */
+ private fun checkPolylineThen(stuffToDo: () -> Unit) {
+ if (currPolylineOptions != null) stuffToDo()
+ }
+
+
+ override fun onCameraMove() {
+ Log.i(TAG, "onCameraMove")
+ // When the camera is moving, add its target to the current path we'll draw on the map.
+ checkPolylineThen { addCameraTargetToPath() }
+ }
+
+ override fun onCameraMoveCanceled() {
+ // When the camera stops moving, add its target to the current path, and draw it on the map.
+ checkPolylineThen {
+ addCameraTargetToPath()
+ map.addPolyline(currPolylineOptions)
+ }
+
+ isCanceled = true // Set to clear the map when dragging starts again.
+ currPolylineOptions = null
+ Log.i(TAG, "onCameraMoveCancelled")
+ }
+
+ override fun onCameraIdle() {
+ checkPolylineThen {
+ addCameraTargetToPath()
+ map.addPolyline(currPolylineOptions)
+ }
+
+ currPolylineOptions = null
+ isCanceled = false // Set to *not* clear the map when dragging starts again.
+ Log.i(TAG, "onCameraIdle")
+ }
+
+ private fun addCameraTargetToPath() {
+ currPolylineOptions?.add(map.cameraPosition.target)
+ }
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt
index 901f7f4e..123ad938 100644
--- a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt
@@ -21,6 +21,21 @@ package com.example.kotlindemos
*/
class DemoDetailsList {
companion object {
- val DEMOS = listOf()
+ val DEMOS = listOf(
+ DemoDetails(R.string.polygon_demo_label, R.string.polygon_demo_details,
+ PolygonDemoActivity::class.java),
+ DemoDetails(R.string.camera_demo_label, R.string.camera_demo_description,
+ CameraDemoActivity::class.java),
+ DemoDetails(R.string.markers_demo_label, R.string.markers_demo_description,
+ MarkerDemoActivity::class.java),
+ DemoDetails(R.string.layers_demo_label, R.string.layers_demo_description,
+ LayersDemoActivity::class.java),
+ DemoDetails(R.string.polyline_demo_label, R.string.polyline_demo_description,
+ PolylineDemoActivity::class.java),
+ DemoDetails(R.string.tags_demo_label, R.string.tags_demo_details,
+ TagsDemoActivity::class.java),
+ DemoDetails(R.string.region_demo_label, R.string.region_demo_details,
+ VisibleRegionDemoActivity::class.java)
+ )
}
}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/LayersDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/LayersDemoActivity.kt
new file mode 100644
index 00000000..58e667f8
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/LayersDemoActivity.kt
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.AlertDialog
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.util.Log
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.CheckBox
+import android.widget.Spinner
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_HYBRID
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_NONE
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_NORMAL
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_SATELLITE
+import com.google.android.gms.maps.GoogleMap.MAP_TYPE_TERRAIN
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+import pub.devrel.easypermissions.AfterPermissionGranted
+import pub.devrel.easypermissions.EasyPermissions
+
+private const val LOCATION_PERMISSION_REQUEST_CODE = 1
+
+/**
+ * Demonstrates the different base layers of a map.
+ */
+class LayersDemoActivity :
+ AppCompatActivity(),
+ OnMapReadyCallback,
+ AdapterView.OnItemSelectedListener,
+ EasyPermissions.PermissionCallbacks {
+
+ private val TAG = MarkerDemoActivity::class.java.name
+
+ private lateinit var map: GoogleMap
+
+ private lateinit var trafficCheckbox: CheckBox
+ private lateinit var myLocationCheckbox: CheckBox
+ private lateinit var buildingsCheckbox: CheckBox
+ private lateinit var indoorCheckbox: CheckBox
+ private lateinit var spinner: Spinner
+
+ /**
+ * Flag indicating whether a requested permission has been denied after returning in
+ * [.onRequestPermissionsResult].
+ */
+ private var showPermissionDeniedDialog = false
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.layers_demo)
+
+ spinner = findViewById(R.id.layers_spinner).apply {
+ adapter = ArrayAdapter.createFromResource(this@LayersDemoActivity,
+ R.array.layers_array, android.R.layout.simple_spinner_item).apply {
+ setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ }
+
+ // set a listener for when the spinner to select map type is changed.
+ onItemSelectedListener = this@LayersDemoActivity
+ }
+
+ myLocationCheckbox = findViewById(R.id.my_location)
+ buildingsCheckbox = findViewById(R.id.buildings)
+ indoorCheckbox = findViewById(R.id.indoor)
+ trafficCheckbox = findViewById(R.id.traffic)
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ mapFragment.getMapAsync(this)
+ }
+
+ /**
+ * Display a dialog box asking the user to grant permissions if they were denied
+ */
+ override fun onResumeFragments() {
+ super.onResumeFragments()
+ if (showPermissionDeniedDialog) {
+ AlertDialog.Builder(this).apply {
+ setPositiveButton(R.string.ok, null)
+ setMessage(R.string.location_permission_denied)
+ create()
+ }.show()
+ showPermissionDeniedDialog = false
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // exit early if the map was not initialised properly
+ map = googleMap ?: return
+
+ updateMapType()
+
+ // check the state of all checkboxes and update the map accordingly
+ with(map) {
+ isTrafficEnabled = trafficCheckbox.isChecked
+ isBuildingsEnabled = buildingsCheckbox.isChecked
+ isIndoorEnabled = indoorCheckbox.isChecked
+ }
+
+ // Must deal with the location checkbox separately as must check that
+ // location permission have been granted before enabling the 'My Location' layer.
+ if (myLocationCheckbox.isChecked) enableMyLocation()
+
+
+ // attach a listener to each checkbox
+ trafficCheckbox.setOnClickListener { map.isTrafficEnabled = trafficCheckbox.isChecked }
+
+ buildingsCheckbox.setOnClickListener {
+ map.isBuildingsEnabled = buildingsCheckbox.isChecked
+ }
+
+ indoorCheckbox.setOnClickListener { map.isIndoorEnabled = indoorCheckbox.isChecked }
+
+ // if this box is checked, must check for permission before enabling the My Location layer
+ myLocationCheckbox.setOnClickListener {
+ if (!myLocationCheckbox.isChecked) {
+ map.isMyLocationEnabled = false
+ } else {
+ enableMyLocation()
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ @AfterPermissionGranted(LOCATION_PERMISSION_REQUEST_CODE)
+ private fun enableMyLocation() {
+ // Enable the location layer. Request the location permission if needed.
+ val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
+
+ if (EasyPermissions.hasPermissions(this, *permissions)) {
+ map.isMyLocationEnabled = true
+
+ } else {
+ // if permissions are not currently granted, request permissions
+ EasyPermissions.requestPermissions(this,
+ getString(R.string.permission_rationale_location),
+ LOCATION_PERMISSION_REQUEST_CODE, *permissions)
+ }
+ }
+
+ /**
+ * Change the type of the map depending on the currently selected item in the spinner
+ */
+ private fun updateMapType() {
+ // This can also be called by the Android framework in onCreate() at which
+ // point map may not be ready yet.
+ if (!::map.isInitialized) return
+
+ map.mapType = when (spinner.selectedItem) {
+ getString(R.string.normal) -> MAP_TYPE_NORMAL
+ getString(R.string.hybrid) -> MAP_TYPE_HYBRID
+ getString(R.string.satellite) -> MAP_TYPE_SATELLITE
+ getString(R.string.terrain) -> MAP_TYPE_TERRAIN
+ getString(R.string.none_map) -> MAP_TYPE_NONE
+ else -> {
+ map.mapType // do not change map type
+ Log.e(TAG, "Error setting layer with name ${spinner.selectedItem}")
+ }
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ EasyPermissions.onRequestPermissionsResult(requestCode,
+ permissions, grantResults, this)
+ }
+
+ override fun onPermissionsDenied(requestCode: Int, list: List) {
+ // Un-check the box until the layer has been enabled
+ // and show dialog box with permission rationale.
+ myLocationCheckbox.isChecked = false
+ showPermissionDeniedDialog = true
+ }
+
+ override fun onPermissionsGranted(requestCode: Int, perms: MutableList) {
+ // do nothing, handled in updateMyLocation
+ }
+
+ /**
+ * Called as part of the AdapterView.OnItemSelectedListener
+ */
+ override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
+ updateMapType()
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>) {
+ // Do nothing.
+ }
+
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt
new file mode 100644
index 00000000..3ff7e67a
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt
@@ -0,0 +1,494 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.media.Image
+import android.os.Bundle
+import android.os.Handler
+import android.os.SystemClock
+import android.support.annotation.ColorInt
+import android.support.annotation.DrawableRes
+import android.support.v4.content.res.ResourcesCompat
+import android.support.v4.graphics.drawable.DrawableCompat
+import android.support.v7.app.AppCompatActivity
+import android.text.SpannableString
+import android.text.style.ForegroundColorSpan
+import android.util.Log
+import android.view.View
+import android.view.animation.BounceInterpolator
+import android.widget.CheckBox
+import android.widget.ImageView
+import android.widget.RadioGroup
+import android.widget.SeekBar
+import android.widget.SeekBar.OnSeekBarChangeListener
+import android.widget.TextView
+import android.widget.Toast
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.GoogleMap.InfoWindowAdapter
+import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener
+import com.google.android.gms.maps.GoogleMap.OnInfoWindowCloseListener
+import com.google.android.gms.maps.GoogleMap.OnInfoWindowLongClickListener
+import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener
+import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.BitmapDescriptor
+import com.google.android.gms.maps.model.BitmapDescriptorFactory
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.google.android.gms.maps.model.Marker
+import com.google.android.gms.maps.model.MarkerOptions
+import java.util.ArrayList
+import java.util.Random
+
+/**
+ * This stores the details of a place that used to draw a marker
+ */
+class PlaceDetails(
+ val position: LatLng,
+ val title: String = "Marker",
+ val snippet: String? = null,
+ val icon: BitmapDescriptor = BitmapDescriptorFactory.defaultMarker(),
+ val infoWindowAnchorX: Float = 0.5F,
+ val infoWindowAnchorY: Float = 0F,
+ val draggable: Boolean = false,
+ val zIndex: Float = 0F)
+
+/**
+ * This shows how to place markers on a map.
+ */
+class MarkerDemoActivity :
+ AppCompatActivity(),
+ OnMarkerClickListener,
+ OnInfoWindowClickListener,
+ OnMarkerDragListener,
+ OnInfoWindowLongClickListener,
+ OnInfoWindowCloseListener,
+ OnMapAndViewReadyListener.OnGlobalLayoutAndMapReadyListener {
+
+ private val TAG = MarkerDemoActivity::class.java.name
+
+ /** This is ok to be lateinit as it is initialised in onMapReady */
+ private lateinit var map: GoogleMap
+
+ /**
+ * Keeps track of the last selected marker (though it may no longer be selected). This is
+ * useful for refreshing the info window.
+ *
+ * Must be nullable as it is null when no marker has been selected
+ */
+ private var lastSelectedMarker: Marker? = null
+
+ private val markerRainbow = ArrayList()
+
+ /** map to store place names and locations */
+ private val places = mapOf(
+ "BRISBANE" to LatLng(-27.47093, 153.0235),
+ "MELBOURNE" to LatLng(-37.81319, 144.96298),
+ "DARWIN" to LatLng(-12.4634, 130.8456),
+ "SYDNEY" to LatLng(-33.87365, 151.20689),
+ "ADELAIDE" to LatLng(-34.92873, 138.59995),
+ "PERTH" to LatLng(-31.952854, 115.857342),
+ "ALICE_SPRINGS" to LatLng(-24.6980, 133.8807)
+ )
+
+ /** These can be lateinit as they are set in onCreate */
+ private lateinit var topText: TextView
+ private lateinit var rotationBar: SeekBar
+ private lateinit var flatBox: CheckBox
+ private lateinit var options: RadioGroup
+
+ private val random = Random()
+
+ /** Demonstrates customizing the info window and/or its contents. */
+ internal inner class CustomInfoWindowAdapter : InfoWindowAdapter {
+
+ // These are both view groups containing an ImageView with id "badge" and two
+ // TextViews with id "title" and "snippet".
+ private val window: View = layoutInflater.inflate(R.layout.custom_info_window, null)
+ private val contents: View = layoutInflater.inflate(R.layout.custom_info_contents, null)
+
+ override fun getInfoWindow(marker: Marker): View? {
+ if (options.checkedRadioButtonId != R.id.custom_info_window) {
+ // This means that getInfoContents will be called.
+ return null
+ }
+ render(marker, window)
+ return window
+ }
+
+ override fun getInfoContents(marker: Marker): View? {
+ if (options.checkedRadioButtonId != R.id.custom_info_contents) {
+ // This means that the default info contents will be used.
+ return null
+ }
+ render(marker, contents)
+ return contents
+ }
+
+ private fun render(marker: Marker, view: View) {
+ val badge = when (marker.title) {
+ "Brisbane" -> R.drawable.badge_qld
+ "Adelaide" -> R.drawable.badge_sa
+ "Sydney" -> R.drawable.badge_nsw
+ "Melbourne" -> R.drawable.badge_victoria
+ "Perth" -> R.drawable.badge_wa
+ in "Darwin Marker 1".."Darwin Marker 4" -> R.drawable.badge_nt
+ else -> 0 // Passing 0 to setImageResource will clear the image view.
+ }
+
+ view.findViewById(R.id.badge).setImageResource(badge)
+
+ // Set the title and snippet for the custom info window
+ val title: String? = marker.title
+ val titleUi = view.findViewById(R.id.title)
+
+ if (title != null) {
+ // Spannable string allows us to edit the formatting of the text.
+ titleUi.text = SpannableString(title).apply {
+ setSpan(ForegroundColorSpan(Color.RED), 0, length, 0)
+ }
+ } else {
+ titleUi.text = ""
+ }
+
+ val snippet: String? = marker.snippet
+ val snippetUi = view.findViewById(R.id.snippet)
+ if (snippet != null && snippet.length > 12) {
+ snippetUi.text = SpannableString(snippet).apply {
+ setSpan(ForegroundColorSpan(Color.MAGENTA), 0, 10, 0)
+ setSpan(ForegroundColorSpan(Color.BLUE), 12, snippet.length, 0)
+ }
+ } else {
+ snippetUi.text = ""
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.marker_demo)
+
+ topText = findViewById(R.id.top_text)
+
+ rotationBar = findViewById(R.id.rotationSeekBar).apply {
+ max = 360
+ setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
+
+ /** Called when the Rotation progress bar is moved */
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ val rotation = seekBar?.progress?.toFloat()
+ checkReadyThen { markerRainbow.map { it.rotation = rotation ?: 0f } }
+ }
+
+ override fun onStartTrackingTouch(p0: SeekBar?) {
+ // do nothing
+ }
+
+ override fun onStopTrackingTouch(p0: SeekBar?) {
+ //do nothing
+ }
+
+ } )
+ }
+
+ flatBox = findViewById(R.id.flat)
+
+ options = findViewById(R.id.custom_info_window_options).apply {
+ setOnCheckedChangeListener { _, _ ->
+ if (lastSelectedMarker?.isInfoWindowShown == true) {
+ // Refresh the info window when the info window's content has changed.
+ // must deal with the possibility that lastSelectedMarker has changed in
+ // another thread between the null check and this line, do this with !!
+ lastSelectedMarker?.showInfoWindow()
+ }
+ }
+ }
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ OnMapAndViewReadyListener(mapFragment, this)
+ }
+
+ /**
+ * This is the callback that is triggered when the GoogleMap has loaded and is ready for use
+ */
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // return early if the map was not initialised properly
+ map = googleMap ?: return
+
+ // create bounds that encompass every location we reference
+ val boundsBuilder = LatLngBounds.Builder()
+ // include all places we have markers for on the map
+ places.keys.map { place -> boundsBuilder.include(places.getValue(place)) }
+ val bounds = boundsBuilder.build()
+
+ with(map) {
+ // Hide the zoom controls as the button panel will cover it.
+ uiSettings.isZoomControlsEnabled = false
+
+ // Setting an info window adapter allows us to change the both the contents and
+ // look of the info window.
+ setInfoWindowAdapter(CustomInfoWindowAdapter())
+
+ // Set listeners for marker events. See the bottom of this class for their behavior.
+ setOnMarkerClickListener(this@MarkerDemoActivity)
+ setOnInfoWindowClickListener(this@MarkerDemoActivity)
+ setOnMarkerDragListener(this@MarkerDemoActivity)
+ setOnInfoWindowCloseListener(this@MarkerDemoActivity)
+ setOnInfoWindowLongClickListener(this@MarkerDemoActivity)
+
+ // Override the default content description on the view, for accessibility mode.
+ // Ideally this string would be localised.
+ setContentDescription("Map with lots of markers.")
+
+ moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50))
+ }
+
+ // Add lots of markers to the googleMap.
+ addMarkersToMap()
+
+ }
+
+ /**
+ * Show all the specified markers on the map
+ */
+ private fun addMarkersToMap() {
+
+ val placeDetailsMap = mutableMapOf(
+ // Uses a coloured icon
+ "BRISBANE" to PlaceDetails(
+ position = places.getValue("BRISBANE"),
+ title = "Brisbane",
+ snippet = "Population: 2,074,200",
+ icon = BitmapDescriptorFactory
+ .defaultMarker(BitmapDescriptorFactory.HUE_AZURE)
+ ),
+
+ // Uses a custom icon with the info window popping out of the center of the icon.
+ "SYDNEY" to PlaceDetails(
+ position = places.getValue("SYDNEY"),
+ title = "Sydney",
+ snippet = "Population: 4,627,300",
+ icon = BitmapDescriptorFactory.fromResource(R.drawable.arrow),
+ infoWindowAnchorX = 0.5f,
+ infoWindowAnchorY = 0.5f
+ ),
+
+ // Will create a draggable marker. Long press to drag.
+ "MELBOURNE" to PlaceDetails(
+ position = places.getValue("MELBOURNE"),
+ title = "Melbourne",
+ snippet = "Population: 4,137,400",
+ draggable = true
+ ),
+
+ // Use a vector drawable resource as a marker icon.
+ "ALICE_SPRINGS" to PlaceDetails(
+ position = places.getValue("ALICE_SPRINGS"),
+ title = "Alice Springs",
+ icon = vectorToBitmap(
+ R.drawable.ic_android, Color.parseColor("#A4C639"))
+ ),
+
+ // More markers for good measure
+ "PERTH" to PlaceDetails(
+ position = places.getValue("PERTH"),
+ title = "Perth",
+ snippet = "Population: 1,738,800"
+ ),
+
+ "ADELAIDE" to PlaceDetails(
+ position = places.getValue("ADELAIDE"),
+ title = "Adelaide",
+ snippet = "Population: 1,213,000"
+ )
+
+ )
+
+ // add 4 markers on top of each other in Darwin with varying z-indexes
+ (0 until 4).map {
+ placeDetailsMap.put(
+ "DARWIN ${it + 1}", PlaceDetails(
+ position = places.getValue("DARWIN"),
+ title = "Darwin Marker ${it + 1}",
+ snippet = "z-index initially ${it + 1}",
+ zIndex = it.toFloat()
+ )
+ )
+ }
+
+ // place markers for each of the defined locations
+ placeDetailsMap.keys.map {
+ with(placeDetailsMap.getValue(it)) {
+ map.addMarker(MarkerOptions()
+ .position(position)
+ .title(title)
+ .snippet(snippet)
+ .icon(icon)
+ .infoWindowAnchor(infoWindowAnchorX, infoWindowAnchorY)
+ .draggable(draggable)
+ .zIndex(zIndex))
+
+ }
+ }
+
+ // Creates a marker rainbow demonstrating how to create default marker icons of different
+ // hues (colors).
+ val numMarkersInRainbow = 12
+ (0 until numMarkersInRainbow).mapTo(markerRainbow) {
+ map.addMarker(MarkerOptions().apply{
+ position(LatLng(
+ -30 + 10 * Math.sin(it * Math.PI / (numMarkersInRainbow - 1)),
+ 135 - 10 * Math.cos(it * Math.PI / (numMarkersInRainbow - 1))))
+ title("Marker $it")
+ icon(BitmapDescriptorFactory.defaultMarker((it * 360 / numMarkersInRainbow)
+ .toFloat()))
+ flat(flatBox.isChecked)
+ rotation(rotationBar.progress.toFloat())
+ })
+ }
+ }
+
+ /**
+ * Demonstrates converting a [Drawable] to a [BitmapDescriptor],
+ * for use as a marker icon.
+ */
+ private fun vectorToBitmap(@DrawableRes id : Int, @ColorInt color : Int): BitmapDescriptor {
+ val vectorDrawable: Drawable? = ResourcesCompat.getDrawable(resources, id, null)
+ if (vectorDrawable == null) {
+ Log.e(TAG, "Resource not found")
+ return BitmapDescriptorFactory.defaultMarker()
+ }
+ val bitmap = Bitmap.createBitmap(vectorDrawable.intrinsicWidth,
+ vectorDrawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(bitmap)
+ vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
+ DrawableCompat.setTint(vectorDrawable, color)
+ vectorDrawable.draw(canvas)
+ return BitmapDescriptorFactory.fromBitmap(bitmap)
+ }
+
+ /**
+ * Checks if the map is ready, the executes the provided lambda function
+ *
+ * @param stuffToDo the code to be executed if the map is ready
+ */
+ private fun checkReadyThen(stuffToDo : () -> Unit) {
+ if (!::map.isInitialized) {
+ Toast.makeText(this, R.string.map_not_ready, Toast.LENGTH_SHORT).show()
+ } else {
+ stuffToDo()
+ }
+ }
+
+ /** Called when the Clear button is clicked. */
+ @Suppress("UNUSED_PARAMETER")
+ fun onClearMap(view: View) {
+ checkReadyThen { map.clear() }
+ }
+
+ /** Called when the Reset button is clicked. */
+ @Suppress("UNUSED_PARAMETER")
+ fun onResetMap(view: View) {
+ checkReadyThen {
+ map.clear()
+ addMarkersToMap()
+ }
+ }
+
+ /** Called when the Flat check box is checked or unchecked */
+ @Suppress("UNUSED_PARAMETER")
+ fun onToggleFlat(view: View) {
+ checkReadyThen { markerRainbow.map { marker -> marker.isFlat = flatBox.isChecked } }
+ }
+
+ //
+ // Marker related listeners.
+ //
+ override fun onMarkerClick(marker : Marker): Boolean {
+
+ // Markers have a z-index that is settable and gettable.
+ marker.zIndex += 1.0f
+ Toast.makeText(this, "${marker.title} z-index set to ${marker.zIndex}",
+ Toast.LENGTH_SHORT).show()
+
+ lastSelectedMarker = marker
+
+ if (marker.position == places.getValue("PERTH")) {
+ // This causes the marker at Perth to bounce into position when it is clicked.
+ val handler = Handler()
+ val start = SystemClock.uptimeMillis()
+ val duration = 1500
+
+ val interpolator = BounceInterpolator()
+
+ handler.post(object : Runnable {
+ override fun run() {
+ val elapsed = SystemClock.uptimeMillis() - start
+ val t = Math.max(
+ 1 - interpolator.getInterpolation(elapsed.toFloat() / duration), 0f)
+ marker.setAnchor(0.5f, 1.0f + 2 * t)
+
+ // Post again 16ms later.
+ if (t > 0.0) {
+ handler.postDelayed(this, 16)
+ }
+ }
+ })
+ } else if (marker.position == places.getValue("ADELAIDE")) {
+ // This causes the marker at Adelaide to change color and alpha.
+ marker.apply {
+ setIcon(BitmapDescriptorFactory.defaultMarker(random.nextFloat() * 360))
+ alpha = random.nextFloat()
+ }
+ }
+
+ // We return false to indicate that we have not consumed the event and that we wish
+ // for the default behavior to occur (which is for the camera to move such that the
+ // marker is centered and for the marker's info window to open, if it has one).
+ return false
+ }
+
+ override fun onInfoWindowClick(marker : Marker) {
+ Toast.makeText(this, "Click Info Window", Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onInfoWindowClose(marker : Marker) {
+ Toast.makeText(this, "Close Info Window", Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onInfoWindowLongClick(marker : Marker) {
+ Toast.makeText(this, "Info Window long click", Toast.LENGTH_SHORT).show()
+ }
+
+ override fun onMarkerDragStart(marker : Marker) {
+ topText.text = getString(R.string.on_marker_drag_start)
+ }
+
+ override fun onMarkerDragEnd(marker : Marker) {
+ topText.text = getString(R.string.on_marker_drag_end)
+ }
+
+ override fun onMarkerDrag(marker : Marker) {
+ topText.text = getString(R.string.on_marker_drag, marker.position.latitude, marker.position.longitude)
+ }
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt
new file mode 100644
index 00000000..f340a4ee
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import android.annotation.SuppressLint
+import android.os.Build
+import android.view.View
+import android.view.ViewTreeObserver.OnGlobalLayoutListener
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+
+/**
+ * Helper class that will delay triggering the OnMapReady callback until both the GoogleMap and the
+ * View having completed initialization. This is only necessary if a developer wishes to immediately
+ * invoke any method on the GoogleMap that also requires the View to have finished layout
+ * (ie. anything that needs to know the View's true size like snapshotting).
+ */
+class OnMapAndViewReadyListener(
+ private val mapFragment: SupportMapFragment,
+ private val toBeNotified: OnGlobalLayoutAndMapReadyListener
+ ) : OnGlobalLayoutListener,
+ OnMapReadyCallback {
+
+ private val mapView: View? = mapFragment.view
+
+ private var isViewReady = false
+ private var isMapReady = false
+ private var googleMap: GoogleMap? = null
+
+ /** A listener that needs to wait for both the GoogleMap and the View to be initialized. */
+ interface OnGlobalLayoutAndMapReadyListener {
+ fun onMapReady(googleMap: GoogleMap?)
+ }
+
+ init {
+ registerListeners()
+ }
+
+ private fun registerListeners() {
+ // View layout.
+ if (mapView?.width != 0 && mapView?.height != 0) {
+ // View has already completed layout.
+ isViewReady = true
+ } else {
+ // Map has not undergone layout, register a View observer.
+ mapView.viewTreeObserver.addOnGlobalLayoutListener(this)
+ }
+
+ // GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later.
+ mapFragment.getMapAsync(this)
+ }
+
+ override fun onMapReady(googleMap: GoogleMap) {
+ // NOTE: The GoogleMap API specifies the listener is removed just prior to invocation.
+ this.googleMap = googleMap
+ isMapReady = true
+ fireCallbackIfReady()
+ }
+
+ // We use the new method when supported
+ @SuppressLint("NewApi") // We check which build version we are using.
+ override fun onGlobalLayout() {
+ // Remove our listener.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ mapView?.viewTreeObserver?.removeGlobalOnLayoutListener(this)
+ } else {
+ mapView?.viewTreeObserver?.removeOnGlobalLayoutListener(this)
+ }
+ isViewReady = true
+ fireCallbackIfReady()
+ }
+
+ private fun fireCallbackIfReady() {
+ if (isViewReady && isMapReady) {
+ toBeNotified.onMapReady(googleMap)
+ }
+ }
+}
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/PolygonDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/PolygonDemoActivity.kt
new file mode 100644
index 00000000..e6ce289d
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/PolygonDemoActivity.kt
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.example.kotlindemos
+
+import android.graphics.Color
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.AdapterView
+import android.widget.SeekBar
+import android.widget.Spinner
+import android.widget.CheckBox
+import android.widget.ArrayAdapter
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.model.Dash
+import com.google.android.gms.maps.model.Dot
+import com.google.android.gms.maps.model.Gap
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.model.JointType
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.model.PatternItem
+import com.google.android.gms.maps.model.Polygon
+import com.google.android.gms.maps.model.PolygonOptions
+import com.google.android.gms.maps.SupportMapFragment
+
+import java.util.Arrays
+
+/**
+ * This shows how to draw polygons on a map.
+ */
+class PolygonDemoActivity :
+ AppCompatActivity(),
+ OnMapReadyCallback,
+ SeekBar.OnSeekBarChangeListener,
+ AdapterView.OnItemSelectedListener {
+
+ private val center = LatLng(-20.0, 130.0)
+ private val MAX_WIDTH_PX = 100
+ private val MAX_HUE_DEGREES = 360
+ private val MAX_ALPHA = 255
+ private val PATTERN_DASH_LENGTH_PX = 50
+ private val PATTERN_GAP_LENGTH_PX = 10
+ private val dot = Dot()
+ private val dash = Dash(PATTERN_DASH_LENGTH_PX.toFloat())
+ private val gap = Gap(PATTERN_GAP_LENGTH_PX.toFloat())
+ private val patternDotted = Arrays.asList(dot, gap)
+ private val patternDashed = Arrays.asList(dash, gap)
+ private val patternMixed = Arrays.asList(dot, gap, dot, dash, gap)
+
+ private lateinit var mutablePolygon: Polygon
+ private lateinit var fillHueBar: SeekBar
+ private lateinit var fillAlphaBar: SeekBar
+ private lateinit var strokeWidthBar: SeekBar
+ private lateinit var strokeHueBar: SeekBar
+ private lateinit var strokeAlphaBar: SeekBar
+ private lateinit var strokeJointTypeSpinner: Spinner
+ private lateinit var strokePatternSpinner: Spinner
+ private lateinit var clickabilityCheckbox: CheckBox
+
+ // These are the options for polygon stroke joints and patterns. We use their
+ // string resource IDs as identifiers.
+
+ private val jointTypeNameResourceIds = intArrayOf(R.string.joint_type_default, // Default
+ R.string.joint_type_bevel, R.string.joint_type_round)
+
+ private val patternTypeNameResourceIds = intArrayOf(R.string.pattern_solid, // Default
+ R.string.pattern_dashed, R.string.pattern_dotted, R.string.pattern_mixed)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.polygon_demo)
+
+ fillHueBar = findViewById(R.id.fillHueSeekBar).apply {
+ max = MAX_HUE_DEGREES
+ progress = MAX_HUE_DEGREES / 2
+ }
+
+ fillAlphaBar = findViewById(R.id.fillAlphaSeekBar).apply {
+ max = MAX_ALPHA
+ progress = MAX_ALPHA / 2
+ }
+
+ strokeWidthBar = findViewById(R.id.strokeWidthSeekBar).apply {
+ max = MAX_WIDTH_PX
+ progress = MAX_WIDTH_PX / 3
+ }
+
+ strokeHueBar = findViewById(R.id.strokeHueSeekBar).apply {
+ max = MAX_HUE_DEGREES
+ progress = 0
+ }
+
+ strokeAlphaBar = findViewById(R.id.strokeAlphaSeekBar).apply {
+ max = MAX_ALPHA
+ progress = MAX_ALPHA
+ }
+
+ strokeJointTypeSpinner = findViewById(R.id.strokeJointTypeSpinner).apply {
+ adapter = ArrayAdapter(
+ this@PolygonDemoActivity, android.R.layout.simple_spinner_item,
+ getResourceStrings(jointTypeNameResourceIds))
+ }
+
+ strokePatternSpinner = findViewById(R.id.strokePatternSpinner).apply {
+ adapter = ArrayAdapter(
+ this@PolygonDemoActivity, android.R.layout.simple_spinner_item,
+ getResourceStrings(patternTypeNameResourceIds))
+ }
+
+ clickabilityCheckbox = findViewById(R.id.toggleClickability)
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ mapFragment.getMapAsync(this)
+ }
+
+ private fun getResourceStrings(resourceIds: IntArray): List {
+ return resourceIds.map { getString(it) }
+ }
+
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // return early if the map was not initialised properly
+ googleMap ?: return
+
+ val fillColorArgb = Color.HSVToColor(
+ fillAlphaBar.progress, floatArrayOf(fillHueBar.progress.toFloat(), 1f, 1f))
+ val strokeColorArgb = Color.HSVToColor(
+ strokeAlphaBar.progress, floatArrayOf(strokeHueBar.progress.toFloat(), 1f, 1f))
+
+ with(googleMap) {
+ // Override the default content description on the view, for accessibility mode.
+ setContentDescription(getString(R.string.polygon_demo_description))
+ // Move the googleMap so that it is centered on the mutable polygon.
+ moveCamera(CameraUpdateFactory.newLatLngZoom(center, 4f))
+
+ // Create a rectangle with two rectangular holes.
+ mutablePolygon = addPolygon(PolygonOptions().apply {
+ addAll(createRectangle(center, 5.0, 5.0))
+ addHole(createRectangle(LatLng(-22.0, 128.0), 1.0, 1.0))
+ addHole(createRectangle(LatLng(-18.0, 133.0), 0.5, 1.5))
+ fillColor(fillColorArgb)
+ strokeColor(strokeColorArgb)
+ strokeWidth(strokeWidthBar.progress.toFloat())
+ clickable(clickabilityCheckbox.isChecked)
+ })
+
+ // Add a listener for polygon clicks that changes the clicked polygon's stroke color.
+ setOnPolygonClickListener { polygon ->
+ // Flip the red, green and blue components of the polygon's stroke color.
+ polygon.strokeColor = polygon.strokeColor xor 0x00ffffff
+ }
+ }
+
+ // set listeners on seekBars
+ arrayOf(fillHueBar, fillAlphaBar, strokeWidthBar, strokeHueBar, strokeAlphaBar).map {
+ it.setOnSeekBarChangeListener(this)
+ }
+
+ // set listeners on spinners
+ arrayOf(strokeJointTypeSpinner, strokePatternSpinner).map {
+ it.onItemSelectedListener = this
+ }
+
+ // set line pattern and joint type based on current spinner position
+ with(mutablePolygon) {
+ strokeJointType = getSelectedJointType(strokeJointTypeSpinner.selectedItemPosition)
+ strokePattern = getSelectedPattern(strokePatternSpinner.selectedItemPosition)
+ }
+
+ }
+
+ /**
+ * Creates a List of LatLngs that form a rectangle with the given dimensions.
+ */
+ private fun createRectangle(
+ center: LatLng,
+ halfWidth: Double,
+ halfHeight: Double
+ ): List {
+ return Arrays.asList(
+ LatLng(center.latitude - halfHeight, center.longitude - halfWidth),
+ LatLng(center.latitude - halfHeight, center.longitude + halfWidth),
+ LatLng(center.latitude + halfHeight, center.longitude + halfWidth),
+ LatLng(center.latitude + halfHeight, center.longitude - halfWidth),
+ LatLng(center.latitude - halfHeight, center.longitude - halfWidth))
+ }
+
+ private fun getSelectedJointType(pos: Int): Int {
+ return when (jointTypeNameResourceIds[pos]) {
+ R.string.joint_type_bevel -> JointType.BEVEL
+ R.string.joint_type_round -> JointType.ROUND
+ R.string.joint_type_default -> JointType.DEFAULT
+ else -> 0
+ }
+ }
+
+ private fun getSelectedPattern(pos: Int): List? {
+ return when (patternTypeNameResourceIds[pos]) {
+ R.string.pattern_solid -> null
+ R.string.pattern_dotted -> patternDotted
+ R.string.pattern_dashed -> patternDashed
+ R.string.pattern_mixed -> patternMixed
+ else -> null
+ }
+ }
+
+ /**
+ * Toggles the clickability of the polygon based on the state of the View that triggered this
+ * call.
+ * This callback is defined on the CheckBox in the layout for this Activity.
+ */
+ fun toggleClickability(view: View) {
+ if (view is CheckBox) {
+ mutablePolygon.isClickable = view.isChecked
+ }
+ }
+
+
+ /**
+ * Listener that is called when a seek bar is moved.
+ * Can change polygon fill color/transparency, stroke color/transparency and stroke width.
+ */
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int,
+ fromUser: Boolean) {
+
+ mutablePolygon.fillColor = when (seekBar) {
+ fillHueBar -> Color.HSVToColor(Color.alpha(mutablePolygon.fillColor),
+ floatArrayOf(progress.toFloat(), 1f, 1f))
+ fillAlphaBar -> {
+ val prevColor = mutablePolygon.fillColor
+ Color.argb(progress, Color.red(prevColor), Color.green(prevColor),
+ Color.blue(prevColor))
+ }
+ else -> mutablePolygon.fillColor
+ }
+
+ mutablePolygon.strokeColor = when (seekBar) {
+ strokeHueBar -> Color.HSVToColor(
+ Color.alpha(mutablePolygon.strokeColor),
+ floatArrayOf(progress.toFloat(), 1f, 1f))
+ strokeAlphaBar -> {
+ val prevColorArgb = mutablePolygon.strokeColor
+ Color.argb(progress, Color.red(prevColorArgb),
+ Color.green(prevColorArgb), Color.blue(prevColorArgb))
+ }
+ else -> mutablePolygon.strokeColor
+ }
+
+ if (seekBar == strokeWidthBar) mutablePolygon.strokeWidth = progress.toFloat()
+
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {
+ // do nothing
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {
+ // do nothing
+ }
+
+ /**
+ * Listener for when an item is selected using a spinner.
+ * Can change line pattern and joint type.
+ */
+ override fun onItemSelected(parent: AdapterView<*>?, view: View?, pos: Int,
+ id: Long) {
+ when (parent?.id) {
+ R.id.strokeJointTypeSpinner ->
+ mutablePolygon.strokeJointType = getSelectedJointType(pos)
+ R.id.strokePatternSpinner ->
+ mutablePolygon.strokePattern = getSelectedPattern(pos)
+ }
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>?) {
+ // don't do anything here
+ }
+
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/PolylineDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/PolylineDemoActivity.kt
new file mode 100644
index 00000000..37326aa8
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/PolylineDemoActivity.kt
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.OnMapReadyCallback
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.BitmapDescriptorFactory
+import com.google.android.gms.maps.model.ButtCap
+import com.google.android.gms.maps.model.Cap
+import com.google.android.gms.maps.model.CustomCap
+import com.google.android.gms.maps.model.Dash
+import com.google.android.gms.maps.model.Dot
+import com.google.android.gms.maps.model.Gap
+import com.google.android.gms.maps.model.JointType
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.PatternItem
+import com.google.android.gms.maps.model.Polyline
+import com.google.android.gms.maps.model.PolylineOptions
+import com.google.android.gms.maps.model.RoundCap
+import com.google.android.gms.maps.model.SquareCap
+import android.graphics.Color
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.CheckBox
+import android.widget.SeekBar
+import android.widget.Spinner
+import java.util.Arrays
+
+/**
+ * This shows how to draw polylines on a map.
+ */
+class PolylineDemoActivity :
+ AppCompatActivity(),
+ OnMapReadyCallback,
+ SeekBar.OnSeekBarChangeListener,
+ AdapterView.OnItemSelectedListener {
+
+ private val CUSTOM_CAP_IMAGE_REF_WIDTH_PX = 50
+ private val INITIAL_STROKE_WIDTH_PX = 5
+ private val MAX_WIDTH_PX = 100
+ private val MAX_HUE_DEGREES = 360
+ private val MAX_ALPHA = 255
+ private val PATTERN_DASH_LENGTH_PX = 50
+ private val PATTERN_GAP_LENGTH_PX = 20
+
+ // City locations for mutable polyline.
+ private val adelaideLatLng = LatLng(-34.92873, 138.59995)
+ private val darwinLatLng = LatLng(-12.4258647, 130.7932231)
+ private val melbourneLatLng = LatLng(-37.81319, 144.96298)
+ private val perthLatLng = LatLng(-31.95285, 115.85734)
+
+ // Airport locations for geodesic polyline.
+ private val aklLatLng = LatLng(-37.006254, 174.783018)
+ private val jfkLatLng = LatLng(40.641051, -73.777485)
+ private val laxLatLng = LatLng(33.936524, -118.377686)
+ private val lhrLatLng = LatLng(51.471547, -0.460052)
+
+ private val dot = Dot()
+ private val dash = Dash(PATTERN_DASH_LENGTH_PX.toFloat())
+ private val gap = Gap(PATTERN_GAP_LENGTH_PX.toFloat())
+ private val patternDotted = Arrays.asList(dot, gap)
+ private val patternDashed = Arrays.asList(dash, gap)
+ private val patternMixed = Arrays.asList(dot, gap, dot, dash, gap)
+
+ private lateinit var mutablePolyline: Polyline
+ private lateinit var hueBar: SeekBar
+ private lateinit var alphaBar: SeekBar
+ private lateinit var widthBar: SeekBar
+ private lateinit var startCapSpinner: Spinner
+ private lateinit var endCapSpinner: Spinner
+ private lateinit var jointTypeSpinner: Spinner
+ private lateinit var patternSpinner: Spinner
+ private lateinit var clickabilityCheckbox: CheckBox
+
+ // These are the options for polyline caps, joints and patterns. We use their
+ // string resource IDs as identifiers.
+ private val capTypeNameResourceIds = intArrayOf(R.string.cap_butt, // Default
+ R.string.cap_round, R.string.cap_square, R.string.cap_image)
+
+ private val jointTypeNameResourceIds = intArrayOf(R.string.joint_type_default, // Default
+ R.string.joint_type_bevel, R.string.joint_type_round)
+
+ private val patternTypeNameResourceIds = intArrayOf(R.string.pattern_solid, // Default
+ R.string.pattern_dashed, R.string.pattern_dotted, R.string.pattern_mixed)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.polyline_demo)
+
+ hueBar = findViewById(R.id.hueSeekBar).apply {
+ max = MAX_HUE_DEGREES
+ progress = 0
+ }
+
+ alphaBar = findViewById(R.id.alphaSeekBar).apply {
+ max = MAX_ALPHA
+ progress = MAX_ALPHA
+ }
+
+ widthBar = findViewById(R.id.widthSeekBar).apply {
+ max = MAX_WIDTH_PX
+ progress = MAX_WIDTH_PX / 2
+ }
+
+ startCapSpinner = findViewById(R.id.startCapSpinner).apply {
+ adapter = ArrayAdapter(
+ this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
+ getResourceStrings(capTypeNameResourceIds))
+ }
+
+ endCapSpinner = findViewById(R.id.endCapSpinner).apply {
+ adapter = ArrayAdapter(
+ this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
+ getResourceStrings(capTypeNameResourceIds))
+ }
+
+ jointTypeSpinner = findViewById(R.id.jointTypeSpinner).apply {
+ adapter = ArrayAdapter(
+ this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
+ getResourceStrings(jointTypeNameResourceIds))
+ }
+
+ patternSpinner = findViewById(R.id.patternSpinner).apply {
+ adapter = ArrayAdapter(
+ this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
+ getResourceStrings(patternTypeNameResourceIds))
+ }
+
+ clickabilityCheckbox = findViewById(R.id.toggleClickability)
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ mapFragment.getMapAsync(this)
+ }
+
+ private fun getResourceStrings(resourceIds: IntArray): List {
+ return resourceIds.map { getString(it) }
+ }
+
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // exit early if the map was not initialised properly
+ googleMap ?: return
+
+ with(googleMap) {
+ // Override the default content description on the view, for accessibility mode.
+ setContentDescription(getString(R.string.polyline_demo_description))
+
+ // A geodesic polyline that goes around the world.
+ addPolyline(PolylineOptions().apply {
+ add(lhrLatLng, aklLatLng, laxLatLng, jfkLatLng, lhrLatLng)
+ width(INITIAL_STROKE_WIDTH_PX.toFloat())
+ color(Color.BLUE)
+ geodesic(true)
+ clickable(clickabilityCheckbox.isChecked)
+ })
+
+ // Move the googleMap so that it is centered on the mutable polyline.
+ moveCamera(CameraUpdateFactory.newLatLngZoom(melbourneLatLng, 3f))
+
+ // Add a listener for polyline clicks that changes the clicked polyline's color.
+ setOnPolylineClickListener { polyline ->
+ // Flip the values of the red, green and blue components of the polyline's color.
+ polyline.color = polyline.color xor 0x00ffffff
+ }
+ }
+
+ // A simple polyline across Australia. This polyline will be mutable.
+ val color = Color.HSVToColor(
+ alphaBar.progress, floatArrayOf(hueBar.progress.toFloat(), 1f, 1f))
+
+ mutablePolyline = googleMap.addPolyline(PolylineOptions().apply{
+ color(color)
+ width(widthBar.progress.toFloat())
+ clickable(clickabilityCheckbox.isChecked)
+ add(melbourneLatLng, adelaideLatLng, perthLatLng, darwinLatLng)
+ })
+
+ arrayOf(hueBar, alphaBar, widthBar).map {
+ it.setOnSeekBarChangeListener(this)
+ }
+
+ arrayOf(startCapSpinner, endCapSpinner, jointTypeSpinner, patternSpinner).map {
+ it.onItemSelectedListener = this
+ }
+
+ with(mutablePolyline) {
+ startCap = getSelectedCap(startCapSpinner.selectedItemPosition) ?: ButtCap()
+ endCap = getSelectedCap(endCapSpinner.selectedItemPosition) ?: ButtCap()
+ jointType = getSelectedJointType(jointTypeSpinner.selectedItemPosition)
+ pattern = getSelectedPattern(patternSpinner.selectedItemPosition)
+ }
+
+ clickabilityCheckbox.setOnClickListener {
+ view -> mutablePolyline.isClickable = (view as CheckBox).isChecked
+ }
+ }
+
+ private fun getSelectedCap(pos: Int): Cap? {
+ return when (capTypeNameResourceIds[pos]) {
+ R.string.cap_butt -> ButtCap()
+ R.string.cap_square -> SquareCap()
+ R.string.cap_round -> RoundCap()
+ R.string.cap_image -> CustomCap(
+ BitmapDescriptorFactory.fromResource(R.drawable.chevron),
+ CUSTOM_CAP_IMAGE_REF_WIDTH_PX.toFloat())
+ else -> null
+ }
+ }
+
+ private fun getSelectedJointType(pos: Int): Int {
+ return when (jointTypeNameResourceIds[pos]) {
+ R.string.joint_type_bevel -> JointType.BEVEL
+ R.string.joint_type_round -> JointType.ROUND
+ R.string.joint_type_default -> JointType.DEFAULT
+ else -> 0
+ }
+ }
+
+ private fun getSelectedPattern(pos: Int): List? {
+ return when (patternTypeNameResourceIds[pos]) {
+ R.string.pattern_solid -> null
+ R.string.pattern_dotted -> patternDotted
+ R.string.pattern_dashed -> patternDashed
+ R.string.pattern_mixed -> patternMixed
+ else -> null
+ }
+ }
+
+ /**
+ * Listener for changes in a seekbar's position.
+ * Can change polyline color, width and transparency.
+ */
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+
+ when(seekBar) {
+ hueBar -> mutablePolyline.color = Color.HSVToColor(
+ Color.alpha(mutablePolyline.color), floatArrayOf(progress.toFloat(), 1f, 1f))
+ alphaBar -> {
+ val prevHSV = FloatArray(3)
+ Color.colorToHSV(mutablePolyline.color, prevHSV)
+ mutablePolyline.color = Color.HSVToColor(progress, prevHSV)
+ }
+ widthBar -> mutablePolyline.width = progress.toFloat()
+ }
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+ // Don't do anything here.
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+ // Don't do anything here.
+ }
+
+ /**
+ * Listener for changes in a spinner's position.
+ * Can change the polyline's start and end caps, pattern and joint type.
+ */
+ override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
+ when (parent.id) {
+ R.id.startCapSpinner -> mutablePolyline.startCap = getSelectedCap(pos) ?: ButtCap()
+ R.id.endCapSpinner -> mutablePolyline.endCap = getSelectedCap(pos) ?: ButtCap()
+ R.id.jointTypeSpinner -> mutablePolyline.jointType = getSelectedJointType(pos)
+ R.id.patternSpinner -> mutablePolyline.pattern = getSelectedPattern(pos)
+ }
+ }
+
+ override fun onNothingSelected(parent: AdapterView<*>) {
+ // Don't do anything here.
+ }
+
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TagsDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TagsDemoActivity.kt
new file mode 100644
index 00000000..d8006e80
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/TagsDemoActivity.kt
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import android.graphics.Color
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.widget.TextView
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.BitmapDescriptorFactory
+import com.google.android.gms.maps.model.Circle
+import com.google.android.gms.maps.model.CircleOptions
+import com.google.android.gms.maps.model.GroundOverlay
+import com.google.android.gms.maps.model.GroundOverlayOptions
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.google.android.gms.maps.model.Marker
+import com.google.android.gms.maps.model.MarkerOptions
+import com.google.android.gms.maps.model.Polygon
+import com.google.android.gms.maps.model.PolygonOptions
+import com.google.android.gms.maps.model.Polyline
+import com.google.android.gms.maps.model.PolylineOptions
+
+/**
+ * This shows how to use setTag/getTag on API objects.
+ */
+class TagsDemoActivity : AppCompatActivity(),
+ GoogleMap.OnCircleClickListener,
+ GoogleMap.OnGroundOverlayClickListener,
+ GoogleMap.OnMarkerClickListener,
+ OnMapAndViewReadyListener.OnGlobalLayoutAndMapReadyListener,
+ GoogleMap.OnPolygonClickListener,
+ GoogleMap.OnPolylineClickListener {
+
+ private lateinit var map: GoogleMap
+
+ private lateinit var tagText: TextView
+
+ private val places = mapOf(
+ "BRISBANE" to LatLng(-27.47093, 153.0235),
+ "MELBOURNE" to LatLng(-37.81319, 144.96298),
+ "DARWIN" to LatLng(-12.4634, 130.8456),
+ "SYDNEY" to LatLng(-33.87365, 151.20689),
+ "ADELAIDE" to LatLng(-34.92873, 138.59995),
+ "PERTH" to LatLng(-31.952854, 115.857342),
+ "ALICE_SPRINGS" to LatLng(-24.6980, 133.8807),
+ "HOBART" to LatLng(-42.8823388, 147.311042)
+ )
+
+ /**
+ * Class to store a tag to attach to a map object to keep track of
+ * how many times it has been clicked
+ */
+ private class CustomTag(private val description: String) {
+ private var clickCount: Int = 0
+
+ fun incrementClickCount() {
+ clickCount++
+ }
+
+ override fun toString() = "The $description has been clicked $clickCount times."
+
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.tags_demo)
+
+ tagText = findViewById(R.id.tag_text)
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ OnMapAndViewReadyListener(mapFragment, this)
+ }
+
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // return early if the map was not initialised properly
+ map = googleMap ?: return
+
+ // Add a circle, a ground overlay, a marker, a polygon and a polyline to the googleMap.
+ addObjectsToMap()
+
+ with(map.uiSettings) {
+ // Turn off the map toolbar.
+ isMapToolbarEnabled = false
+
+ // Disable interaction with the map - other than clicking.
+ isZoomControlsEnabled = false
+ isScrollGesturesEnabled = false
+ isZoomGesturesEnabled = false
+ isTiltGesturesEnabled = false
+ isRotateGesturesEnabled = false
+ }
+
+ with(map) {
+ // Set listeners for click events. See the bottom of this class for their behavior.
+ setOnCircleClickListener(this@TagsDemoActivity)
+ setOnGroundOverlayClickListener(this@TagsDemoActivity)
+ setOnMarkerClickListener(this@TagsDemoActivity)
+ setOnPolygonClickListener(this@TagsDemoActivity)
+ setOnPolylineClickListener(this@TagsDemoActivity)
+
+ // Override the default content description on the view, for accessibility mode.
+ // Ideally this string would be localised.
+ setContentDescription(getString(R.string.tags_demo_map_description))
+
+ // include all places we have markers for in the initial view of the map
+ val boundsBuilder = LatLngBounds.Builder()
+ places.keys.map { boundsBuilder.include(places.getValue(it)) }
+ // Move the camera to view all listed locations
+ moveCamera(CameraUpdateFactory.newLatLngBounds(boundsBuilder.build(), 100))
+
+ }
+ }
+
+ private fun addObjectsToMap() {
+ with(map) {
+
+ // A circle centered on Adelaide.
+ addCircle(CircleOptions().apply {
+ center(places.getValue("ADELAIDE"))
+ radius(500000.0)
+ fillColor(Color.argb(150, 66, 173, 244))
+ strokeColor(Color.rgb(66, 173, 244))
+ clickable(true)
+ }).run {
+ // add a tag to the circle to count clicks
+ tag = CustomTag("Adelaide circle")
+ }
+
+ // A ground overlay at Sydney.
+ addGroundOverlay(GroundOverlayOptions().apply {
+ image(BitmapDescriptorFactory.fromResource(R.drawable.harbour_bridge))
+ position(places.getValue("SYDNEY"), 700000f)
+ clickable(true)
+ }).run {
+ // add a tag to the overlay to count clicks
+ tag = CustomTag("Sydney ground overlay")
+ }
+
+ // A marker at Hobart.
+ addMarker(MarkerOptions().apply {
+ position(places.getValue("HOBART"))
+ }).run {
+ // add a tag to the marker to count clicks
+ tag = CustomTag("Hobart marker")
+ }
+
+ // A polygon centered at Darwin.
+ addPolygon(PolygonOptions().apply{
+ add(LatLng(places.getValue("DARWIN").latitude + 3,
+ places.getValue("DARWIN").longitude - 3),
+ LatLng(places.getValue("DARWIN").latitude + 3,
+ places.getValue("DARWIN").longitude + 3),
+ LatLng(places.getValue("DARWIN").latitude - 3,
+ places.getValue("DARWIN").longitude + 3),
+ LatLng(places.getValue("DARWIN").latitude - 3,
+ places.getValue("DARWIN").longitude - 3))
+ fillColor(Color.argb(150, 34, 173, 24))
+ strokeColor(Color.rgb(34, 173, 24))
+ clickable(true)
+ }).run {
+ // add a tag to the marker to count clicks
+ tag = CustomTag("Darwin polygon")
+ }
+
+ // A polyline from Perth to Brisbane.
+ addPolyline(PolylineOptions().apply{
+ add(places.getValue("PERTH"), places.getValue("BRISBANE"))
+ color(Color.rgb(103, 24, 173))
+ width(30f)
+ clickable(true)
+ }).run {
+ // add a tag to the polyline to count clicks
+ tag = CustomTag("Perth to Brisbane polyline")
+ }
+ }
+ }
+
+ // Click event listeners.
+ private fun onClick(tag: CustomTag) {
+ tag.incrementClickCount()
+ tagText.text = tag.toString()
+ }
+
+ override fun onCircleClick(circle: Circle) {
+ onClick(circle.tag as CustomTag)
+ }
+
+ override fun onGroundOverlayClick(groundOverlay: GroundOverlay) {
+ onClick(groundOverlay.tag as CustomTag)
+ }
+
+ override fun onMarkerClick(marker: Marker): Boolean {
+ onClick(marker.tag as CustomTag)
+ // We return true to indicate that we have consumed the event and that we do not wish
+ // for the default behavior to occur (which is for the camera to move such that the
+ // marker is centered and for the marker's info window to open, if it has one).
+ return true
+ }
+
+ override fun onPolygonClick(polygon: Polygon) {
+ onClick(polygon.tag as CustomTag)
+ }
+
+ override fun onPolylineClick(polyline: Polyline) {
+ onClick(polyline.tag as CustomTag)
+ }
+
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/VisibleRegionDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/VisibleRegionDemoActivity.kt
new file mode 100644
index 00000000..ed4138ec
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/VisibleRegionDemoActivity.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.kotlindemos
+
+import com.google.android.gms.maps.CameraUpdateFactory
+import com.google.android.gms.maps.GoogleMap
+import com.google.android.gms.maps.SupportMapFragment
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.LatLngBounds
+import com.google.android.gms.maps.model.MarkerOptions
+
+import android.os.Bundle
+import android.os.Handler
+import android.os.SystemClock
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.view.animation.OvershootInterpolator
+import android.widget.Button
+import android.widget.TextView
+
+/**
+ * This shows how to use setPadding to allow overlays that obscure part of the map without
+ * obscuring the map UI or copyright notices.
+ */
+class VisibleRegionDemoActivity :
+ AppCompatActivity(),
+ OnMapAndViewReadyListener.OnGlobalLayoutAndMapReadyListener {
+
+ private val operaHouseLatLng = LatLng(-33.85704, 151.21522)
+ private val sfoLatLng = LatLng(37.614631, -122.385153)
+ private val australiaBounds = LatLngBounds(LatLng(-44.0, 113.0),
+ LatLng(-10.0, 154.0))
+
+ private lateinit var map: GoogleMap
+
+ private lateinit var messageView: TextView
+ private lateinit var normalButton: Button
+ private lateinit var morePaddedButton: Button
+ private lateinit var operaHouseButton: Button
+ private lateinit var sfoButton: Button
+ private lateinit var australiaButton: Button
+
+ /** Keep track of current values for padding, so we can animate from them. */
+ private var currentLeft = 150
+ private var currentTop = 0
+ private var currentRight = 0
+ private var currentBottom = 0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.visible_region_demo)
+ messageView = findViewById(R.id.message_text)
+
+ val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
+ OnMapAndViewReadyListener(mapFragment, this)
+
+ normalButton = findViewById(R.id.vr_normal_button)
+ morePaddedButton = findViewById(R.id.vr_more_padded_button)
+ operaHouseButton = findViewById(R.id.vr_soh_button)
+ sfoButton = findViewById(R.id.vr_sfo_button)
+ australiaButton = findViewById(R.id.vr_aus_button)
+
+ }
+
+ override fun onMapReady(googleMap: GoogleMap?) {
+
+ // exit early if the map was not initialised properly
+ map = googleMap ?: return
+
+ map.apply{
+ // Set padding for the current camera view
+ setPadding(currentLeft, currentTop, currentRight, currentBottom)
+ // Move to a place with indoor (sfoLatLng airport).
+ moveCamera(CameraUpdateFactory.newLatLngZoom(sfoLatLng, 18f))
+ // Add a marker to the Opera House.
+ addMarker(MarkerOptions().position(operaHouseLatLng).title("Sydney Opera House"))
+ // Add a camera idle listener that displays the current camera position in a TextView
+ setOnCameraIdleListener {
+ messageView.text = getString(R.string.camera_change_message,
+ this@VisibleRegionDemoActivity.map.cameraPosition)
+ }
+ }
+
+ normalButton.setOnClickListener {
+ animatePadding(150, 0, 0, 0)
+ }
+
+ // listener for when the 'more' padding button is clicked
+ // increases the amount of padding along the right and bottom of the map
+ morePaddedButton.setOnClickListener {
+ // get the view that contains the map
+ val mapView: View? = supportFragmentManager.findFragmentById(R.id.map).view
+ animatePadding(150, 0, (mapView?.width ?: 0) / 3,
+ (mapView?.height ?: 0)/ 4)
+ }
+
+ operaHouseButton.setOnClickListener {
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(operaHouseLatLng, 16f))
+ }
+
+ sfoButton.setOnClickListener {
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(sfoLatLng, 18f))
+ }
+
+ australiaButton.setOnClickListener {
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0))
+ }
+ }
+
+ // this function smoothly changes the amount of padding over a period of time
+ private fun animatePadding(toLeft: Int, toTop: Int, toRight: Int, toBottom: Int) {
+
+ val handler = Handler()
+ val start = SystemClock.uptimeMillis()
+ val duration: Long = 1000
+
+ val interpolator = OvershootInterpolator()
+
+ val startLeft: Int = currentLeft
+ val startTop: Int = currentTop
+ val startRight: Int = currentRight
+ val startBottom: Int = currentBottom
+
+ currentLeft = toLeft
+ currentTop = toTop
+ currentRight = toRight
+ currentBottom = toBottom
+
+ handler.post(object : Runnable {
+ override fun run() {
+ val elapsed = SystemClock.uptimeMillis() - start
+ val t: Float = interpolator.getInterpolation(elapsed.toFloat() / duration)
+
+ val leftDiff = ((toLeft - startLeft) * t).toInt()
+ val topDiff = ((toTop - startTop) * t).toInt()
+ val rightDiff = ((toRight - startRight) * t).toInt()
+ val bottomDiff = ((toBottom - startBottom) * t).toInt()
+
+ val left = startLeft + leftDiff
+ val top = startTop + topDiff
+ val right = startRight + rightDiff
+ val bottom = startBottom + bottomDiff
+
+ map.setPadding(left, top, right, bottom)
+
+ // Post again 16ms later.
+ if (elapsed < duration) { handler.postDelayed(this, 16) }
+ }
+ })
+ }
+}
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/arrow.png b/ApiDemos/kotlin/app/src/main/res/drawable/arrow.png
new file mode 100644
index 00000000..77b3f5aa
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/arrow.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_nsw.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_nsw.png
new file mode 100644
index 00000000..3a121046
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/badge_nsw.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png
new file mode 100644
index 00000000..b85cf6c7
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_qld.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_qld.png
new file mode 100644
index 00000000..9787fb15
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/badge_qld.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png
new file mode 100644
index 00000000..11aa229f
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png
new file mode 100644
index 00000000..80db77e7
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_wa.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_wa.png
new file mode 100644
index 00000000..7c0dc76c
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/badge_wa.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/chevron.png b/ApiDemos/kotlin/app/src/main/res/drawable/chevron.png
new file mode 100644
index 00000000..259e277d
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/chevron.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/custom_info_bubble.9.png b/ApiDemos/kotlin/app/src/main/res/drawable/custom_info_bubble.9.png
new file mode 100644
index 00000000..b6bf33b7
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/custom_info_bubble.9.png differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/harbour_bridge.jpg b/ApiDemos/kotlin/app/src/main/res/drawable/harbour_bridge.jpg
new file mode 100644
index 00000000..f1312590
Binary files /dev/null and b/ApiDemos/kotlin/app/src/main/res/drawable/harbour_bridge.jpg differ
diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/ic_android.xml b/ApiDemos/kotlin/app/src/main/res/drawable/ic_android.xml
new file mode 100644
index 00000000..29d3c682
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/drawable/ic_android.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/camera_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/camera_demo.xml
new file mode 100644
index 00000000..53653d01
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/camera_demo.xml
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml
new file mode 100644
index 00000000..62a24813
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml
new file mode 100644
index 00000000..0cfe93c6
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/layers_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/layers_demo.xml
new file mode 100755
index 00000000..b2a79924
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/layers_demo.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml
new file mode 100644
index 00000000..3f1be3b0
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/polygon_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/polygon_demo.xml
new file mode 100644
index 00000000..2942d288
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/polygon_demo.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/polyline_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/polyline_demo.xml
new file mode 100644
index 00000000..b49a6736
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/polyline_demo.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/tags_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/tags_demo.xml
new file mode 100644
index 00000000..5010a553
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/tags_demo.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/main/res/layout/visible_region_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/visible_region_demo.xml
new file mode 100755
index 00000000..1228158a
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/layout/visible_region_demo.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/values/colors.xml b/ApiDemos/kotlin/app/src/main/res/values/colors.xml
index 3ab3e9cb..5169d545 100644
--- a/ApiDemos/kotlin/app/src/main/res/values/colors.xml
+++ b/ApiDemos/kotlin/app/src/main/res/values/colors.xml
@@ -3,4 +3,5 @@
#3F51B5#303F9F#FF4081
+ #D0FFFFFF
diff --git a/ApiDemos/kotlin/app/src/main/res/values/google_maps_api.xml b/ApiDemos/kotlin/app/src/main/res/values/google_maps_api.xml
new file mode 100644
index 00000000..1447fd86
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/main/res/values/google_maps_api.xml
@@ -0,0 +1,17 @@
+
+
+
+ "ADD_YOUR_KEY_HERE"
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/values/strings.xml b/ApiDemos/kotlin/app/src/main/res/values/strings.xml
index 22db8efe..053076be 100644
--- a/ApiDemos/kotlin/app/src/main/res/values/strings.xml
+++ b/ApiDemos/kotlin/app/src/main/res/values/strings.xml
@@ -18,4 +18,115 @@
Google Maps API DemosNo demosGoogle Play services is not installed on this device.
+
+
+ Animate
+ Demonstrates Camera Functions
+ Camera
+ \u2193
+ Custom Duration
+ Go to Bondi
+ Go to Sydney
+ \u2190
+ \u2192
+ \u25A0
+ \u2193
+ \u2191
+ \u2191
+ +
+ -
+
+
+ Tags
+ Demonstrates how to get and set tags on API objects.
+ Map with a circle, ground overlay, marker, polygon and polyline.
+
+
+ Clickable
+ Fill Alpha
+ Fill Hue
+ Bevel
+ Default
+ Round
+ Dashed
+ Dotted
+ Mixed
+ Solid
+ Demonstrates how to add Polygons to a map.
+ Demonstrates how to add Polygons to a map
+ Polygons
+ Properties for Polygon over Australia
+ Stroke Alpha
+ Stroke Hue
+ Stroke Joint Type
+ Stroke Pattern
+ Stroke Width
+
+
+ Custom Info Contents
+ Custom Info Window
+ Default Info Window
+ Drag Melbourne
+ Clear
+ Flat
+ Reset
+ Map is not ready
+ Demonstrates how to add Markers to a map.
+ Markers
+ onMarkerDrag. Current Position: (%1$f, %2$f)
+ Started dragging marker
+ Marker stopped dragging
+ Rotation
+ State badge
+
+
+ Buildings
+ Hybrid
+ Indoor
+
+ @string/normal
+ @string/hybrid
+ @string/satellite
+ @string/terrain
+ @string/none_map
+
+ Layers
+ Demonstrates the different map layers.
+ My Location
+ None
+ Normal
+ OK
+ Satellite
+ Terrain
+ Traffic
+
+
+ Access to the location service is required to demonstrate the \'my location\' feature, which shows your current location on the map.
+ This sample requires location permission to enable the \'my location\' layer. Please try again and grant access to use the location.\nIf the permission has been permanently denied, it can be enabled from the System Settings > Apps > \'Google Maps API Demos\'.
+ Location permission is required for this demo.
+
+
+ Alpha
+ Butt
+ Image
+ Round
+ Square
+ End Cap
+ Hue
+ Joint Type
+ Pattern
+ Demonstrates how to add Polylines to a map
+ Polyline
+ Start Cap
+ Width
+
+
+ CameraChangeListener: %1$s
+ Visible Regions
+ Demonstrates how to use Visible Regions.
+ Normal
+ More
+ SOH
+ SFO
+ AUS
\ No newline at end of file