Merge pull request #87 from katherineallen/master

Added samples to Kotlin project
This commit is contained in:
Stephen McDonald 2018-02-15 12:32:53 +11:00 committed by GitHub
commit b03a3e25e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 3154 additions and 2 deletions

View File

@ -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'
}

View File

@ -16,6 +16,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kotlindemos">
<!-- This app requires location permissions for the layers demo.
The user's current location is displayed using the 'My Location' layer. -->
<!-- Access to location is needed for the UI Settings Demo -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
@ -23,6 +28,9 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/google_maps_key" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -30,6 +38,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MarkerDemoActivity"/>
<activity android:name=".LayersDemoActivity"/>
<activity android:name=".PolygonDemoActivity"/>
<activity android:name=".CameraDemoActivity"/>
<activity android:name=".PolylineDemoActivity"/>
<activity android:name=".TagsDemoActivity"/>
<activity android:name=".VisibleRegionDemoActivity"/>
</application>
</manifest>

View File

@ -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)
}
}

View File

@ -21,6 +21,21 @@ package com.example.kotlindemos
*/
class DemoDetailsList {
companion object {
val DEMOS = listOf<DemoDetails>()
val DEMOS = listOf<DemoDetails>(
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)
)
}
}

View File

@ -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<Spinner>(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<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
EasyPermissions.onRequestPermissionsResult(requestCode,
permissions, grantResults, this)
}
override fun onPermissionsDenied(requestCode: Int, list: List<String>) {
// 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<String>) {
// 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.
}
}

View File

@ -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<Marker>()
/** 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<ImageView>(R.id.badge).setImageResource(badge)
// Set the title and snippet for the custom info window
val title: String? = marker.title
val titleUi = view.findViewById<TextView>(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<TextView>(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<SeekBar>(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<RadioGroup>(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)
}
}

View File

@ -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)
}
}
}

View File

@ -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<SeekBar>(R.id.fillHueSeekBar).apply {
max = MAX_HUE_DEGREES
progress = MAX_HUE_DEGREES / 2
}
fillAlphaBar = findViewById<SeekBar>(R.id.fillAlphaSeekBar).apply {
max = MAX_ALPHA
progress = MAX_ALPHA / 2
}
strokeWidthBar = findViewById<SeekBar>(R.id.strokeWidthSeekBar).apply {
max = MAX_WIDTH_PX
progress = MAX_WIDTH_PX / 3
}
strokeHueBar = findViewById<SeekBar>(R.id.strokeHueSeekBar).apply {
max = MAX_HUE_DEGREES
progress = 0
}
strokeAlphaBar = findViewById<SeekBar>(R.id.strokeAlphaSeekBar).apply {
max = MAX_ALPHA
progress = MAX_ALPHA
}
strokeJointTypeSpinner = findViewById<Spinner>(R.id.strokeJointTypeSpinner).apply {
adapter = ArrayAdapter(
this@PolygonDemoActivity, android.R.layout.simple_spinner_item,
getResourceStrings(jointTypeNameResourceIds))
}
strokePatternSpinner = findViewById<Spinner>(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<String> {
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<LatLng> {
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<PatternItem>? {
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
}
}

View File

@ -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<SeekBar>(R.id.hueSeekBar).apply {
max = MAX_HUE_DEGREES
progress = 0
}
alphaBar = findViewById<SeekBar>(R.id.alphaSeekBar).apply {
max = MAX_ALPHA
progress = MAX_ALPHA
}
widthBar = findViewById<SeekBar>(R.id.widthSeekBar).apply {
max = MAX_WIDTH_PX
progress = MAX_WIDTH_PX / 2
}
startCapSpinner = findViewById<Spinner>(R.id.startCapSpinner).apply {
adapter = ArrayAdapter(
this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
getResourceStrings(capTypeNameResourceIds))
}
endCapSpinner = findViewById<Spinner>(R.id.endCapSpinner).apply {
adapter = ArrayAdapter(
this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
getResourceStrings(capTypeNameResourceIds))
}
jointTypeSpinner = findViewById<Spinner>(R.id.jointTypeSpinner).apply {
adapter = ArrayAdapter(
this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
getResourceStrings(jointTypeNameResourceIds))
}
patternSpinner = findViewById<Spinner>(R.id.patternSpinner).apply {
adapter = ArrayAdapter(
this@PolylineDemoActivity, android.R.layout.simple_spinner_item,
getResourceStrings(patternTypeNameResourceIds))
}
clickabilityCheckbox = findViewById<CheckBox>(R.id.toggleClickability)
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
}
private fun getResourceStrings(resourceIds: IntArray): List<String> {
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<PatternItem>? {
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.
}
}

View File

@ -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)
}
}

View File

@ -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) }
}
})
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -0,0 +1,8 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FF000000" android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z"/>
</vector>

View File

@ -0,0 +1,195 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/stop_animation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onStopAnimation"
android:text="@string/stop_animation" />
<ToggleButton
android:id="@+id/animate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:onClick="onToggleAnimate"
android:textOn="@string/animate"
android:textOff="@string/animate" />
</LinearLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:layout_weight="1">
<Button
android:id="@+id/scroll_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:onClick="onScrollLeft"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:text="@string/left_arrow"
android:layout_alignParentStart="true" />
<Button
android:id="@+id/scroll_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:onClick="onScrollUp"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/scroll_left"
android:text="@string/up_arrow"
android:layout_toEndOf="@id/scroll_left" />
<Button
android:id="@+id/scroll_down"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:onClick="onScrollDown"
android:layout_below="@id/scroll_up"
android:layout_toRightOf="@id/scroll_left"
android:text="@string/down_arrow"
android:layout_toEndOf="@id/scroll_left" />
<Button
android:id="@+id/scroll_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:onClick="onScrollRight"
android:layout_centerVertical="true"
android:layout_toRightOf="@id/scroll_down"
android:text="@string/right_arrow"
android:layout_toEndOf="@id/scroll_down" />
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end"
android:orientation="vertical">
<Button
android:id="@+id/zoom_in"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:onClick="onZoomIn"
android:text="@string/zoom_in" />
<Button
android:id="@+id/zoom_out"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:onClick="onZoomOut"
android:text="@string/zoom_out" />
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_gravity="end">
<Button
android:id="@+id/tilt_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:text="@string/tilt_more"
android:onClick="onTiltMore" />
<Button
android:id="@+id/tilt_less"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:text="@string/tilt_less"
android:onClick="onTiltLess" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/duration_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onToggleCustomDuration"
android:text="@string/duration" />
<SeekBar
android:id="@+id/duration_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:max="5000" />
</LinearLayout>
<LinearLayout
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/sydney"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="onGoToSydney"
android:layout_weight="0.5"
android:text="@string/go_to_sydney"
style="?android:attr/borderlessButtonStyle"/>
<Button
android:id="@+id/bondi"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:onClick="onGoToBondi"
android:layout_weight="0.5"
android:text="@string/go_to_bondi"
style="?android:attr/borderlessButtonStyle"/>
</LinearLayout>
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
</LinearLayout>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/badge"
android:contentDescription="@string/state_badge_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:adjustViewBounds="true"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff000000"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff7f7f7f"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/custom_info_bubble">
<ImageView
android:id="@+id/badge"
android:contentDescription="@string/state_badge_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:adjustViewBounds="true"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff000000"
android:textSize="14sp"
android:textStyle="bold" />
<TextView
android:id="@+id/snippet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="#ff7f7f7f"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
<!-- A set of test checkboxes. -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignTop="@id/map"
android:padding="6dp"
android:background="@color/white"
android:orientation="vertical">
<Spinner
android:id="@+id/layers_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/traffic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/traffic" />
<CheckBox
android:id="@+id/my_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="@string/my_location" />
<CheckBox
android:id="@+id/buildings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/buildings" />
<CheckBox
android:id="@+id/indoor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/indoor" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/top_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="2"
android:text="@string/drag_melbourne" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/flat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/flat"
android:textSize="14sp"
android:onClick="onToggleFlat" />
<TextView
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rotation" />
<SeekBar
android:id="@+id/rotationSeekBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:background="@color/white"
android:orientation="vertical"
android:padding="5dp">
<LinearLayout
style="?android:attr/buttonBarButtonStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:onClick="onClearMap"
android:text="@string/clear_map"
style="?android:attr/borderlessButtonStyle"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:onClick="onResetMap"
android:text="@string/reset_map"
style="?android:attr/borderlessButtonStyle"/>
</LinearLayout>
<RadioGroup
android:id="@+id/custom_info_window_options"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/default_info_window"
android:checked="true"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:text="@string/default_info_window" />
<RadioButton
android:id="@+id/custom_info_contents"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:text="@string/custom_info_contents" />
<RadioButton
android:id="@+id/custom_info_window"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:text="@string/custom_info_window" />
</RadioGroup>
</LinearLayout>
</FrameLayout>
</LinearLayout>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/properties_australia_polygon" />
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="1">
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/fill_hue" />
<SeekBar android:id="@+id/fillHueSeekBar" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/fill_alpha" />
<SeekBar android:id="@+id/fillAlphaSeekBar" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/stroke_width" />
<SeekBar android:id="@+id/strokeWidthSeekBar" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/stroke_hue" />
<SeekBar android:id="@+id/strokeHueSeekBar" />
</TableRow>
<TableRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/stroke_alpha" />
<SeekBar android:id="@+id/strokeAlphaSeekBar" />
</TableRow>
</TableLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/stroke_joint_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Spinner
android:id="@+id/strokeJointTypeSpinner"
android:spinnerMode="dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@string/stroke_pattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Spinner
android:id="@+id/strokePatternSpinner"
android:spinnerMode="dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<CheckBox
android:id="@+id/toggleClickability"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:onClick="toggleClickability"
android:text="@string/clickable" />
<fragment
android:id="@+id/map"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="1">
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/hue" />
<SeekBar android:id="@+id/hueSeekBar" />
</TableRow>
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/alpha" />
<SeekBar android:id="@+id/alphaSeekBar" />
</TableRow>
<TableRow
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical">
<TextView android:text="@string/width" />
<SeekBar android:id="@+id/widthSeekBar" />
</TableRow>
</TableLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/start_cap"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Spinner
android:id="@+id/startCapSpinner"
android:spinnerMode="dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@string/end_cap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Spinner
android:id="@+id/endCapSpinner"
android:spinnerMode="dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/joint_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Spinner
android:id="@+id/jointTypeSpinner"
android:spinnerMode="dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text="@string/pattern"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Spinner
android:id="@+id/patternSpinner"
android:spinnerMode="dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
<CheckBox
android:id="@+id/toggleClickability"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/clickable" />
<fragment
android:id="@+id/map"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tag_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>
</LinearLayout>

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?><!--
Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--><!-- This can go anywhere in your layout (see other demos for some examples). -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/message_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment" />
<LinearLayout
android:layout_gravity="bottom|right"
android:background="#A000"
android:orientation="vertical"
android:padding="5dp"
android:layout_height="match_parent"
android:layout_width="150px">
<Button
android:id="@+id/vr_normal_button"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setNoPadding"
android:text="@string/vr_normal_button" />
<Button
android:id="@+id/vr_more_padded_button"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setMorePadding"
android:text="@string/vr_more_padded_button" />
<Button
android:id="@+id/vr_soh_button"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="moveToOperaHouse"
android:text="@string/vr_opera_house_button" />
<Button
android:id="@+id/vr_sfo_button"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="moveToSFO"
android:text="@string/vr_sfo_button" />
<Button
android:id="@+id/vr_aus_button"
android:padding="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="moveToAUS"
android:text="@string/vr_australia_button" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>

View File

@ -3,4 +3,5 @@
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="white">#D0FFFFFF</color>
</resources>

View File

@ -0,0 +1,17 @@
<resources>
<!--
TODO: Before you run your application, you need a Google Maps API key.
See this page for more information:
https://developers.google.com/maps/documentation/android/start#get_an_android_certificate_and_the_google_maps_api_key
Once you have your key (it starts with "AIza"), replace the "google_maps_key"
string in this file.
Note: This resource is used for the debug build target. Update this file if you just want to run
the demo app.
-->
<string name="google_maps_key" translatable="false" templateMergeStrategy="preserve">
"ADD_YOUR_KEY_HERE"
</string>
</resources>

View File

@ -18,4 +18,115 @@
<string name="demo_title">Google Maps API Demos</string>
<string name="no_demos">No demos</string>
<string name="play_services_not_installed">Google Play services is not installed on this device.</string>
<!-- Camera Demo -->
<string name="animate">Animate</string>
<string name="camera_demo_description">Demonstrates Camera Functions</string>
<string name="camera_demo_label">Camera</string>
<string name="down_arrow">\u2193</string>
<string name="duration">Custom Duration</string>
<string name="go_to_bondi">Go to Bondi</string>
<string name="go_to_sydney">Go to Sydney</string>
<string name="left_arrow">\u2190</string>
<string name="right_arrow">\u2192</string>
<string name="stop_animation">\u25A0</string>
<string name="tilt_less">\u2193</string>
<string name="tilt_more">\u2191</string>
<string name="up_arrow">\u2191</string>
<string name="zoom_in">+</string>
<string name="zoom_out">-</string>
<!-- Tags Demo -->
<string name="tags_demo_label">Tags</string>
<string name="tags_demo_details">Demonstrates how to get and set tags on API objects.</string>
<string name="tags_demo_map_description">Map with a circle, ground overlay, marker, polygon and polyline.</string>
<!-- Polygon Demo -->
<string name="clickable">Clickable</string>
<string name="fill_alpha">Fill Alpha</string>
<string name="fill_hue">Fill Hue</string>
<string name="joint_type_bevel">Bevel</string>
<string name="joint_type_default">Default</string>
<string name="joint_type_round">Round</string>
<string name="pattern_dashed">Dashed</string>
<string name="pattern_dotted">Dotted</string>
<string name="pattern_mixed">Mixed</string>
<string name="pattern_solid">Solid</string>
<string name="polygon_demo_description">Demonstrates how to add Polygons to a map.</string>
<string name="polygon_demo_details">Demonstrates how to add Polygons to a map</string>
<string name="polygon_demo_label">Polygons</string>
<string name="properties_australia_polygon">Properties for Polygon over Australia</string>
<string name="stroke_alpha">Stroke Alpha</string>
<string name="stroke_hue">Stroke Hue</string>
<string name="stroke_joint_type">Stroke Joint Type</string>
<string name="stroke_pattern">Stroke Pattern</string>
<string name="stroke_width">Stroke Width</string>
<!-- Markers Demo -->
<string name="custom_info_contents">Custom Info Contents</string>
<string name="custom_info_window">Custom Info Window</string>
<string name="default_info_window">Default Info Window</string>
<string name="drag_melbourne">Drag Melbourne</string>
<string name="clear_map">Clear</string>
<string name="flat">Flat</string>
<string name="reset_map">Reset</string>
<string name="map_not_ready">Map is not ready</string>
<string name="markers_demo_description">Demonstrates how to add Markers to a map.</string>
<string name="markers_demo_label">Markers</string>
<string name="on_marker_drag">onMarkerDrag. Current Position: (%1$f, %2$f)</string>
<string name="on_marker_drag_start">Started dragging marker</string>
<string name="on_marker_drag_end">Marker stopped dragging</string>
<string name="rotation">Rotation</string>
<string name="state_badge_label">State badge</string>
<!-- Layers Demo -->
<string name="buildings">Buildings</string>
<string name="hybrid">Hybrid</string>
<string name="indoor">Indoor</string>
<string-array name="layers_array">
<item>@string/normal</item>
<item>@string/hybrid</item>
<item>@string/satellite</item>
<item>@string/terrain</item>
<item>@string/none_map</item>
</string-array>
<string name="layers_demo_label">Layers</string>
<string name="layers_demo_description">Demonstrates the different map layers.</string>
<string name="my_location">My Location</string>
<string name="none_map">None</string>
<string name="normal">Normal</string>
<string name="ok">OK</string>
<string name="satellite">Satellite</string>
<string name="terrain">Terrain</string>
<string name="traffic">Traffic</string>
<!-- Permissions -->
<string name="permission_rationale_location">Access to the location service is required to demonstrate the \'my location\' feature, which shows your current location on the map.</string>
<string name="location_permission_denied">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 &gt; Apps &gt; \'Google Maps API Demos\'.</string>
<string name="permission_required_toast">Location permission is required for this demo.</string>
<!-- Polylines Demo -->
<string name="alpha">Alpha</string>
<string name="cap_butt">Butt</string>
<string name="cap_image">Image</string>
<string name="cap_round">Round</string>
<string name="cap_square">Square</string>
<string name="end_cap">End Cap</string>
<string name="hue">Hue</string>
<string name="joint_type">Joint Type</string>
<string name="pattern">Pattern</string>
<string name="polyline_demo_description">Demonstrates how to add Polylines to a map</string>
<string name="polyline_demo_label">Polyline</string>
<string name="start_cap">Start Cap</string>
<string name="width">Width</string>
<!-- Visible Regions Demo -->
<string name="camera_change_message">CameraChangeListener: %1$s</string>
<string name="region_demo_label">Visible Regions</string>
<string name="region_demo_details">Demonstrates how to use Visible Regions.</string>
<string name="vr_normal_button">Normal</string>
<string name="vr_more_padded_button">More</string>
<string name="vr_opera_house_button">SOH</string>
<string name="vr_sfo_button">SFO</string>
<string name="vr_australia_button">AUS</string>
</resources>