Skip to content

Commit 0514d79

Browse files
authored
feat: Add SaveStateDemoActivity. (#431)
1 parent 05b53a2 commit 0514d79

File tree

6 files changed

+202
-3
lines changed

6 files changed

+202
-3
lines changed

ApiDemos/kotlin/app/src/gms/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<activity android:name=".ProgrammaticDemoActivity" />
5353
<activity android:name=".RawMapViewDemoActivity" />
5454
<activity android:name=".RetainMapDemoActivity" />
55+
<activity android:name=".SaveStateDemoActivity" />
5556
<activity android:name=".SplitStreetViewPanoramaAndMapDemoActivity" />
5657
<activity android:name=".StreetViewPanoramaBasicDemoActivity" />
5758
<activity android:name=".StreetViewPanoramaEventsDemoActivity" />

ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/CameraClampingDemoActivity.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ import androidx.appcompat.app.AppCompatActivity
2323
import androidx.lifecycle.lifecycleScope
2424
import com.google.android.gms.maps.CameraUpdateFactory
2525
import com.google.android.gms.maps.GoogleMap
26-
import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener
27-
import com.google.android.gms.maps.OnMapReadyCallback
2826
import com.google.android.gms.maps.SupportMapFragment
2927
import com.google.android.gms.maps.model.CameraPosition
3028
import com.google.android.gms.maps.model.LatLng
@@ -33,7 +31,6 @@ import com.google.maps.android.ktx.CameraIdleEvent
3331
import com.google.maps.android.ktx.awaitMap
3432
import com.google.maps.android.ktx.cameraEvents
3533
import kotlinx.coroutines.ExperimentalCoroutinesApi
36-
import kotlinx.coroutines.coroutineScope
3734
import kotlinx.coroutines.flow.collect
3835
import kotlinx.coroutines.launch
3936

ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/DemoDetailsList.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class DemoDetailsList {
8686
DemoDetails(R.string.raw_map_view_demo_label,
8787
R.string.raw_map_view_demo_description,
8888
RawMapViewDemoActivity::class.java),
89+
DemoDetails(R.string.save_state_demo_label,
90+
R.string.save_state_demo_description,
91+
SaveStateDemoActivity::class.java),
8992
DemoDetails(R.string.street_view_panorama_basic_demo_label,
9093
R.string.street_view_panorama_basic_demo_details,
9194
StreetViewPanoramaBasicDemoActivity::class.java),
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="utf-8"?><!--
2+
Copyright (C) 2012 The Android Open Source Project
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
17+
android:layout_width="match_parent"
18+
android:layout_height="match_parent"
19+
android:orientation="vertical">
20+
21+
<TextView
22+
android:id="@+id/instructions"
23+
android:layout_width="match_parent"
24+
android:layout_height="wrap_content"
25+
android:text="@string/save_state_instructions" />
26+
27+
<fragment
28+
android:id="@+id/map"
29+
android:layout_width="match_parent"
30+
android:layout_height="match_parent"
31+
class="com.example.kotlindemos.SaveStateDemoActivity$SaveStateMapFragment" />
32+
33+
</LinearLayout>

ApiDemos/kotlin/app/src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,9 @@
310310
<string name="retain_map_demo_label">Retain map</string>
311311
<string name="retain_map_demo_description">Demonstrates how to reuse a MapFragment.</string>
312312

313+
<!-- Saved State -->
314+
<string name="save_state_demo_label">Save the state of a MapFragment.</string>
315+
<string name="save_state_demo_description">Demonstrates how to save the state of a MapFragment upon rotation of the device.</string>
316+
<string name="save_state_instructions">Drag the marker, tap on it to change its color and rotate the device to check that the state of the map is preserved.</string>
317+
313318
</resources>

0 commit comments

Comments
 (0)