From 9048d38339d5c7873bd74f03f8325d657e5d20cf Mon Sep 17 00:00:00 2001 From: Katherine Allen Date: Tue, 9 Jan 2018 15:10:19 +1100 Subject: [PATCH 1/9] added kotlin marker demo and associated files --- ApiDemos/kotlin/app/build.gradle | 3 + .../kotlin/app/src/main/AndroidManifest.xml | 4 + .../example/kotlindemos/DemoDetailsList.kt | 5 +- .../example/kotlindemos/MarkerDemoActivity.kt | 493 ++++++++++++++++++ .../kotlindemos/OnMapAndViewReadyListener.kt | 93 ++++ .../app/src/main/res/drawable/arrow.png | Bin 0 -> 1649 bytes .../app/src/main/res/drawable/badge_nsw.png | Bin 0 -> 2342 bytes .../app/src/main/res/drawable/badge_nt.png | Bin 0 -> 2807 bytes .../app/src/main/res/drawable/badge_qld.png | Bin 0 -> 2566 bytes .../app/src/main/res/drawable/badge_sa.png | Bin 0 -> 2645 bytes .../src/main/res/drawable/badge_victoria.png | Bin 0 -> 2494 bytes .../app/src/main/res/drawable/badge_wa.png | Bin 0 -> 1588 bytes .../res/drawable/custom_info_bubble.9.png | Bin 0 -> 1273 bytes .../app/src/main/res/drawable/ic_android.xml | 8 + .../main/res/layout/custom_info_contents.xml | 53 ++ .../main/res/layout/custom_info_window.xml | 54 ++ .../app/src/main/res/layout/marker_demo.xml | 110 ++++ .../kotlin/app/src/main/res/values/colors.xml | 1 + .../src/main/res/values/google_maps_api.xml | 17 + .../app/src/main/res/values/strings.xml | 15 + 20 files changed, 855 insertions(+), 1 deletion(-) create mode 100644 ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt create mode 100644 ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/arrow.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/badge_nsw.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/badge_qld.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/badge_wa.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/custom_info_bubble.9.png create mode 100644 ApiDemos/kotlin/app/src/main/res/drawable/ic_android.xml create mode 100644 ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml create mode 100644 ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml create mode 100644 ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml create mode 100644 ApiDemos/kotlin/app/src/main/res/values/google_maps_api.xml diff --git a/ApiDemos/kotlin/app/build.gradle b/ApiDemos/kotlin/app/build.gradle index d7aba0ca..5599302a 100644 --- a/ApiDemos/kotlin/app/build.gradle +++ b/ApiDemos/kotlin/app/build.gradle @@ -22,6 +22,7 @@ android { } } + dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" @@ -30,5 +31,7 @@ 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' } \ No newline at end of file diff --git a/ApiDemos/kotlin/app/src/main/AndroidManifest.xml b/ApiDemos/kotlin/app/src/main/AndroidManifest.xml index 2d16c4c9..eb549433 100644 --- a/ApiDemos/kotlin/app/src/main/AndroidManifest.xml +++ b/ApiDemos/kotlin/app/src/main/AndroidManifest.xml @@ -23,6 +23,9 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> + @@ -30,6 +33,7 @@ + \ No newline at end of file diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt index 901f7f4e..03109440 100644 --- a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt +++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/DemoDetailsList.kt @@ -21,6 +21,9 @@ package com.example.kotlindemos */ class DemoDetailsList { companion object { - val DEMOS = listOf() + val DEMOS = listOf( + DemoDetails(R.string.markers_demo_label, R.string.markers_demo_description, + MarkerDemoActivity::class.java) + ) } } \ No newline at end of file diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt new file mode 100644 index 00000000..13e23a31 --- /dev/null +++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt @@ -0,0 +1,493 @@ +/* + * 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 { + + /** This is ok to be lateinit as it is initialised in onMapReady */ + private lateinit var map: GoogleMap + + /** + * Keeps track of the last selected marker (though it may no longer be selected). This is + * useful for refreshing the info window. + * + * Must be nullable as it is null when no marker has been selected + */ + private var lastSelectedMarker: Marker? = null + + private val markerRainbow = ArrayList() + + /** map to store place names and locations */ + private val places = mapOf( + "BRISBANE" to LatLng(-27.47093, 153.0235), + "MELBOURNE" to LatLng(-37.81319, 144.96298), + "DARWIN" to LatLng(-12.4634, 130.8456), + "SYDNEY" to LatLng(-33.87365, 151.20689), + "ADELAIDE" to LatLng(-34.92873, 138.59995), + "PERTH" to LatLng(-31.952854, 115.857342), + "ALICE_SPRINGS" to LatLng(-24.6980, 133.8807) + ) + + /** These can be lateinit as they are set in onCreate */ + private lateinit var topText: TextView + private lateinit var rotationBar: SeekBar + private lateinit var flatBox: CheckBox + private lateinit var options: RadioGroup + + private val random = Random() + + private val TAG = MarkerDemoActivity::class.java.name + + /** Demonstrates customizing the info window and/or its contents. */ + internal inner class CustomInfoWindowAdapter : InfoWindowAdapter { + + // These are both viewgroups containing an ImageView with id "badge" and two + // TextViews with id "title" and "snippet". + private val window: View = layoutInflater.inflate(R.layout.custom_info_window, null) + private val contents: View = layoutInflater.inflate(R.layout.custom_info_contents, null) + + override fun getInfoWindow(marker: Marker): View? { + if (options.checkedRadioButtonId != R.id.custom_info_window) { + // This means that getInfoContents will be called. + return null + } + render(marker, window) + return window + } + + override fun getInfoContents(marker: Marker): View? { + if (options.checkedRadioButtonId != R.id.custom_info_contents) { + // This means that the default info contents will be used. + return null + } + render(marker, contents) + return contents + } + + private fun render(marker: Marker, view: View) { + val badge = when (marker.title) { + "Brisbane" -> R.drawable.badge_qld + "Adelaide" -> R.drawable.badge_sa + "Sydney" -> R.drawable.badge_nsw + "Melbourne" -> R.drawable.badge_victoria + "Perth" -> R.drawable.badge_wa + in "Darwin Marker 1".."Darwin Marker 4" -> R.drawable.badge_nt + else -> 0 // Passing 0 to setImageResource will clear the image view. + } + + view.findViewById(R.id.badge).setImageResource(badge) + + // Set the title and snippet for the custom info window + val title: String? = marker.title + val titleUi = view.findViewById(R.id.title) + + if (title != null) { + // Spannable string allows us to edit the formatting of the text. + titleUi.text = SpannableString(title).apply { + setSpan(ForegroundColorSpan(Color.RED), 0, length, 0) + } + } else { + titleUi.text = "" + } + + val snippet: String? = marker.snippet + val snippetUi = view.findViewById(R.id.snippet) + if (snippet != null && snippet.length > 12) { + snippetUi.text = SpannableString(snippet).apply { + setSpan(ForegroundColorSpan(Color.MAGENTA), 0, 10, 0) + setSpan(ForegroundColorSpan(Color.BLUE), 12, snippet.length, 0) + } + } else { + snippetUi.text = "" + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.marker_demo) + + topText = findViewById(R.id.top_text) + + rotationBar = findViewById(R.id.rotationSeekBar).apply { + max = 360 + setOnSeekBarChangeListener(object: OnSeekBarChangeListener { + + /** Called when the Rotation progress bar is moved */ + override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { + val rotation = seekBar?.progress?.toFloat() + checkReadyThen { markerRainbow.map { it.rotation = rotation ?: 0F } } + } + + override fun onStartTrackingTouch(p0: SeekBar?) { + // do nothing + } + + override fun onStopTrackingTouch(p0: SeekBar?) { + //do nothing + } + + } ) + } + + + + flatBox = findViewById(R.id.flat) + + options = findViewById(R.id.custom_info_window_options).apply { + setOnCheckedChangeListener { _, _ -> + if (lastSelectedMarker?.isInfoWindowShown == true) { + // Refresh the info window when the info window's content has changed. + // must deal with the possibility that lastSelectedMarker has changed in + // another thread between the null check and this line, do this with !! + lastSelectedMarker?.showInfoWindow() + } + } + } + + val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment + OnMapAndViewReadyListener(mapFragment, this) + } + + /** + * This is the callback that is triggered when the GoogleMap has loaded and is ready for use + */ + override fun onMapReady(googleMap: GoogleMap?) { + + // exit early if the map was not initialised properly + if (googleMap == null) 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() + + map = googleMap.apply { + // 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() + .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 lamdba 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. */ + fun onClearMap(view: View) { + checkReadyThen { map.clear() } + } + + /** Called when the Reset button is clicked. */ + fun onResetMap(view: View) { + checkReadyThen { + map.clear() + addMarkersToMap() + } + } + + /** Called when the Flat check box is checked or unchecked */ + 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 = "onMarkerDrag. Current Position: ${marker.position}" + } +} \ No newline at end of file diff --git a/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt new file mode 100644 index 00000000..f340a4ee --- /dev/null +++ b/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/OnMapAndViewReadyListener.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2018 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.kotlindemos + +import android.annotation.SuppressLint +import android.os.Build +import android.view.View +import android.view.ViewTreeObserver.OnGlobalLayoutListener +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.OnMapReadyCallback +import com.google.android.gms.maps.SupportMapFragment + +/** + * Helper class that will delay triggering the OnMapReady callback until both the GoogleMap and the + * View having completed initialization. This is only necessary if a developer wishes to immediately + * invoke any method on the GoogleMap that also requires the View to have finished layout + * (ie. anything that needs to know the View's true size like snapshotting). + */ +class OnMapAndViewReadyListener( + private val mapFragment: SupportMapFragment, + private val toBeNotified: OnGlobalLayoutAndMapReadyListener + ) : OnGlobalLayoutListener, + OnMapReadyCallback { + + private val mapView: View? = mapFragment.view + + private var isViewReady = false + private var isMapReady = false + private var googleMap: GoogleMap? = null + + /** A listener that needs to wait for both the GoogleMap and the View to be initialized. */ + interface OnGlobalLayoutAndMapReadyListener { + fun onMapReady(googleMap: GoogleMap?) + } + + init { + registerListeners() + } + + private fun registerListeners() { + // View layout. + if (mapView?.width != 0 && mapView?.height != 0) { + // View has already completed layout. + isViewReady = true + } else { + // Map has not undergone layout, register a View observer. + mapView.viewTreeObserver.addOnGlobalLayoutListener(this) + } + + // GoogleMap. Note if the GoogleMap is already ready it will still fire the callback later. + mapFragment.getMapAsync(this) + } + + override fun onMapReady(googleMap: GoogleMap) { + // NOTE: The GoogleMap API specifies the listener is removed just prior to invocation. + this.googleMap = googleMap + isMapReady = true + fireCallbackIfReady() + } + + // We use the new method when supported + @SuppressLint("NewApi") // We check which build version we are using. + override fun onGlobalLayout() { + // Remove our listener. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + mapView?.viewTreeObserver?.removeGlobalOnLayoutListener(this) + } else { + mapView?.viewTreeObserver?.removeOnGlobalLayoutListener(this) + } + isViewReady = true + fireCallbackIfReady() + } + + private fun fireCallbackIfReady() { + if (isViewReady && isMapReady) { + toBeNotified.onMapReady(googleMap) + } + } +} diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/arrow.png b/ApiDemos/kotlin/app/src/main/res/drawable/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..77b3f5aaac23f59afa56aa1f19e62123524647e6 GIT binary patch literal 1649 zcmV-%29EiOP)gXFAg< z?;jJnF@_c&n2RRH7x!K(L<0iGM*tDgXc1zB0@LZtnV!AY^2eO1)7mMsC(KH|Ip@sm zHTyg3y$KG|^~%h|`O{|K8jm$z45+y1iipVawR3K`5ZA z(BkdrXH}}IP$-C;c620Iv;{;6;ueC%^{Q~>=jdVpq6$`l6?S_uWvD|?)Mh~RT!eEFTrAY zv0xEc3Eg%Q5293GfgjFkHBq=ms;W_{ za6bN2H}QK&KywYR4h-yl_}TmS40|hHzY0$*`Yh9a{gj<8&24{YDzu7-sw$}}CI%+? zts+jOqXeIjkAL#j7yWm=_~?P5)VewF{`b}ZR=#-+p6b~8>%94=e3MMJ+^EJ*Rh3%K zI5mkv)uRUTMw-9;>dXEGFE2ZOIMv&ZROik=2&~>XA4?Z(!ix3R-aMss%4-@cOl1jc zai#Mq$2SQ=;O{vw@WU;umh9c1?z5E1neC&|+fnPW$9UA1KI{`70}M#+sKnCjSx?|<9|02mzn<-;)GJ7Hs`c+(nU z?gS7eHDm)l9AtByPc7QI>zUWDME5_p{b@+9D62Q#*oC-F3qnt!Co?9M$dIpz4J||NP*Up`lFe460E1if*LCnvW0YsT&j0Yg|ah&^#A7| zmam(G{*8xl#<@RkI%C=e)8Sz@D@Sv6>4fJ9RRs|lZa|o~-OFy*gTUxwCqL z0MNhj5YoMuW6jcoJo`HTqeQ&vub%JEVzmkd)xc1Tb773Qz+KC3+p@AquDv3;iY_im zcgJ?5`{rWRL;G^JFtV5f`M-frqEdsa1x7sLf_*Lsvd@>?n(Ewf!jojxlJ1>5-AgpQ zqrS{mbsSjX!LYYs_}Gzqd+yscRJ~r6^30o8p}XVrpEa-#kTv@uK$SI8NeINWc{p?I zv7Y;O4W;|qancFekpfn~eG{JS*t$(e>|zo#s33|whzd}^5ZHV@lFi-IbI;cY((C7< z`}Q3UC&?P+>Gkt&fNwU5k*Lv+ZHWq1B16PDh|pf!-LYf;rX&Br{ML8ejzO-($N;7A zTi!SKCivz9ApwmUr&I}sNN`lF%u97_-{;2&HdJRr(71cA3?}-HD(Uz!qQ}-;dS~*~ z_zMCVo%x6e#FRVoOK#!69{*;`1asl|X9XYtxC}a;pbsDpAPFE5J$q$wN)uVVF=@== zo>`ByOpD*Iq=rBMBX{t-;hwe2cfU13t<`bY5-|%P0FVPvh@QtNQ$)rG5R1?dk#3BT zjL=lN+E5d_w+-&Q@Up+0*4P}I0aYCMHoNxU={@fPXf9h@dhtZ`pRs6vipVm6kq8r7 z5ETgeWmkWkWk)w?J1ddw?3%GOm$5&+)U~@WvJA>LFSk?)BW2exf-Tdnm?TsNGYQJq v;k=8Q3EB3EP)Nkl zs&s@G5e5)WcA*YE5-F5fVN>w_5b-T#1ZkhixJYOT7keV$OGM~=A?q}c(lKU~KVo9% zg&|^{A4G&UuM>v$l?3m4SzB*gW<)}*X#BGtWs^*(dyPZg91dqjnbGhkJtN}>5nl}$ z6DSFtza+e>#iKxK#g%d;F58v(zCeYml?s z_gvWEAI?7eGI_rIEs0c2o{5%?Tl{+Ox%J&$OG9%?>VuM!k&iu97-IIs*acGY=(L(} zX0%bP53zV-a2IwD8HzoFDAsWOFABBb@L(f0j#D5rjMfgB)#D@{2{j`_%OP{34o7~W zL%LB1X9y45Ai_Wqc4_A}+Df1*H@sk#_jtDlNR^-}O zIdK(!i9^k71FNff(cp?DMA*o)eRq)@TT`U44~}7VxpFhIHb($WD(v4Bj@$?{t1lq3 zIscO5_={S6`idMM%#K9X(^?d!C~;t8IMP?kkiJ%ioPQ{AGL&x@kd;#yP&%Flnc8dJ zug0l+Vo>$0f%$9R`}j7y=E3C>n=c zCmE2$aN|Q9a9m3x`!6_xt=l4az2%+dXU}?2gmndXoZQ%+%n}AQKKo8{j-R5c}6l z;M{)?(s&&X3^ZX^r~wB?>XG|vEecjDQJE8ko6QQ`s8`^6trGZ3i5<(sv2}(5+0(Sh z4;9@Thrxo%Cyh9_PRC*y)*o^_E1x#Prs7>5gNMi-iNiShT~-mnqwHZ58WwT5bizwS zaK)Qf=$I2&B;!3qcH9?(EpI4rxtyd|A;-Qo5x7<<$HP;9j=b$5IGi4i%e4yJB(gWC z!#~!Ndi_O(z00Cdd?*qp_eKyIu?;jS@dFWaWJDrAOe9h~%uK4yku2XZ@i$aB5U0n{Kd6!U z8#-SSg__e**ca|qFTs0m%~a#kaTTrrM{EBafu%GXdeIl zTQn-C8Bspbgj$NbeMOlm9g1UHRBR+cokAmxlueH6qfyAuh`^4NaGYAD!08Pnso~K$ zHDD-m_C%pHFB15gq*JHBQ8EC6(Xi8~u9pqNv4auV`DP^SLt;FWy=uCVMU=&H7JR!+ z3mX}suinwMGdzXi);7B~{vFAg(UWJni7;1NDjut!H^Al!goOD~DDyt-AQk&0-iXz) zN*tW2M*3I{)f-m)wAB%>9~rF zLXI@XXASc1x(Jr9b`B?=53w?<>h-QII5YCGKS~-Fy=kp^X&!2R+a9&QqhlBKp6d+r zQAhBtFHM-*?LugC9ZQB!qI>6ec;EbP+Ylh&(LjMbOi@Vx3l5B(6csp!F)P!1+8FkZK7|=G7_%OD71pMvkJx zV(E)S+4y3R0VjH91)6@F?k^c3 zKO!VQE1oc+^$WF!$c1bbO8}LPr{HbjvOq}8>1#rCvOi?plie9>VWDa3K{Z+rshMht zn+AR-Lxu7}uu{}QQuIQvC%dp4^Lvh^7wYD6Y#%@f)`X;}ACp}m@grf>?f76Iy(j5- z%&%*+qKqO;A$<13{JP(rMYs>a{+z>y%tM5_?g({S*=6mc%U*oK(L$IS)UO_TAoL8O zIEXIFS@&GPUHVE$$mP9FsCHW|*B(GyS0sdaQ=jOuT8)HNgt$9SpgTgY^Lv|DteH|A z^UzX%6ZbX({KO&3%A8_&so9UR;xcIGUbnoD<-Vtc|Kf4zb!fGHnXaE2J M07*qoM6N<$f;~1}3IG5A literal 0 HcmV?d00001 diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_nt.png new file mode 100644 index 0000000000000000000000000000000000000000..b85cf6c7ee30c77dd2efaa516118285842412ba4 GIT binary patch literal 2807 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 literal 0 HcmV?d00001 diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_sa.png new file mode 100644 index 0000000000000000000000000000000000000000..11aa229f7914c0836e2a072946ed907127e988c3 GIT binary patch literal 2645 zcmV-b3aa&qP)!X|rLl>f&>_@_)U=X^(P_0#)EdQf8nq5;BBap< zE25|fL_|eJKtn)5O;iw22*d}0UqPi*ty+yS{ZG%icmL&o?Ee6k?97?F|Ngt@-tV6G zxd1v{coqE70JCUJq_K#`I-!ViC8gEC$NZ(7XXXkm>s zqC!TqdwV3`F^KynqSZvc%7**^0F+5+0mF%Mm4)a-l2;y4P^SYT33$+id)lz`Z6%`u zXfKVP`YygnmBut(CE%LsMJXDflPX<>9i1ozXJB?I+R$NpC^IoPvP7;cVv#gHFW|I zlJxk|lK+YQay*^|n6^=>wDMk>t01O{4``uvCjnP11dIv?9P|X>*q;Cn9V{ofe4-P8 z+yd9K#!oNo5pIuE=@l~S#5l>+Bn5L? z{wMtYYIZw&jI?-hrz?O88h|sB}V_&*JJ&0mi_ry)xt>C zKU!gt&K$J{;jOw_lE!5+@>$dDGgUp;H64MFJ0JIqk)7KG4r|-KuJxqqepd-QC@7=I;9=z$b^8xnW5+TPr(r zrlc-={%K#G7)WVpskoo>n$&HZg7LXixO>?BDr(AUHyyZ9ml=4(% zSaAgKd5W&S-r7Kdf`V|6XhD%0JG(rdBFYHBL}dC3wSIclutdr?tQVzCPrEKr|$_4Xr&(HE1GlQAhN2}46e z(Zj>T;e77yaxBZWRZkn$V*O72*fqlw3-a<3qt#dfHXEXS^#mTI<8R6 ztF}Rys05UTvPD2-U#$m_`uchcAZ~7My71*}!K(k2#1eVky1?O}R(?d5njM+&{{8zF zKzw|BI>e8g5VtO9#-iB_q`y`x^iT)Yi=BY=6&@bmE?#bKW@^=_*#IA8%7B}!|Kxlr zXM6iV{PbzSH{YCX0m5iB;;vo0@af_0 zAs`aS-rAEM*s4^JR@eCPm;9a5Z!m+c-$tWi&j_>e_Q zy-a!2r4pI4=j@QonX%}k%$Fkl_&LBC60hXnIMfJx-x3W-PN3i4EG1gzevU1Ycl z!X%k;yUZwQgSRMen&XD`^y*=*6Bg0$*(?0$C!|ifF&g* z7?-jZfn2nBcfK12Q`LBhj}N%Vyc}>#hC>kN#o8L$dTM2WOZFslst|C)Ho)Sgfcdd> z|0zj`;={(Y^;mVG3iI>x@$~7_SW{CYj6E|mQ&fTF<>i=_pM~#lkP_HJ5-BE$?j`q~ z$RHZ*8ut|u^V)ol=!#^lPMdPGWAS)#u_&e(Ky`IBottNkGu9;03w(A%bx*06WW^SO_(@Q zfLgX}873ws;_chFF*-UL=gysjW5+(HzV|HUQhq)b-Aa=b4se`+&brL(hEIx)ns)Hs z*D{jFOqYEpJ~TBg*1A77;aMdukCnZz2bU~tp@7B^T~w(}fcTKb0CC*f7pcb$kvJM3 ze*?JwFMu;6WI{QxKZvZCyZjf60c(%Z&RU%|1ClVA8@yW8wFfrb9t&(cKm&H$JX+H%55fwg}j;p00000NkvXXu0mjf Dz+@OB literal 0 HcmV?d00001 diff --git a/ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png b/ApiDemos/kotlin/app/src/main/res/drawable/badge_victoria.png new file mode 100644 index 0000000000000000000000000000000000000000..80db77e77ed7fea76992d82a61e0e05319b9a002 GIT binary patch literal 2494 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 + + diff --git a/ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml new file mode 100644 index 00000000..0dcbdef2 --- /dev/null +++ b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_contents.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + diff --git a/ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml new file mode 100644 index 00000000..317e4a8b --- /dev/null +++ b/ApiDemos/kotlin/app/src/main/res/layout/custom_info_window.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + diff --git a/ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml b/ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml new file mode 100644 index 00000000..5e900345 --- /dev/null +++ b/ApiDemos/kotlin/app/src/main/res/layout/marker_demo.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + +