lite_list_demo_row.xml
. It contains a MapView
+ * that is programatically initialised in
+ * {@link #(int, android.view.View, android.view.ViewGroup)}
+ */
+ private class MapAdapter extends RecyclerView.Adaptermap
field is set, otherwise it is null.
+ * When the {@link #onMapReady(com.google.android.gms.maps.GoogleMap)} callback is received and
+ * the {@link com.google.android.gms.maps.GoogleMap} is ready, it stored in the {@link #map}
+ * field. The map is then initialised with the NamedLocation that is stored as the tag of the
+ * MapView. This ensures that the map is initialised with the latest data that it should
+ * display.
+ */
+ class ViewHolder extends RecyclerView.ViewHolder implements OnMapReadyCallback {
+
+ MapView mapView;
+ TextView title;
+ GoogleMap map;
+ View layout;
+
+ private ViewHolder(View itemView) {
+ super(itemView);
+ layout = itemView;
+ mapView = layout.findViewById(R.id.lite_listrow_map);
+ title = layout.findViewById(R.id.lite_listrow_text);
+ if (mapView != null) {
+ // Initialise the MapView
+ mapView.onCreate(null);
+ // Set the map ready callback to receive the GoogleMap object
+ mapView.getMapAsync(this);
+ }
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ MapsInitializer.initialize(getApplicationContext());
+ map = googleMap;
+ setMapLocation();
+ }
+
+ /**
+ * Displays a {@link LiteListDemoActivity.NamedLocation} on a
+ * {@link com.google.android.gms.maps.GoogleMap}.
+ * Adds a marker and centers the camera on the NamedLocation with the normal map type.
+ */
+ private void setMapLocation() {
+ if (map == null) return;
+
+ NamedLocation data = (NamedLocation) mapView.getTag();
+ if (data == null) return;
+
+ // Add a marker for this item and set the camera
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(data.location, 13f));
+ map.addMarker(new MarkerOptions().position(data.location));
+
+ // Set the map type back to normal.
+ map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
+ }
+
+ private void bindView(int pos) {
+ NamedLocation item = namedLocations[pos];
+ // Store a reference of the ViewHolder object in the layout.
+ layout.setTag(this);
+ // Store a reference to the item in the mapView's tag. We use it to get the
+ // coordinate of a location, when setting the map location.
+ mapView.setTag(item);
+ setMapLocation();
+ title.setText(item.name);
+ }
+ }
+ }
+
+ /**
+ * RecycleListener that completely clears the {@link com.google.android.gms.maps.GoogleMap}
+ * attached to a row in the RecyclerView.
+ * Sets the map type to {@link com.google.android.gms.maps.GoogleMap#MAP_TYPE_NONE} and clears
+ * the map.
+ */
+ private RecyclerView.RecyclerListener mRecycleListener = new RecyclerView.RecyclerListener() {
+
+ @Override
+ public void onViewRecycled(RecyclerView.ViewHolder holder) {
+ MapAdapter.ViewHolder mapHolder = (MapAdapter.ViewHolder) holder;
+ if (mapHolder != null && mapHolder.map != null) {
+ // Clear the map and free up resources by changing the map type to none.
+ // Also reset the map when it gets reattached to layout, so the previous map would
+ // not be displayed.
+ mapHolder.map.clear();
+ mapHolder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
+ }
+ }
+ };
+
+ /**
+ * Location represented by a position ({@link com.google.android.gms.maps.model.LatLng} and a
+ * name ({@link java.lang.String}).
+ */
+ private static class NamedLocation {
+
+ public final String name;
+ public final LatLng location;
+
+ NamedLocation(String name, LatLng location) {
+ this.name = name;
+ this.location = location;
+ }
+ }
+
+ /**
+ * A list of locations to show in this ListView.
+ */
+ private static final NamedLocation[] LIST_LOCATIONS = new NamedLocation[]{
+ new NamedLocation("Cape Town", new LatLng(-33.920455, 18.466941)),
+ new NamedLocation("Beijing", new LatLng(39.937795, 116.387224)),
+ new NamedLocation("Bern", new LatLng(46.948020, 7.448206)),
+ new NamedLocation("Breda", new LatLng(51.589256, 4.774396)),
+ new NamedLocation("Brussels", new LatLng(50.854509, 4.376678)),
+ new NamedLocation("Copenhagen", new LatLng(55.679423, 12.577114)),
+ new NamedLocation("Hannover", new LatLng(52.372026, 9.735672)),
+ new NamedLocation("Helsinki", new LatLng(60.169653, 24.939480)),
+ new NamedLocation("Hong Kong", new LatLng(22.325862, 114.165532)),
+ new NamedLocation("Istanbul", new LatLng(41.034435, 28.977556)),
+ new NamedLocation("Johannesburg", new LatLng(-26.202886, 28.039753)),
+ new NamedLocation("Lisbon", new LatLng(38.707163, -9.135517)),
+ new NamedLocation("London", new LatLng(51.500208, -0.126729)),
+ new NamedLocation("Madrid", new LatLng(40.420006, -3.709924)),
+ new NamedLocation("Mexico City", new LatLng(19.427050, -99.127571)),
+ new NamedLocation("Moscow", new LatLng(55.750449, 37.621136)),
+ new NamedLocation("New York", new LatLng(40.750580, -73.993584)),
+ new NamedLocation("Oslo", new LatLng(59.910761, 10.749092)),
+ new NamedLocation("Paris", new LatLng(48.859972, 2.340260)),
+ new NamedLocation("Prague", new LatLng(50.087811, 14.420460)),
+ new NamedLocation("Rio de Janeiro", new LatLng(-22.90187, -43.232437)),
+ new NamedLocation("Rome", new LatLng(41.889998, 12.500162)),
+ new NamedLocation("Sao Paolo", new LatLng(-22.863878, -43.244097)),
+ new NamedLocation("Seoul", new LatLng(37.560908, 126.987705)),
+ new NamedLocation("Stockholm", new LatLng(59.330650, 18.067360)),
+ new NamedLocation("Sydney", new LatLng(-33.873651, 151.2068896)),
+ new NamedLocation("Taipei", new LatLng(25.022112, 121.478019)),
+ new NamedLocation("Tokyo", new LatLng(35.670267, 139.769955)),
+ new NamedLocation("Tulsa Oklahoma", new LatLng(36.149777, -95.993398)),
+ new NamedLocation("Vaduz", new LatLng(47.141076, 9.521482)),
+ new NamedLocation("Vienna", new LatLng(48.209206, 16.372778)),
+ new NamedLocation("Warsaw", new LatLng(52.235474, 21.004057)),
+ new NamedLocation("Wellington", new LatLng(-41.286480, 174.776217)),
+ new NamedLocation("Winnipeg", new LatLng(49.875832, -97.150726))
+ };
+
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java
new file mode 100644
index 000000000..6986f9476
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java
@@ -0,0 +1,126 @@
+// 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.mapdemo;
+
+import android.Manifest.permission;
+import android.annotation.SuppressLint;
+import android.content.pm.PackageManager;
+import androidx.core.app.ActivityCompat;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
+import com.google.android.gms.maps.LocationSource;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.LatLng;
+
+import android.location.Location;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+/**
+ * This shows how to use a custom location source.
+ */
+public class LocationSourceDemoActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ /**
+ * A {@link LocationSource} which reports a new location whenever a user long presses the map
+ * at
+ * the point at which a user long pressed the map.
+ */
+ private static class LongPressLocationSource implements LocationSource, OnMapLongClickListener {
+
+ private OnLocationChangedListener mListener;
+
+ /**
+ * Flag to keep track of the activity's lifecycle. This is not strictly necessary in this
+ * case because onMapLongPress events don't occur while the activity containing the map is
+ * paused but is included to demonstrate best practices (e.g., if a background service were
+ * to be used).
+ */
+ private boolean mPaused;
+
+ @Override
+ public void activate(OnLocationChangedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void deactivate() {
+ mListener = null;
+ }
+
+ @Override
+ public void onMapLongClick(LatLng point) {
+ if (mListener != null && !mPaused) {
+ Location location = new Location("LongPressLocationProvider");
+ location.setLatitude(point.latitude);
+ location.setLongitude(point.longitude);
+ location.setAccuracy(100);
+ mListener.onLocationChanged(location);
+ }
+ }
+
+ public void onPause() {
+ mPaused = true;
+ }
+
+ public void onResume() {
+ mPaused = false;
+ }
+ }
+
+ private LongPressLocationSource mLocationSource;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.basic_demo);
+
+ mLocationSource = new LongPressLocationSource();
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mLocationSource.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mLocationSource.onPause();
+ }
+
+ @SuppressLint("MissingPermission")
+ @Override
+ public void onMapReady(GoogleMap map) {
+ map.setLocationSource(mLocationSource);
+ map.setOnMapLongClickListener(mLocationSource);
+
+ if (ActivityCompat.checkSelfPermission(this, permission.ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED
+ && ActivityCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ map.setMyLocationEnabled(true);
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MainActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MainActivity.java
new file mode 100644
index 000000000..25be799fd
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MainActivity.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 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.
+ */
+
+package com.example.mapdemo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+/**
+ * The main activity of the API library demo gallery.
+ *
+ * The main layout lists the demonstrated features, with buttons to launch them.
+ */
+public final class MainActivity extends AppCompatActivity {
+
+ private static final String TAG = MainActivity.class.getSimpleName();
+
+ /** A custom array adapter that shows a {@link FeatureView} containing details about the demo. */
+ private static class CustomArrayAdapter extends ArrayAdapter
+ * The activity should implement {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}
+ * to handle permit or denial of this permission request.
+ */
+ public static class RationaleDialog extends DialogFragment {
+
+ private static final String ARGUMENT_PERMISSION_REQUEST_CODE = "requestCode";
+
+ private static final String ARGUMENT_FINISH_ACTIVITY = "finish";
+
+ private boolean finishActivity = false;
+
+ /**
+ * Creates a new instance of a dialog displaying the rationale for the use of the location
+ * permission.
+ *
+ * The permission is requested after clicking 'ok'.
+ *
+ * @param requestCode Id of the request that is used to request the permission. It is
+ * returned to the {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}.
+ * @param finishActivity Whether the calling Activity should be finished if the dialog is
+ * cancelled.
+ */
+ public static RationaleDialog newInstance(int requestCode, boolean finishActivity) {
+ Bundle arguments = new Bundle();
+ arguments.putInt(ARGUMENT_PERMISSION_REQUEST_CODE, requestCode);
+ arguments.putBoolean(ARGUMENT_FINISH_ACTIVITY, finishActivity);
+ RationaleDialog dialog = new RationaleDialog();
+ dialog.setArguments(arguments);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Bundle arguments = getArguments();
+ final int requestCode = arguments.getInt(ARGUMENT_PERMISSION_REQUEST_CODE);
+ finishActivity = arguments.getBoolean(ARGUMENT_FINISH_ACTIVITY);
+
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.permission_rationale_location)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // After click on Ok, request the permission.
+ ActivityCompat.requestPermissions(getActivity(),
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ requestCode);
+ // Do not finish the Activity while requesting permission.
+ finishActivity = false;
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (finishActivity) {
+ Toast.makeText(getActivity(),
+ R.string.permission_required_toast,
+ Toast.LENGTH_SHORT)
+ .show();
+ getActivity().finish();
+ }
+ }
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PolygonDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PolygonDemoActivity.java
new file mode 100644
index 000000000..4be71e21a
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PolygonDemoActivity.java
@@ -0,0 +1,294 @@
+// 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.mapdemo;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.Spinner;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+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.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.Polygon;
+import com.google.android.gms.maps.model.PolygonOptions;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This shows how to draw polygons on a map.
+ */
+// [START maps_android_sample_polygons]
+public class PolygonDemoActivity extends AppCompatActivity
+ implements OnSeekBarChangeListener, OnItemSelectedListener, OnMapReadyCallback {
+
+ private static final LatLng CENTER = new LatLng(-20, 130);
+ private static final int MAX_WIDTH_PX = 100;
+ private static final int MAX_HUE_DEGREES = 360;
+ private static final int MAX_ALPHA = 255;
+
+ private static final int PATTERN_DASH_LENGTH_PX = 50;
+ private static final int PATTERN_GAP_LENGTH_PX = 10;
+ private static final Dot DOT = new Dot();
+ private static final Dash DASH = new Dash(PATTERN_DASH_LENGTH_PX);
+ private static final Gap GAP = new Gap(PATTERN_GAP_LENGTH_PX);
+ private static final List
+ * Storing custom {@link Parcelable} objects directly in the {@link Bundle} provided by the
+ * {@link #onActivityCreated(Bundle)} method will throw a {@code 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.
+ *
+ * However, note that it is safe to store {@link Parcelable} objects from the Maps API (eg.
+ * MarkerOptions, LatLng, etc.) directly in the Bundle provided by the
+ * {@link #onActivityCreated(Bundle)} method.
+ */
+ public static class SaveStateMapFragment extends SupportMapFragment
+ implements OnMarkerClickListener, OnMarkerDragListener, OnMapReadyCallback {
+
+ private LatLng mMarkerPosition;
+
+ private MarkerInfo mMarkerInfo;
+
+ private boolean mMoveCameraToMarker;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ // Activity created for the first time.
+ mMarkerPosition = DEFAULT_MARKER_POSITION;
+ mMarkerInfo = new MarkerInfo(BitmapDescriptorFactory.HUE_RED);
+ mMoveCameraToMarker = true;
+ } else {
+ // 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);
+
+ Bundle bundle = savedInstanceState.getBundle(OTHER_OPTIONS);
+ mMarkerInfo = bundle.getParcelable(MARKER_INFO);
+
+ mMoveCameraToMarker = false;
+ }
+
+ getMapAsync(this);
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ 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.
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(MARKER_INFO, mMarkerInfo);
+ outState.putBundle(OTHER_OPTIONS, bundle);
+ }
+
+ @Override
+ public boolean onMarkerClick(Marker marker) {
+ float newHue = MARKER_HUES[new Random().nextInt(MARKER_HUES.length)];
+ mMarkerInfo.mHue = newHue;
+ marker.setIcon(BitmapDescriptorFactory.defaultMarker(newHue));
+ return true;
+ }
+
+ @Override
+ public void onMapReady(GoogleMap map) {
+ MarkerOptions markerOptions = new MarkerOptions()
+ .position(mMarkerPosition)
+ .icon(BitmapDescriptorFactory.defaultMarker(mMarkerInfo.mHue))
+ .draggable(true);
+ map.addMarker(markerOptions);
+ map.setOnMarkerDragListener(this);
+ map.setOnMarkerClickListener(this);
+
+ if (mMoveCameraToMarker) {
+ map.animateCamera(CameraUpdateFactory.newLatLng(mMarkerPosition));
+ }
+ }
+
+ @Override
+ public void onMarkerDragStart(Marker marker) {
+ }
+
+ @Override
+ public void onMarkerDrag(Marker marker) {
+ }
+
+ @Override
+ public void onMarkerDragEnd(Marker marker) {
+ mMarkerPosition = marker.getPosition();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.save_state_demo);
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/SnapshotDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/SnapshotDemoActivity.java
new file mode 100755
index 000000000..198a44b56
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/SnapshotDemoActivity.java
@@ -0,0 +1,101 @@
+// 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.mapdemo;
+
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.GoogleMap.OnMapLoadedCallback;
+import com.google.android.gms.maps.GoogleMap.SnapshotReadyCallback;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+/**
+ * This shows how to take a snapshot of the map.
+ */
+public class SnapshotDemoActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ /**
+ * Note that this may be null if the Google Play services APK is not available.
+ */
+ private GoogleMap mMap;
+
+ private CheckBox mWaitForMapLoadCheckBox;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.snapshot_demo);
+ mWaitForMapLoadCheckBox = (CheckBox) findViewById(R.id.wait_for_map_load);
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
+ }
+
+ @Override
+ public void onMapReady(GoogleMap map) {
+ mMap = map;
+ }
+
+ /**
+ * Called when the snapshot button is clicked.
+ */
+ public void onScreenshot(View view) {
+ takeSnapshot();
+ }
+
+ private void takeSnapshot() {
+ if (mMap == null) {
+ return;
+ }
+
+ final ImageView snapshotHolder = (ImageView) findViewById(R.id.snapshot_holder);
+
+ final SnapshotReadyCallback callback = new SnapshotReadyCallback() {
+ @Override
+ public void onSnapshotReady(Bitmap snapshot) {
+ // Callback is called from the main thread, so we can modify the ImageView safely.
+ snapshotHolder.setImageBitmap(snapshot);
+ }
+ };
+
+ if (mWaitForMapLoadCheckBox.isChecked()) {
+ mMap.setOnMapLoadedCallback(new OnMapLoadedCallback() {
+ @Override
+ public void onMapLoaded() {
+ mMap.snapshot(callback);
+ }
+ });
+ } else {
+ mMap.snapshot(callback);
+ }
+ }
+
+ /**
+ * Called when the clear button is clicked.
+ */
+ public void onClearScreenshot(View view) {
+ ImageView snapshotHolder = (ImageView) findViewById(R.id.snapshot_holder);
+ snapshotHolder.setImageDrawable(null);
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/SplitStreetViewPanoramaAndMapDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/SplitStreetViewPanoramaAndMapDemoActivity.java
new file mode 100755
index 000000000..7118ee6a8
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/SplitStreetViewPanoramaAndMapDemoActivity.java
@@ -0,0 +1,121 @@
+// 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.mapdemo;
+
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.OnStreetViewPanoramaReadyCallback;
+import com.google.android.gms.maps.StreetViewPanorama;
+import com.google.android.gms.maps.StreetViewPanorama.OnStreetViewPanoramaChangeListener;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.SupportStreetViewPanoramaFragment;
+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.android.gms.maps.model.MarkerOptions;
+import com.google.android.gms.maps.model.StreetViewPanoramaLocation;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+/**
+ * This shows how to create a simple activity with streetview and a map
+ */
+public class SplitStreetViewPanoramaAndMapDemoActivity extends AppCompatActivity
+ implements OnMarkerDragListener, OnStreetViewPanoramaChangeListener {
+
+ private static final String MARKER_POSITION_KEY = "MarkerPosition";
+
+ // George St, Sydney
+ private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689);
+
+ private StreetViewPanorama streetViewPanorama;
+
+ private Marker marker;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.split_street_view_panorama_and_map_demo);
+
+ final LatLng markerPosition;
+ if (savedInstanceState == null) {
+ markerPosition = SYDNEY;
+ } else {
+ markerPosition = savedInstanceState.getParcelable(MARKER_POSITION_KEY);
+ }
+
+ SupportStreetViewPanoramaFragment streetViewPanoramaFragment =
+ (SupportStreetViewPanoramaFragment)
+ getSupportFragmentManager().findFragmentById(R.id.streetviewpanorama);
+ streetViewPanoramaFragment.getStreetViewPanoramaAsync(
+ new OnStreetViewPanoramaReadyCallback() {
+ @Override
+ public void onStreetViewPanoramaReady(StreetViewPanorama panorama) {
+ streetViewPanorama = panorama;
+ streetViewPanorama.setOnStreetViewPanoramaChangeListener(
+ SplitStreetViewPanoramaAndMapDemoActivity.this);
+ // Only need to set the position once as the streetview fragment will maintain
+ // its state.
+ if (savedInstanceState == null) {
+ streetViewPanorama.setPosition(SYDNEY);
+ }
+ }
+ });
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
+ mapFragment.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(GoogleMap map) {
+ map.setOnMarkerDragListener(SplitStreetViewPanoramaAndMapDemoActivity.this);
+ // Creates a draggable marker. Long press to drag.
+ marker = map.addMarker(new MarkerOptions()
+ .position(markerPosition)
+ .icon(BitmapDescriptorFactory.fromResource(R.drawable.pegman))
+ .draggable(true));
+ }
+ });
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(MARKER_POSITION_KEY, marker.getPosition());
+ }
+
+ @Override
+ public void onStreetViewPanoramaChange(StreetViewPanoramaLocation location) {
+ if (location != null) {
+ marker.setPosition(location.position);
+ }
+ }
+
+ @Override
+ public void onMarkerDragStart(Marker marker) {
+ }
+
+ @Override
+ public void onMarkerDragEnd(Marker marker) {
+ streetViewPanorama.setPosition(marker.getPosition(), 150);
+ }
+
+ @Override
+ public void onMarkerDrag(Marker marker) {
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaBasicDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaBasicDemoActivity.java
new file mode 100755
index 000000000..41d72fd3e
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaBasicDemoActivity.java
@@ -0,0 +1,55 @@
+// 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.mapdemo;
+
+import com.google.android.gms.maps.OnStreetViewPanoramaReadyCallback;
+import com.google.android.gms.maps.StreetViewPanorama;
+import com.google.android.gms.maps.SupportStreetViewPanoramaFragment;
+import com.google.android.gms.maps.model.LatLng;
+
+import android.os.Bundle;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+/**
+ * This shows how to create a simple activity with streetview
+ */
+public class StreetViewPanoramaBasicDemoActivity extends AppCompatActivity {
+
+ // George St, Sydney
+ private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689);
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.street_view_panorama_basic_demo);
+
+ SupportStreetViewPanoramaFragment streetViewPanoramaFragment =
+ (SupportStreetViewPanoramaFragment)
+ getSupportFragmentManager().findFragmentById(R.id.streetviewpanorama);
+ streetViewPanoramaFragment.getStreetViewPanoramaAsync(
+ new OnStreetViewPanoramaReadyCallback() {
+ @Override
+ public void onStreetViewPanoramaReady(StreetViewPanorama panorama) {
+ // Only set the panorama to SYDNEY on startup (when no panoramas have been
+ // loaded which is when the savedInstanceState is null).
+ if (savedInstanceState == null) {
+ panorama.setPosition(SYDNEY);
+ }
+ }
+ });
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaEventsDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaEventsDemoActivity.java
new file mode 100755
index 000000000..17ffa6e03
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaEventsDemoActivity.java
@@ -0,0 +1,132 @@
+// 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.mapdemo;
+
+import android.graphics.Point;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.gms.maps.StreetViewPanorama;
+import com.google.android.gms.maps.StreetViewPanorama.OnStreetViewPanoramaCameraChangeListener;
+import com.google.android.gms.maps.StreetViewPanorama.OnStreetViewPanoramaChangeListener;
+import com.google.android.gms.maps.StreetViewPanorama.OnStreetViewPanoramaClickListener;
+import com.google.android.gms.maps.StreetViewPanorama.OnStreetViewPanoramaLongClickListener;
+import com.google.android.gms.maps.SupportStreetViewPanoramaFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.StreetViewPanoramaCamera;
+import com.google.android.gms.maps.model.StreetViewPanoramaLocation;
+import com.google.android.gms.maps.model.StreetViewPanoramaOrientation;
+
+/**
+ * This shows how to listen to some {@link StreetViewPanorama} events.
+ */
+public class StreetViewPanoramaEventsDemoActivity extends AppCompatActivity
+ implements OnStreetViewPanoramaChangeListener, OnStreetViewPanoramaCameraChangeListener,
+ OnStreetViewPanoramaClickListener, OnStreetViewPanoramaLongClickListener {
+
+ // George St, Sydney
+ private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689);
+
+ private StreetViewPanorama streetViewPanorama;
+
+ private TextView panoChangeTimesTextView;
+
+ private TextView panoCameraChangeTextView;
+
+ private TextView panoClickTextView;
+
+ private TextView panoLongClickTextView;
+
+ private int panoChangeTimes = 0;
+
+ private int panoCameraChangeTimes = 0;
+
+ private int panoClickTimes = 0;
+
+ private int panoLongClickTimes = 0;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.street_view_panorama_events_demo);
+
+ panoChangeTimesTextView = findViewById(R.id.change_pano);
+ panoCameraChangeTextView = findViewById(R.id.change_camera);
+ panoClickTextView = findViewById(R.id.click_pano);
+ panoLongClickTextView = findViewById(R.id.long_click_pano);
+
+ SupportStreetViewPanoramaFragment streetViewPanoramaFragment =
+ (SupportStreetViewPanoramaFragment)
+ getSupportFragmentManager().findFragmentById(R.id.streetviewpanorama);
+ streetViewPanoramaFragment.getStreetViewPanoramaAsync(
+ panorama -> {
+ streetViewPanorama = panorama;
+ streetViewPanorama.setOnStreetViewPanoramaChangeListener(
+ StreetViewPanoramaEventsDemoActivity.this);
+ streetViewPanorama.setOnStreetViewPanoramaCameraChangeListener(
+ StreetViewPanoramaEventsDemoActivity.this);
+ streetViewPanorama.setOnStreetViewPanoramaClickListener(
+ StreetViewPanoramaEventsDemoActivity.this);
+ streetViewPanorama.setOnStreetViewPanoramaLongClickListener(
+ StreetViewPanoramaEventsDemoActivity.this);
+
+ // Only set the panorama to SYDNEY on startup (when no panoramas have been
+ // loaded which is when the savedInstanceState is null).
+ if (savedInstanceState == null) {
+ streetViewPanorama.setPosition(SYDNEY);
+ }
+ });
+ }
+
+ @Override
+ public void onStreetViewPanoramaChange(StreetViewPanoramaLocation location) {
+ if (location != null) {
+ panoChangeTimesTextView.setText("Times panorama changed=" + ++panoChangeTimes);
+ }
+ }
+
+ @Override
+ public void onStreetViewPanoramaCameraChange(StreetViewPanoramaCamera camera) {
+ panoCameraChangeTextView.setText("Times camera changed=" + ++panoCameraChangeTimes);
+ }
+
+ @Override
+ public void onStreetViewPanoramaClick(StreetViewPanoramaOrientation orientation) {
+ Point point = streetViewPanorama.orientationToPoint(orientation);
+ if (point != null) {
+ panoClickTimes++;
+ panoClickTextView.setText(
+ "Times clicked=" + panoClickTimes + " : " + point.toString());
+ streetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder()
+ .orientation(orientation)
+ .zoom(streetViewPanorama.getPanoramaCamera().zoom)
+ .build(), 1000);
+ }
+ }
+
+ @Override
+ public void onStreetViewPanoramaLongClick(StreetViewPanoramaOrientation orientation) {
+ Point point = streetViewPanorama.orientationToPoint(orientation);
+ if (point != null) {
+ panoLongClickTimes++;
+ panoLongClickTextView.setText(
+ "Times long clicked=" + panoLongClickTimes + " : " + point.toString());
+ }
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaNavigationDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaNavigationDemoActivity.java
new file mode 100755
index 000000000..e0f8b847d
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaNavigationDemoActivity.java
@@ -0,0 +1,270 @@
+// 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.mapdemo;
+
+import com.google.android.gms.maps.OnStreetViewPanoramaReadyCallback;
+import com.google.android.gms.maps.StreetViewPanorama;
+import com.google.android.gms.maps.SupportStreetViewPanoramaFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.StreetViewPanoramaCamera;
+import com.google.android.gms.maps.model.StreetViewPanoramaLink;
+import com.google.android.gms.maps.model.StreetViewPanoramaLocation;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+
+/**
+ * This shows how to create an activity with access to all the options in Panorama
+ * which can be adjusted dynamically
+ */
+
+public class StreetViewPanoramaNavigationDemoActivity extends AppCompatActivity {
+
+ // George St, Sydney
+ private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689);
+
+ // Cole St, San Fran
+ private static final LatLng SAN_FRAN = new LatLng(37.769263, -122.450727);
+
+ // Santorini, Greece
+ private static final String SANTORINI = "WddsUw1geEoAAAQIt9RnsQ";
+
+ // LatLng with no panorama
+ private static final LatLng INVALID = new LatLng(-45.125783, 151.276417);
+
+ /**
+ * The amount in degrees by which to scroll the camera
+ */
+ private static final int PAN_BY_DEG = 30;
+
+ private static final float ZOOM_BY = 0.5f;
+
+ private StreetViewPanorama mStreetViewPanorama;
+
+ private SeekBar mCustomDurationBar;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.street_view_panorama_navigation_demo);
+
+ SupportStreetViewPanoramaFragment streetViewPanoramaFragment =
+ (SupportStreetViewPanoramaFragment)
+ getSupportFragmentManager().findFragmentById(R.id.streetviewpanorama);
+ streetViewPanoramaFragment.getStreetViewPanoramaAsync(
+ new OnStreetViewPanoramaReadyCallback() {
+ @Override
+ public void onStreetViewPanoramaReady(StreetViewPanorama panorama) {
+ mStreetViewPanorama = panorama;
+ // Only set the panorama to SYDNEY on startup (when no panoramas have been
+ // loaded which is when the savedInstanceState is null).
+ if (savedInstanceState == null) {
+ mStreetViewPanorama.setPosition(SYDNEY);
+ }
+ }
+ });
+ mCustomDurationBar = (SeekBar) findViewById(R.id.duration_bar);
+ }
+
+ /**
+ * When the panorama is not ready the PanoramaView cannot be used. This should be called on
+ * all entry points that call methods on the Panorama API.
+ */
+ private boolean checkReady() {
+ if (mStreetViewPanorama == null) {
+ Toast.makeText(this, R.string.panorama_not_ready, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Called when the Go To San Fran button is clicked.
+ */
+ public void onGoToSanFran(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ mStreetViewPanorama.setPosition(SAN_FRAN, 30);
+ }
+
+ /**
+ * Called when the Animate To Sydney button is clicked.
+ */
+ public void onGoToSydney(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ mStreetViewPanorama.setPosition(SYDNEY);
+ }
+
+ /**
+ * Called when the Animate To Santorini button is clicked.
+ */
+ public void onGoToSantorini(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ mStreetViewPanorama.setPosition(SANTORINI);
+ }
+
+ /**
+ * Called when the Animate To Invalid button is clicked.
+ */
+ public void onGoToInvalid(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ mStreetViewPanorama.setPosition(INVALID);
+ }
+
+ public void onZoomIn(View view) {
+ if (!checkReady()) {
+ return;
+ }
+
+ mStreetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder().zoom(
+ mStreetViewPanorama.getPanoramaCamera().zoom + ZOOM_BY)
+ .tilt(mStreetViewPanorama.getPanoramaCamera().tilt)
+ .bearing(mStreetViewPanorama.getPanoramaCamera().bearing)
+ .build(), getDuration());
+ }
+
+ public void onZoomOut(View view) {
+ if (!checkReady()) {
+ return;
+ }
+
+ mStreetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder().zoom(
+ mStreetViewPanorama.getPanoramaCamera().zoom - ZOOM_BY)
+ .tilt(mStreetViewPanorama.getPanoramaCamera().tilt)
+ .bearing(mStreetViewPanorama.getPanoramaCamera().bearing)
+ .build(), getDuration());
+ }
+
+ public void onPanLeft(View view) {
+ if (!checkReady()) {
+ return;
+ }
+
+ mStreetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder().zoom(
+ mStreetViewPanorama.getPanoramaCamera().zoom)
+ .tilt(mStreetViewPanorama.getPanoramaCamera().tilt)
+ .bearing(mStreetViewPanorama.getPanoramaCamera().bearing - PAN_BY_DEG)
+ .build(), getDuration());
+ }
+
+ public void onPanRight(View view) {
+ if (!checkReady()) {
+ return;
+ }
+
+ mStreetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder().zoom(
+ mStreetViewPanorama.getPanoramaCamera().zoom)
+ .tilt(mStreetViewPanorama.getPanoramaCamera().tilt)
+ .bearing(mStreetViewPanorama.getPanoramaCamera().bearing + PAN_BY_DEG)
+ .build(), getDuration());
+
+ }
+
+ public void onPanUp(View view) {
+ if (!checkReady()) {
+ return;
+ }
+
+ float currentTilt = mStreetViewPanorama.getPanoramaCamera().tilt;
+ float newTilt = currentTilt + PAN_BY_DEG;
+
+ newTilt = (newTilt > 90) ? 90 : newTilt;
+
+ mStreetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder()
+ .zoom(mStreetViewPanorama.getPanoramaCamera().zoom)
+ .tilt(newTilt)
+ .bearing(mStreetViewPanorama.getPanoramaCamera().bearing)
+ .build(), getDuration());
+ }
+
+ public void onPanDown(View view) {
+ if (!checkReady()) {
+ return;
+ }
+
+ float currentTilt = mStreetViewPanorama.getPanoramaCamera().tilt;
+ float newTilt = currentTilt - PAN_BY_DEG;
+
+ newTilt = (newTilt < -90) ? -90 : newTilt;
+
+ mStreetViewPanorama.animateTo(
+ new StreetViewPanoramaCamera.Builder()
+ .zoom(mStreetViewPanorama.getPanoramaCamera().zoom)
+ .tilt(newTilt)
+ .bearing(mStreetViewPanorama.getPanoramaCamera().bearing)
+ .build(), getDuration());
+ }
+
+ public void onRequestPosition(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ if (mStreetViewPanorama.getLocation() != null) {
+ Toast.makeText(view.getContext(), mStreetViewPanorama.getLocation().position.toString(),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ public void onMovePosition(View view) {
+ StreetViewPanoramaLocation location = mStreetViewPanorama.getLocation();
+ StreetViewPanoramaCamera camera = mStreetViewPanorama.getPanoramaCamera();
+ if (location != null && location.links != null) {
+ StreetViewPanoramaLink link = findClosestLinkToBearing(location.links, camera.bearing);
+ mStreetViewPanorama.setPosition(link.panoId);
+ }
+ }
+
+ public static StreetViewPanoramaLink findClosestLinkToBearing(StreetViewPanoramaLink[] links,
+ float bearing) {
+ float minBearingDiff = 360;
+ StreetViewPanoramaLink closestLink = links[0];
+ for (StreetViewPanoramaLink link : links) {
+ if (minBearingDiff > findNormalizedDifference(bearing, link.bearing)) {
+ minBearingDiff = findNormalizedDifference(bearing, link.bearing);
+ closestLink = link;
+ }
+ }
+ return closestLink;
+ }
+
+ // Find the difference between angle a and b as a value between 0 and 180
+ public static float findNormalizedDifference(float a, float b) {
+ float diff = a - b;
+ float normalizedDiff = diff - (float) (360 * Math.floor(diff / 360.0f));
+ return (normalizedDiff < 180.0f) ? normalizedDiff : 360.0f - normalizedDiff;
+ }
+
+ private long getDuration() {
+ return mCustomDurationBar.getProgress();
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaOptionsDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaOptionsDemoActivity.java
new file mode 100755
index 000000000..e8916817e
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaOptionsDemoActivity.java
@@ -0,0 +1,132 @@
+// 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.mapdemo;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.gms.maps.StreetViewPanorama;
+import com.google.android.gms.maps.SupportStreetViewPanoramaFragment;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.StreetViewSource;
+
+/**
+ * This shows how to create an activity with static streetview (all options have been switched off)
+ */
+public class StreetViewPanoramaOptionsDemoActivity extends AppCompatActivity {
+
+ // Cole St, San Fran
+ private static final LatLng SAN_FRAN = new LatLng(37.765927, -122.449972);
+
+ private static int RADIUS = 20;
+
+ private StreetViewPanorama streetViewPanorama;
+
+ private CheckBox streetNameCheckbox;
+
+ private CheckBox navigationCheckbox;
+
+ private CheckBox zoomCheckbox;
+
+ private CheckBox panningCheckbox;
+
+ private CheckBox outdoorCheckbox;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.street_view_panorama_options_demo);
+
+ streetNameCheckbox = findViewById(R.id.streetnames);
+ navigationCheckbox = findViewById(R.id.navigation);
+ zoomCheckbox = findViewById(R.id.zoom);
+ panningCheckbox = findViewById(R.id.panning);
+ outdoorCheckbox = findViewById(R.id.outdoor);
+
+ SupportStreetViewPanoramaFragment streetViewPanoramaFragment =
+ (SupportStreetViewPanoramaFragment)
+ getSupportFragmentManager().findFragmentById(R.id.streetviewpanorama);
+ streetViewPanoramaFragment.getStreetViewPanoramaAsync(
+ panorama -> {
+ streetViewPanorama = panorama;
+ panorama.setStreetNamesEnabled(streetNameCheckbox.isChecked());
+ panorama.setUserNavigationEnabled(navigationCheckbox.isChecked());
+ panorama.setZoomGesturesEnabled(zoomCheckbox.isChecked());
+ panorama.setPanningGesturesEnabled(panningCheckbox.isChecked());
+
+ // Only set the panorama to SAN_FRAN on startup (when no panoramas have been
+ // loaded which is when the savedInstanceState is null).
+ if (savedInstanceState == null) {
+ setPosition();
+ }
+ });
+ }
+
+ private void setPosition() {
+ streetViewPanorama.setPosition(
+ SAN_FRAN,
+ RADIUS,
+ outdoorCheckbox.isChecked() ? StreetViewSource.OUTDOOR : StreetViewSource.DEFAULT
+ );
+ }
+
+ private boolean checkReady() {
+ if (streetViewPanorama == null) {
+ Toast.makeText(this, R.string.map_not_ready, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+ return true;
+ }
+
+ public void onStreetNamesToggled(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ streetViewPanorama.setStreetNamesEnabled(streetNameCheckbox.isChecked());
+ }
+
+ public void onNavigationToggled(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ streetViewPanorama.setUserNavigationEnabled(navigationCheckbox.isChecked());
+ }
+
+ public void onZoomToggled(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ streetViewPanorama.setZoomGesturesEnabled(zoomCheckbox.isChecked());
+ }
+
+ public void onPanningToggled(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ streetViewPanorama.setPanningGesturesEnabled(panningCheckbox.isChecked());
+ }
+
+ public void onOutdoorToggled(View view) {
+ if (!checkReady()) {
+ return;
+ }
+ setPosition();
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaViewDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaViewDemoActivity.java
new file mode 100755
index 000000000..b242a8f94
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StreetViewPanoramaViewDemoActivity.java
@@ -0,0 +1,93 @@
+// 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.mapdemo;
+
+import com.google.android.gms.maps.StreetViewPanoramaOptions;
+import com.google.android.gms.maps.StreetViewPanoramaView;
+import com.google.android.gms.maps.model.LatLng;
+
+import android.os.Bundle;
+import android.view.ViewGroup.LayoutParams;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+
+/**
+ * This shows how to create a simple activity with streetview
+ */
+public class StreetViewPanoramaViewDemoActivity extends AppCompatActivity {
+
+ // George St, Sydney
+ private static final LatLng SYDNEY = new LatLng(-33.87365, 151.20689);
+
+ private StreetViewPanoramaView streetViewPanoramaView;
+
+ private static final String STREETVIEW_BUNDLE_KEY = "StreetViewBundleKey";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ StreetViewPanoramaOptions options = new StreetViewPanoramaOptions();
+ if (savedInstanceState == null) {
+ options.position(SYDNEY);
+ }
+
+ streetViewPanoramaView = new StreetViewPanoramaView(this, options);
+ addContentView(streetViewPanoramaView,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+ // *** IMPORTANT ***
+ // StreetViewPanoramaView requires that the Bundle you pass contain _ONLY_
+ // StreetViewPanoramaView SDK objects or sub-Bundles.
+ Bundle streetViewBundle = null;
+ if (savedInstanceState != null) {
+ streetViewBundle = savedInstanceState.getBundle(STREETVIEW_BUNDLE_KEY);
+ }
+ streetViewPanoramaView.onCreate(streetViewBundle);
+ }
+
+ @Override
+ protected void onResume() {
+ streetViewPanoramaView.onResume();
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ streetViewPanoramaView.onPause();
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ streetViewPanoramaView.onDestroy();
+ super.onDestroy();
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ Bundle streetViewBundle = outState.getBundle(STREETVIEW_BUNDLE_KEY);
+ if (streetViewBundle == null) {
+ streetViewBundle = new Bundle();
+ outState.putBundle(STREETVIEW_BUNDLE_KEY, streetViewBundle);
+ }
+
+ streetViewPanoramaView.onSaveInstanceState(streetViewBundle);
+ }
+}
diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StyledMapDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StyledMapDemoActivity.java
new file mode 100644
index 000000000..933824778
--- /dev/null
+++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/StyledMapDemoActivity.java
@@ -0,0 +1,184 @@
+// 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.mapdemo;
+
+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.LatLng;
+import com.google.android.gms.maps.model.MapStyleOptions;
+
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This shows how to style a map with JSON.
+ */
+public class StyledMapDemoActivity extends AppCompatActivity implements OnMapReadyCallback {
+
+ private GoogleMap mMap = null;
+
+ private static final String TAG = StyledMapDemoActivity.class.getSimpleName();
+
+ private static final String SELECTED_STYLE = "selected_style";
+
+ // Stores the ID of the currently selected style, so that we can re-apply it when
+ // the activity restores state, for example when the device changes orientation.
+ private int mSelectedStyleId = R.string.style_label_default;
+
+ // These are simply the string resource IDs for each of the style names. We use them
+ // as identifiers when choosing which style to apply.
+ private int mStyleIds[] = {
+ R.string.style_label_retro,
+ R.string.style_label_night,
+ R.string.style_label_grayscale,
+ R.string.style_label_no_pois_no_transit,
+ R.string.style_label_default,
+ };
+
+ private static final LatLng SYDNEY = new LatLng(-33.8688, 151.2093);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mSelectedStyleId = savedInstanceState.getInt(SELECTED_STYLE);
+ }
+ setContentView(R.layout.styled_map_demo);
+
+ SupportMapFragment mapFragment =
+ (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
+ mapFragment.getMapAsync(this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ // Store the selected map style, so we can assign it when the activity resumes.
+ outState.putInt(SELECTED_STYLE, mSelectedStyleId);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onMapReady(GoogleMap map) {
+ mMap = map;
+ mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(SYDNEY, 14));
+ setSelectedStyle();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.styled_map, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.menu_style_choose) {
+ showStylesDialog();
+ }
+ return true;
+ }
+
+ /**
+ * Shows a dialog listing the styles to choose from, and applies the selected
+ * style when chosen.
+ */
+ private void showStylesDialog() {
+ // mStyleIds stores each style's resource ID, and we extract the names here, rather
+ // than using an XML array resource which AlertDialog.Builder.setItems() can also
+ // accept. We do this since using an array resource would mean we would not have
+ // constant values we can switch/case on, when choosing which style to apply.
+ List
+ * A workaround to store these objects is to wrap the custom {@link Parcelable} objects in a
+ * new
+ * {@link Bundle} object.
+ *