diff --git a/ApiDemos/kotlin/app/src/gms/AndroidManifest.xml b/ApiDemos/kotlin/app/src/gms/AndroidManifest.xml
index af466568..18268f60 100644
--- a/ApiDemos/kotlin/app/src/gms/AndroidManifest.xml
+++ b/ApiDemos/kotlin/app/src/gms/AndroidManifest.xml
@@ -52,6 +52,7 @@
+
diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/CameraClampingDemoActivity.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/CameraClampingDemoActivity.kt
index a4c3dd16..20386103 100644
--- a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/CameraClampingDemoActivity.kt
+++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/CameraClampingDemoActivity.kt
@@ -23,8 +23,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
-import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener
-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
@@ -33,7 +31,6 @@ import com.google.maps.android.ktx.CameraIdleEvent
import com.google.maps.android.ktx.awaitMap
import com.google.maps.android.ktx.cameraEvents
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/DemoDetailsList.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/DemoDetailsList.kt
index 81e9651b..2267426e 100644
--- a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/DemoDetailsList.kt
+++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/DemoDetailsList.kt
@@ -86,6 +86,9 @@ class DemoDetailsList {
DemoDetails(R.string.raw_map_view_demo_label,
R.string.raw_map_view_demo_description,
RawMapViewDemoActivity::class.java),
+ DemoDetails(R.string.save_state_demo_label,
+ R.string.save_state_demo_description,
+ SaveStateDemoActivity::class.java),
DemoDetails(R.string.street_view_panorama_basic_demo_label,
R.string.street_view_panorama_basic_demo_details,
StreetViewPanoramaBasicDemoActivity::class.java),
diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/SaveStateDemoActivity.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/SaveStateDemoActivity.kt
new file mode 100755
index 00000000..118fe41b
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/SaveStateDemoActivity.kt
@@ -0,0 +1,160 @@
+// Copyright 2020 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
+//
+// 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.
+package com.example.kotlindemos
+
+import android.os.Bundle
+import android.os.Parcelable
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import com.google.android.gms.maps.CameraUpdateFactory
+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.BitmapDescriptorFactory
+import com.google.android.gms.maps.model.LatLng
+import com.google.android.gms.maps.model.Marker
+import com.google.maps.android.ktx.CameraEvent
+import com.google.maps.android.ktx.addMarker
+import com.google.maps.android.ktx.awaitMap
+import com.google.maps.android.ktx.cameraEvents
+import kotlinx.android.parcel.Parcelize
+import kotlinx.coroutines.flow.collect
+import java.util.Random
+
+/**
+ * This activity shows how to save the state of a MapFragment when the activity is recreated, like
+ * after rotation of the device.
+ */
+class SaveStateDemoActivity : AppCompatActivity() {
+
+ @Parcelize
+ internal data class MarkerInfo(var hue: Float) : Parcelable
+
+ /**
+ * Example of a custom `MapFragment` showing how the position of a marker and other
+ * custom
+ * [Parcelable]s objects can be saved after rotation of the device.
+ *
+ *
+ * Storing custom [Parcelable] objects directly in the [Bundle] provided by the
+ * [.onActivityCreated] method will throw a `ClassNotFoundException`. This
+ * is due to the fact that this Bundle is parceled (thus losing its ClassLoader attribute at
+ * this moment) and unparceled later in a different ClassLoader.
+ *
+ * A workaround to store these objects is to wrap the custom [Parcelable] objects in a
+ * new
+ * [Bundle] object.
+ *
+ *
+ * However, note that it is safe to store [Parcelable] objects from the Maps API (eg.
+ * MarkerOptions, LatLng, etc.) directly in the Bundle provided by the
+ * [.onActivityCreated] method.
+ */
+ class SaveStateMapFragment : SupportMapFragment(), OnMarkerClickListener, OnMarkerDragListener {
+
+ private lateinit var mMarkerPosition: LatLng
+ private lateinit var mMarkerInfo: MarkerInfo
+ private var mMoveCameraToMarker = false
+
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // Extract the state of the MapFragment:
+ // - Objects from the API (eg. LatLng, MarkerOptions, etc.) were stored directly in
+ // the savedInsanceState Bundle.
+ // - Custom Parcelable objects were wrapped in another Bundle.
+ mMarkerPosition =
+ savedInstanceState?.getParcelable(MARKER_POSITION) ?: DEFAULT_MARKER_POSITION
+ mMarkerInfo =
+ savedInstanceState?.getBundle(OTHER_OPTIONS)?.getParcelable(MARKER_INFO) ?: MarkerInfo(
+ BitmapDescriptorFactory.HUE_RED)
+ mMoveCameraToMarker = savedInstanceState == null
+
+ lifecycleScope.launchWhenCreated {
+ val map = awaitMap()
+ map.addMarker {
+ icon(BitmapDescriptorFactory.defaultMarker(mMarkerInfo.hue))
+ position(mMarkerPosition)
+ draggable(true)
+ }
+
+ map.setOnMarkerDragListener(this@SaveStateMapFragment)
+ map.setOnMarkerClickListener(this@SaveStateMapFragment)
+
+ if (mMoveCameraToMarker) {
+ map.animateCamera(CameraUpdateFactory.newLatLng(mMarkerPosition))
+ }
+ }
+ }
+
+ override fun onSaveInstanceState(outState: Bundle) {
+ super.onSaveInstanceState(outState)
+
+ // All Parcelable objects of the API (eg. LatLng, MarkerOptions, etc.) can be set
+ // directly in the given Bundle.
+ outState.putParcelable(MARKER_POSITION, mMarkerPosition)
+
+ // All other custom Parcelable objects must be wrapped in another Bundle. Indeed,
+ // failing to do so would throw a ClassNotFoundException. This is due to the fact that
+ // this Bundle is being parceled (losing its ClassLoader at this time) and unparceled
+ // later in a different ClassLoader.
+ val bundle = Bundle()
+ bundle.putParcelable(MARKER_INFO, mMarkerInfo)
+ outState.putBundle(OTHER_OPTIONS, bundle)
+ }
+
+ override fun onMarkerClick(marker: Marker): Boolean {
+ val newHue = MARKER_HUES[Random()
+ .nextInt(MARKER_HUES.size)]
+ mMarkerInfo.hue = newHue
+ marker.setIcon(BitmapDescriptorFactory.defaultMarker(newHue))
+ return true
+ }
+
+ override fun onMarkerDragStart(marker: Marker) {}
+ override fun onMarkerDrag(marker: Marker) {}
+ override fun onMarkerDragEnd(marker: Marker) {
+ mMarkerPosition = marker.position
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.save_state_demo)
+ }
+
+ companion object {
+ /** Default marker position when the activity is first created. */
+ private val DEFAULT_MARKER_POSITION = LatLng(48.858179, 2.294576)
+
+ /** List of hues to use for the marker */
+ private val MARKER_HUES = floatArrayOf(
+ BitmapDescriptorFactory.HUE_RED,
+ BitmapDescriptorFactory.HUE_ORANGE,
+ BitmapDescriptorFactory.HUE_YELLOW,
+ BitmapDescriptorFactory.HUE_GREEN,
+ BitmapDescriptorFactory.HUE_CYAN,
+ BitmapDescriptorFactory.HUE_AZURE,
+ BitmapDescriptorFactory.HUE_BLUE,
+ BitmapDescriptorFactory.HUE_VIOLET,
+ BitmapDescriptorFactory.HUE_MAGENTA,
+ BitmapDescriptorFactory.HUE_ROSE)
+
+ // Bundle keys.
+ private const val OTHER_OPTIONS = "options"
+ private const val MARKER_POSITION = "markerPosition"
+ private const val MARKER_INFO = "markerInfo"
+ }
+}
\ No newline at end of file
diff --git a/ApiDemos/kotlin/app/src/gms/res/layout/save_state_demo.xml b/ApiDemos/kotlin/app/src/gms/res/layout/save_state_demo.xml
new file mode 100755
index 00000000..98400fb2
--- /dev/null
+++ b/ApiDemos/kotlin/app/src/gms/res/layout/save_state_demo.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
diff --git a/ApiDemos/kotlin/app/src/main/res/values/strings.xml b/ApiDemos/kotlin/app/src/main/res/values/strings.xml
index 42f5c5c7..2ad5f9ef 100644
--- a/ApiDemos/kotlin/app/src/main/res/values/strings.xml
+++ b/ApiDemos/kotlin/app/src/main/res/values/strings.xml
@@ -310,4 +310,9 @@
Retain map
Demonstrates how to reuse a MapFragment.
+
+ Save the state of a MapFragment.
+ Demonstrates how to save the state of a MapFragment upon rotation of the device.
+ Drag the marker, tap on it to change its color and rotate the device to check that the state of the map is preserved.
+
\ No newline at end of file