diff --git a/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java b/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java index 9b9d4c4be..7f7d31729 100644 --- a/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java +++ b/MapboxAndroidDemo/src/global/java/com/mapbox/mapboxandroiddemo/MainActivity.java @@ -95,6 +95,7 @@ import com.mapbox.mapboxandroiddemo.examples.labs.PulsingLayerOpacityColorActivity; import com.mapbox.mapboxandroiddemo.examples.labs.RecyclerViewDirectionsActivity; import com.mapbox.mapboxandroiddemo.examples.labs.RecyclerViewOnMapActivity; +import com.mapbox.mapboxandroiddemo.examples.labs.SharedPreferencesActivity; import com.mapbox.mapboxandroiddemo.examples.labs.SnakingDirectionsRouteActivity; import com.mapbox.mapboxandroiddemo.examples.labs.SpaceStationLocationActivity; import com.mapbox.mapboxandroiddemo.examples.labs.SpinningSymbolLayerIconActivity; @@ -1278,6 +1279,14 @@ private void initializeModels() { null, R.string.activity_lab_change_attribution_color_url, true, BuildConfig.MIN_SDK_VERSION)); + exampleItemModels.add(new ExampleItemModel( + R.id.nav_lab, + R.string.activity_lab_shared_preferences_title, + R.string.activity_lab_shared_preferences_description, + new Intent(MainActivity.this, SharedPreferencesActivity.class), + null, + R.string.activity_lab_shared_preferences_url, true, BuildConfig.MIN_SDK_VERSION)); + exampleItemModels.add(new ExampleItemModel( R.id.nav_dds, R.string.activity_dds_geojson_line_title, diff --git a/MapboxAndroidDemo/src/main/AndroidManifest.xml b/MapboxAndroidDemo/src/main/AndroidManifest.xml index c2478d59f..6997bb0f4 100644 --- a/MapboxAndroidDemo/src/main/AndroidManifest.xml +++ b/MapboxAndroidDemo/src/main/AndroidManifest.xml @@ -285,6 +285,13 @@ android:name="android.support.PARENT_ACTIVITY" android:value="com.mapbox.mapboxandroiddemo.MainActivity" /> + + + diff --git a/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/TilequeryActivity.java b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/TilequeryActivity.java index ab563b457..e596c58f9 100644 --- a/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/TilequeryActivity.java +++ b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/javaservices/TilequeryActivity.java @@ -288,6 +288,9 @@ public void onLowMemory() { @Override protected void onDestroy() { super.onDestroy(); + if (mapboxMap != null) { + mapboxMap.removeOnMapClickListener(this); + } mapView.onDestroy(); } diff --git a/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/labs/SharedPreferencesActivity.java b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/labs/SharedPreferencesActivity.java new file mode 100644 index 000000000..7afe000b4 --- /dev/null +++ b/MapboxAndroidDemo/src/main/java/com/mapbox/mapboxandroiddemo/examples/labs/SharedPreferencesActivity.java @@ -0,0 +1,247 @@ +package com.mapbox.mapboxandroiddemo.examples.labs; + +import android.content.SharedPreferences; +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.widget.TextView; +import android.widget.Toast; + +import com.mapbox.geojson.Point; +import com.mapbox.mapboxandroiddemo.R; +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.maps.Style; +import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset; + + +/** + * Use shared preferences to save and retrieve data, so that the data can be displayed after closing the app. + */ +public class SharedPreferencesActivity extends AppCompatActivity implements OnMapReadyCallback, + MapboxMap.OnMapClickListener { + + private static final String SAVED_LAT_KEY = "SAVED_LAT_KEY"; + private static final String SAVED_LONG_KEY = "SAVED_LONG_KEY"; + private static final String CLICK_LOCATION_SOURCE_ID = "CLICK_LOCATION_SOURCE_ID"; + private static final String CLICK_LOCATION_ICON_ID = "CLICK_LOCATION_ICON_ID"; + private static final String CLICK_LOCATION_LAYER_ID = "CLICK_LOCATION_LAYER_ID"; + private MapView mapView; + private MapboxMap mapboxMap; + private SharedPreferences sharedPreferences; + private TextView longTextView; + private TextView latTextView; + private double savedLat; + private double savedLong; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + // Mapbox access token is configured here. This needs to be called either in your application + // object or in the same activity which contains the mapview. + Mapbox.getInstance(this, getString(R.string.access_token)); + + // This contains the MapView in XML and needs to be called after the access token is configured. + setContentView(R.layout.activity_lab_shared_preferences); + + longTextView = findViewById(R.id.shared_pref_saved_long_textview); + latTextView = findViewById(R.id.shared_pref_saved_lat_textview); + longTextView.setText(String.format(getString(R.string.saved_long_textview), String.valueOf(0))); + latTextView.setText(String.format(getString(R.string.saved_lat_textview), String.valueOf(0))); + + mapView = findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull MapboxMap mapboxMap) { + SharedPreferencesActivity.this.mapboxMap = mapboxMap; + mapboxMap.setStyle( + + // Set the map to Mapbox's daytime traffic style + new Style.Builder().fromUri(Style.TRAFFIC_DAY) + + // Add the SymbolLayer icon image to the map style + .withImage(CLICK_LOCATION_ICON_ID, BitmapFactory.decodeResource( + SharedPreferencesActivity.this.getResources(), R.drawable.red_marker)) + + // Adding a GeoJson source for the SymbolLayer icons. + .withSource(new GeoJsonSource(CLICK_LOCATION_SOURCE_ID)) + + // Adding the actual SymbolLayer to the map style. An offset is added that the bottom of the red + // marker icon gets fixed to the coordinate, rather than the middle of the icon being fixed to + // the coordinate point. This is offset is not always needed and is dependent on the image + // that you use for the SymbolLayer icon. + .withLayer(new SymbolLayer(CLICK_LOCATION_LAYER_ID, CLICK_LOCATION_SOURCE_ID) + .withProperties(PropertyFactory.iconImage(CLICK_LOCATION_ICON_ID), + iconAllowOverlap(true), + iconOffset(new Float[] {0f, -9f})) + ), new Style.OnStyleLoaded() { + @Override + public void onStyleLoaded(@NonNull Style style) { + + mapboxMap.addOnMapClickListener(SharedPreferencesActivity.this); + + // Get the coordinates from shared preferences + savedLong = getCoordinateFromSharedPref(SAVED_LONG_KEY); + savedLat = getCoordinateFromSharedPref(SAVED_LAT_KEY); + + // Coordinates haven't been saved if both == 0 + if (savedLong == 0 && savedLat == 0) { + Toast.makeText(SharedPreferencesActivity.this, + getString(R.string.tap_on_map_save_to_shared_pref), Toast.LENGTH_SHORT).show(); + + longTextView.setText(String.format(getString(R.string.saved_long_textview), + getString(R.string.not_saved_yet))); + latTextView.setText(String.format(getString(R.string.saved_lat_textview), + getString(R.string.not_saved_yet))); + + } else { + + // Move the camera to the previously-saved coordinates + mapboxMap.animateCamera(CameraUpdateFactory + .newCameraPosition(new CameraPosition.Builder() + .target(new LatLng(savedLat, savedLong)) + .zoom(4) + .build()), 1200); + + Toast.makeText(SharedPreferencesActivity.this, + getString(R.string.shared_pref_marker_placement), Toast.LENGTH_SHORT).show(); + + // Move the marker to the previously-saved coordinates + moveMarkerToLngLat(savedLong, savedLat); + + longTextView.setText(String.format( + getString(R.string.saved_long_textview), String.valueOf(savedLong))); + + latTextView.setText(String.format( + getString(R.string.saved_lat_textview), String.valueOf(savedLat))); + } + } + }); + } + + @Override + public boolean onMapClick(@NonNull LatLng mapClickPoint) { + double clickLatitude = mapClickPoint.getLatitude(); + double clickLongitude = mapClickPoint.getLongitude(); + + longTextView.setText(String.format( + getString(R.string.saved_long_textview), String.valueOf(clickLongitude))); + latTextView.setText(String.format( + getString(R.string.saved_lat_textview), String.valueOf(clickLatitude))); + + // Save the map click point coordinates to shared preferences + if (sharedPreferences != null) { + putCoordinateToSharedPref(SAVED_LAT_KEY, clickLatitude); + putCoordinateToSharedPref(SAVED_LONG_KEY, clickLongitude); + } + + // Move the marker to the newly-saved coordinates + moveMarkerToLngLat(clickLongitude, clickLatitude); + return true; + } + + /** + * Move the SymbolLayer icon to a new location + * + * @param newLong the new longitude + * @param newLat the new latitude + */ + private void moveMarkerToLngLat(double newLong, double newLat) { + // Move and display the click center layer's red marker icon to + // wherever the map was clicked on + mapboxMap.getStyle(new Style.OnStyleLoaded() { + @Override + public void onStyleLoaded(@NonNull Style style) { + GeoJsonSource clickLocationSource = style.getSourceAs(CLICK_LOCATION_SOURCE_ID); + if (clickLocationSource != null) { + clickLocationSource.setGeoJson(Point.fromLngLat(newLong, newLat)); + } + } + }); + } + + /** + * Save a specific number to shared preferences + * + * @param key the number's key + * @param value the actual number + */ + private void putCoordinateToSharedPref(final String key, final double value) { + sharedPreferences.edit().putLong(key, Double.doubleToRawLongBits(value)).apply(); + } + + /** + * Retrieve a specific number from shared preferences + * + * @param key the key to use for retrieval + * @return the saved number + */ + double getCoordinateFromSharedPref(final String key) { + return Double.longBitsToDouble(sharedPreferences.getLong(key, 0)); + } + + // Add the mapView lifecycle to the activity's lifecycle methods + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mapboxMap != null) { + mapboxMap.removeOnMapClickListener(this); + } + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/MapboxAndroidDemo/src/main/res/layout/activity_lab_shared_preferences.xml b/MapboxAndroidDemo/src/main/res/layout/activity_lab_shared_preferences.xml new file mode 100644 index 000000000..ef3735f27 --- /dev/null +++ b/MapboxAndroidDemo/src/main/res/layout/activity_lab_shared_preferences.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/MapboxAndroidDemo/src/main/res/values/activity_strings.xml b/MapboxAndroidDemo/src/main/res/values/activity_strings.xml index e2fca158a..3a0a3cfa8 100644 --- a/MapboxAndroidDemo/src/main/res/values/activity_strings.xml +++ b/MapboxAndroidDemo/src/main/res/values/activity_strings.xml @@ -453,4 +453,11 @@ Error with the request. Do you have internet access? No features in the response. %1$d Features in the API response: %2$s + + + Not saved yet + Saved latitude: %1$s + Saved longitude: %1$s + Tap on the map to save coordinates to the device\'s Shared Preferences + Marker placed at coordinates saved in Shared Preferences. \ No newline at end of file diff --git a/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml b/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml index e8dd91783..248fc5aba 100644 --- a/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml +++ b/MapboxAndroidDemo/src/main/res/values/descriptions_strings.xml @@ -140,6 +140,7 @@ Quickly show the directions route associated with a RecyclerView item. Use a ValueAnimator to adjust SymbolLayer icons\' rotation values and create a spinning effect. Adjust the attribution "i" to match a map style, app UI, or color motif. + Use the Android system\'s Shared Preferences to save and retrieve coordinates. Show an accurate and government-approved China map in your app using the Mapbox Maps SDK. \ No newline at end of file diff --git a/MapboxAndroidDemo/src/main/res/values/titles_strings.xml b/MapboxAndroidDemo/src/main/res/values/titles_strings.xml index 637dafef4..a44f84996 100644 --- a/MapboxAndroidDemo/src/main/res/values/titles_strings.xml +++ b/MapboxAndroidDemo/src/main/res/values/titles_strings.xml @@ -139,4 +139,5 @@ RecyclerView Directions Spinning icon Style attribution + Shared preferences diff --git a/MapboxAndroidDemo/src/main/res/values/urls_strings.xml b/MapboxAndroidDemo/src/main/res/values/urls_strings.xml index 0b684f1f6..e0d8177bd 100644 --- a/MapboxAndroidDemo/src/main/res/values/urls_strings.xml +++ b/MapboxAndroidDemo/src/main/res/values/urls_strings.xml @@ -139,5 +139,6 @@ https://i.imgur.com/LurOuXQ.png https://i.imgur.com/jxQpAt2.png https://i.imgur.com/cGv98jb.png + https://i.imgur.com/znxAhDG.png https://i.imgur.com/KwoEynZ.png