1+ // Copyright 2020 Google LLC
2+ //
3+ // Licensed under the Apache License, Version 2.0 (the "License");
4+ // you may not use this file except in compliance with the License.
5+ // You may obtain a copy of the License at
6+ //
7+ // http://www.apache.org/licenses/LICENSE-2.0
8+ //
9+ // Unless required by applicable law or agreed to in writing, software
10+ // distributed under the License is distributed on an "AS IS" BASIS,
11+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ // See the License for the specific language governing permissions and
13+ // limitations under the License.
14+ package com.example.kotlindemos
15+
16+ import android.os.Bundle
17+ import android.os.Parcelable
18+ import androidx.appcompat.app.AppCompatActivity
19+ import androidx.lifecycle.lifecycleScope
20+ import com.google.android.gms.maps.CameraUpdateFactory
21+ import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener
22+ import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener
23+ import com.google.android.gms.maps.SupportMapFragment
24+ import com.google.android.gms.maps.model.BitmapDescriptorFactory
25+ import com.google.android.gms.maps.model.LatLng
26+ import com.google.android.gms.maps.model.Marker
27+ import com.google.maps.android.ktx.CameraEvent
28+ import com.google.maps.android.ktx.addMarker
29+ import com.google.maps.android.ktx.awaitMap
30+ import com.google.maps.android.ktx.cameraEvents
31+ import kotlinx.android.parcel.Parcelize
32+ import kotlinx.coroutines.flow.collect
33+ import java.util.Random
34+
35+ /* *
36+ * This activity shows how to save the state of a MapFragment when the activity is recreated, like
37+ * after rotation of the device.
38+ */
39+ class SaveStateDemoActivity : AppCompatActivity () {
40+
41+ @Parcelize
42+ internal data class MarkerInfo (var hue : Float ) : Parcelable
43+
44+ /* *
45+ * Example of a custom `MapFragment` showing how the position of a marker and other
46+ * custom
47+ * [Parcelable]s objects can be saved after rotation of the device.
48+ *
49+ *
50+ * Storing custom [Parcelable] objects directly in the [Bundle] provided by the
51+ * [.onActivityCreated] method will throw a `ClassNotFoundException`. This
52+ * is due to the fact that this Bundle is parceled (thus losing its ClassLoader attribute at
53+ * this moment) and unparceled later in a different ClassLoader.
54+ * <br></br>
55+ * A workaround to store these objects is to wrap the custom [Parcelable] objects in a
56+ * new
57+ * [Bundle] object.
58+ *
59+ *
60+ * However, note that it is safe to store [Parcelable] objects from the Maps API (eg.
61+ * MarkerOptions, LatLng, etc.) directly in the Bundle provided by the
62+ * [.onActivityCreated] method.
63+ */
64+ class SaveStateMapFragment : SupportMapFragment (), OnMarkerClickListener, OnMarkerDragListener {
65+
66+ private lateinit var mMarkerPosition: LatLng
67+ private lateinit var mMarkerInfo: MarkerInfo
68+ private var mMoveCameraToMarker = false
69+
70+
71+ override fun onCreate (savedInstanceState : Bundle ? ) {
72+ super .onCreate(savedInstanceState)
73+
74+ // Extract the state of the MapFragment:
75+ // - Objects from the API (eg. LatLng, MarkerOptions, etc.) were stored directly in
76+ // the savedInsanceState Bundle.
77+ // - Custom Parcelable objects were wrapped in another Bundle.
78+ mMarkerPosition =
79+ savedInstanceState?.getParcelable(MARKER_POSITION ) ? : DEFAULT_MARKER_POSITION
80+ mMarkerInfo =
81+ savedInstanceState?.getBundle(OTHER_OPTIONS )?.getParcelable(MARKER_INFO ) ? : MarkerInfo (
82+ BitmapDescriptorFactory .HUE_RED )
83+ mMoveCameraToMarker = savedInstanceState == null
84+
85+ lifecycleScope.launchWhenCreated {
86+ val map = awaitMap()
87+ map.addMarker {
88+ icon(BitmapDescriptorFactory .defaultMarker(mMarkerInfo.hue))
89+ position(mMarkerPosition)
90+ draggable(true )
91+ }
92+
93+ map.setOnMarkerDragListener(this @SaveStateMapFragment)
94+ map.setOnMarkerClickListener(this @SaveStateMapFragment)
95+
96+ if (mMoveCameraToMarker) {
97+ map.animateCamera(CameraUpdateFactory .newLatLng(mMarkerPosition))
98+ }
99+ }
100+ }
101+
102+ override fun onSaveInstanceState (outState : Bundle ) {
103+ super .onSaveInstanceState(outState)
104+
105+ // All Parcelable objects of the API (eg. LatLng, MarkerOptions, etc.) can be set
106+ // directly in the given Bundle.
107+ outState.putParcelable(MARKER_POSITION , mMarkerPosition)
108+
109+ // All other custom Parcelable objects must be wrapped in another Bundle. Indeed,
110+ // failing to do so would throw a ClassNotFoundException. This is due to the fact that
111+ // this Bundle is being parceled (losing its ClassLoader at this time) and unparceled
112+ // later in a different ClassLoader.
113+ val bundle = Bundle ()
114+ bundle.putParcelable(MARKER_INFO , mMarkerInfo)
115+ outState.putBundle(OTHER_OPTIONS , bundle)
116+ }
117+
118+ override fun onMarkerClick (marker : Marker ): Boolean {
119+ val newHue = MARKER_HUES [Random ()
120+ .nextInt(MARKER_HUES .size)]
121+ mMarkerInfo.hue = newHue
122+ marker.setIcon(BitmapDescriptorFactory .defaultMarker(newHue))
123+ return true
124+ }
125+
126+ override fun onMarkerDragStart (marker : Marker ) {}
127+ override fun onMarkerDrag (marker : Marker ) {}
128+ override fun onMarkerDragEnd (marker : Marker ) {
129+ mMarkerPosition = marker.position
130+ }
131+ }
132+
133+ override fun onCreate (savedInstanceState : Bundle ? ) {
134+ super .onCreate(savedInstanceState)
135+ setContentView(R .layout.save_state_demo)
136+ }
137+
138+ companion object {
139+ /* * Default marker position when the activity is first created. */
140+ private val DEFAULT_MARKER_POSITION = LatLng (48.858179 , 2.294576 )
141+
142+ /* * List of hues to use for the marker */
143+ private val MARKER_HUES = floatArrayOf(
144+ BitmapDescriptorFactory .HUE_RED ,
145+ BitmapDescriptorFactory .HUE_ORANGE ,
146+ BitmapDescriptorFactory .HUE_YELLOW ,
147+ BitmapDescriptorFactory .HUE_GREEN ,
148+ BitmapDescriptorFactory .HUE_CYAN ,
149+ BitmapDescriptorFactory .HUE_AZURE ,
150+ BitmapDescriptorFactory .HUE_BLUE ,
151+ BitmapDescriptorFactory .HUE_VIOLET ,
152+ BitmapDescriptorFactory .HUE_MAGENTA ,
153+ BitmapDescriptorFactory .HUE_ROSE )
154+
155+ // Bundle keys.
156+ private const val OTHER_OPTIONS = " options"
157+ private const val MARKER_POSITION = " markerPosition"
158+ private const val MARKER_INFO = " markerInfo"
159+ }
160+ }
0 commit comments