Merge pull request #87 from katherineallen/master
Added samples to Kotlin project
@ -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'
|
||||
}
|
||||
@ -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>
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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.
|
||||
}
|
||||
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@ -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.
|
||||
}
|
||||
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@ -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) }
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
BIN
ApiDemos/kotlin/app/src/main/res/drawable/arrow.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_nsw.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_qld.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/badge_wa.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/chevron.png
Normal file
|
After Width: | Height: | Size: 675 B |
|
After Width: | Height: | Size: 1.2 KiB |
BIN
ApiDemos/kotlin/app/src/main/res/drawable/harbour_bridge.jpg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
8
ApiDemos/kotlin/app/src/main/res/drawable/ic_android.xml
Normal 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>
|
||||
195
ApiDemos/kotlin/app/src/main/res/layout/camera_demo.xml
Normal 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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
70
ApiDemos/kotlin/app/src/main/res/layout/layers_demo.xml
Executable 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>
|
||||
120
ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml
Normal 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>
|
||||
115
ApiDemos/kotlin/app/src/main/res/layout/polygon_demo.xml
Normal 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>
|
||||
117
ApiDemos/kotlin/app/src/main/res/layout/polyline_demo.xml
Normal 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>
|
||||
32
ApiDemos/kotlin/app/src/main/res/layout/tags_demo.xml
Normal 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>
|
||||
85
ApiDemos/kotlin/app/src/main/res/layout/visible_region_demo.xml
Executable 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>
|
||||
@ -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>
|
||||
|
||||
17
ApiDemos/kotlin/app/src/main/res/values/google_maps_api.xml
Normal 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>
|
||||
@ -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 > Apps > \'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>
|
||||