diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java index 4a9adba48..92ca2494d 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java @@ -14,32 +14,27 @@ public class ExploreMapContract { interface View { boolean isNetworkConnectionEstablished(); - void populatePlaces(LatLng curlatLng,LatLng searchLatLng); + void populatePlaces(LatLng curlatLng); void checkPermissionsAndPerformAction(); void recenterMap(LatLng curLatLng); void showLocationOffDialog(); void openLocationSettings(); void hideBottomDetailsSheet(); void displayBottomSheetWithInfo(Marker marker); - void addOnCameraMoveListener(); + LatLng getMapCenter(); + LatLng getMapFocus(); + LatLng getLastMapFocus(); + void addMarkersToMap(final List nearbyBaseMarkers); + void clearAllMarkers(); void addSearchThisAreaButtonAction(); void setSearchThisAreaButtonVisibility(boolean isVisible); void setProgressBarVisibility(boolean isVisible); boolean isDetailsBottomSheetVisible(); boolean isSearchThisAreaButtonVisible(); - void addCurrentLocationMarker(LatLng curLatLng); - void updateMapToTrackPosition(LatLng curLatLng); Context getContext(); - LatLng getCameraTarget(); - void centerMapToPlace(Place placeToCenter); LatLng getLastLocation(); - com.mapbox.mapboxsdk.geometry.LatLng getLastFocusLocation(); - boolean isCurrentLocationMarkerVisible(); - void setProjectorLatLngBounds(); void disableFABRecenter(); void enableFABRecenter(); - void addNearbyMarkersToMapBoxMap(final List nearbyBaseMarkers, final Marker selectedMarker); - void setMapBoundaries(CameraUpdate cameaUpdate); void setFABRecenterAction(android.view.View.OnClickListener onClickListener); boolean backButtonClicked(); } @@ -51,9 +46,6 @@ public class ExploreMapContract { void detachView(); void setActionListeners(JsonKvStore applicationKvStore); boolean backButtonClicked(); - void onCameraMove(com.mapbox.mapboxsdk.geometry.LatLng latLng); - void markerUnselected(); - void markerSelected(Marker marker); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java index 72a4b216b..f74025fd8 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java @@ -33,6 +33,7 @@ import javax.inject.Inject; import timber.log.Timber; public class ExploreMapController extends MapController { + private final ExploreMapCalls exploreMapCalls; public LatLng latestSearchLocation; // Can be current and camera target on search this area button is used public LatLng currentLocation; // current location of user @@ -46,13 +47,18 @@ public class ExploreMapController extends MapController { } /** - * Takes location as parameter and returns ExplorePlaces info that holds curLatLng, mediaList, explorePlaceList and boundaryCoordinates - * @param curLatLng is current geolocation - * @param searchLatLng is the location that we want to search around - * @param checkingAroundCurrentLocation is a boolean flag. True if we want to check around current location, false if another location - * @return explorePlacesInfo info that holds curLatLng, mediaList, explorePlaceList and boundaryCoordinates + * Takes location as parameter and returns ExplorePlaces info that holds curLatLng, mediaList, + * explorePlaceList and boundaryCoordinates + * + * @param curLatLng is current geolocation + * @param searchLatLng is the location that we want to search around + * @param checkingAroundCurrentLocation is a boolean flag. True if we want to check around + * current location, false if another location + * @return explorePlacesInfo info that holds curLatLng, mediaList, explorePlaceList and + * boundaryCoordinates */ - public ExplorePlacesInfo loadAttractionsFromLocation(LatLng curLatLng, LatLng searchLatLng, boolean checkingAroundCurrentLocation) { + public ExplorePlacesInfo loadAttractionsFromLocation(LatLng curLatLng, LatLng searchLatLng, + boolean checkingAroundCurrentLocation) { if (searchLatLng == null) { Timber.d("Loading attractions explore map, but search is null"); @@ -74,18 +80,23 @@ public class ExploreMapController extends MapController { Timber.d("Sorting places by distance..."); final Map distances = new HashMap<>(); for (Media media : mediaList) { - distances.put(media, computeDistanceBetween(media.getCoordinates(), searchLatLng)); + distances.put(media, + computeDistanceBetween(media.getCoordinates(), searchLatLng)); // Find boundaries with basic find max approach - if (media.getCoordinates().getLatitude() < boundaryCoordinates[0].getLatitude()) { + if (media.getCoordinates().getLatitude() + < boundaryCoordinates[0].getLatitude()) { boundaryCoordinates[0] = media.getCoordinates(); } - if (media.getCoordinates().getLatitude() > boundaryCoordinates[1].getLatitude()) { + if (media.getCoordinates().getLatitude() + > boundaryCoordinates[1].getLatitude()) { boundaryCoordinates[1] = media.getCoordinates(); } - if (media.getCoordinates().getLongitude() < boundaryCoordinates[2].getLongitude()) { + if (media.getCoordinates().getLongitude() + < boundaryCoordinates[2].getLongitude()) { boundaryCoordinates[2] = media.getCoordinates(); } - if (media.getCoordinates().getLongitude() > boundaryCoordinates[3].getLongitude()) { + if (media.getCoordinates().getLongitude() + > boundaryCoordinates[3].getLongitude()) { boundaryCoordinates[3] = media.getCoordinates(); } } @@ -96,7 +107,8 @@ public class ExploreMapController extends MapController { // Sets latestSearchRadius to maximum distance among boundaries and search location for (LatLng bound : boundaryCoordinates) { - double distance = LocationUtils.commonsLatLngToMapBoxLatLng(bound).distanceTo(LocationUtils.commonsLatLngToMapBoxLatLng(latestSearchLocation)); + double distance = LocationUtils.commonsLatLngToMapBoxLatLng(bound) + .distanceTo(LocationUtils.commonsLatLngToMapBoxLatLng(latestSearchLocation)); if (distance > latestSearchRadius) { latestSearchRadius = distance; } @@ -115,6 +127,7 @@ public class ExploreMapController extends MapController { /** * Loads attractions from location for map view, we need to return places in Place data type + * * @return baseMarkerOptions list that holds nearby places with their icons */ public static List loadAttractionsFromLocationToBaseMarkerOptions( @@ -123,7 +136,6 @@ public class ExploreMapController extends MapController { Context context, NearbyBaseMarkerThumbCallback callback, Marker selectedMarker, - boolean shouldTrackPosition, ExplorePlacesInfo explorePlacesInfo) { List baseMarkerOptions = new ArrayList<>(); @@ -145,7 +157,8 @@ public class ExploreMapController extends MapController { String distance = formatDistanceBetween(curLatLng, explorePlace.location); explorePlace.setDistance(distance); - nearbyBaseMarker.title(explorePlace.name.substring(5, explorePlace.name.lastIndexOf("."))); + nearbyBaseMarker.title( + explorePlace.name.substring(5, explorePlace.name.lastIndexOf("."))); nearbyBaseMarker.position( new com.mapbox.mapboxsdk.geometry.LatLng( explorePlace.location.getLatitude(), @@ -160,12 +173,15 @@ public class ExploreMapController extends MapController { .into(new CustomTarget() { // We add icons to markers when bitmaps are ready @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + public void onResourceReady(@NonNull Bitmap resource, + @Nullable Transition transition) { nearbyBaseMarker.setIcon(IconFactory.getInstance(context).fromBitmap( ImageUtils.addRedBorder(resource, 6, context))); baseMarkerOptions.add(nearbyBaseMarker); - if (baseMarkerOptions.size() == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback - callback.onNearbyBaseMarkerThumbsReady(baseMarkerOptions, explorePlacesInfo, selectedMarker, shouldTrackPosition); + if (baseMarkerOptions.size() + == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback + callback.onNearbyBaseMarkerThumbsReady(baseMarkerOptions, + explorePlacesInfo, selectedMarker); } } @@ -177,10 +193,13 @@ public class ExploreMapController extends MapController { @Override public void onLoadFailed(@Nullable final Drawable errorDrawable) { super.onLoadFailed(errorDrawable); - nearbyBaseMarker.setIcon(IconFactory.getInstance(context).fromResource(R.drawable.image_placeholder_96)); + nearbyBaseMarker.setIcon(IconFactory.getInstance(context) + .fromResource(R.drawable.image_placeholder_96)); baseMarkerOptions.add(nearbyBaseMarker); - if (baseMarkerOptions.size() == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback - callback.onNearbyBaseMarkerThumbsReady(baseMarkerOptions, explorePlacesInfo, selectedMarker, shouldTrackPosition); + if (baseMarkerOptions.size() + == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback + callback.onNearbyBaseMarkerThumbsReady(baseMarkerOptions, + explorePlacesInfo, selectedMarker); } } }); @@ -190,7 +209,9 @@ public class ExploreMapController extends MapController { } interface NearbyBaseMarkerThumbCallback { + // Callback to notify thumbnails of explore markers are added as icons and ready - void onNearbyBaseMarkerThumbsReady(List baseMarkers, ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker, boolean shouldTrackPosition); + void onNearbyBaseMarkerThumbsReady(List baseMarkers, + ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker); } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java index b51e74af9..7d6f6cb48 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java @@ -2,8 +2,6 @@ package fr.free.nrw.commons.explore.map; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; -import static fr.free.nrw.commons.utils.MapUtils.CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE; -import static fr.free.nrw.commons.utils.MapUtils.CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT; import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL; import android.Manifest; @@ -14,14 +12,18 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.location.Location; import android.location.LocationManager; import android.os.Bundle; +import android.preference.PreferenceManager; import android.provider.Settings; import android.text.Html; -import android.view.Gravity; import android.view.LayoutInflater; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -36,29 +38,14 @@ import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatTextView; +import androidx.core.content.ContextCompat; import butterknife.BindView; import butterknife.ButterKnife; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.PolygonOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Style; -import com.mapbox.mapboxsdk.maps.UiSettings; -import com.mapbox.pluginscalebar.ScaleBarOptions; -import com.mapbox.pluginscalebar.ScaleBarPlugin; import fr.free.nrw.commons.MapController; -import fr.free.nrw.commons.MapStyle; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; @@ -75,22 +62,35 @@ import fr.free.nrw.commons.nearby.NearbyBaseMarker; import fr.free.nrw.commons.nearby.NearbyMarker; import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.utils.DialogUtil; -import fr.free.nrw.commons.utils.ExecutorUtils; -import fr.free.nrw.commons.utils.LocationUtils; import fr.free.nrw.commons.utils.MapUtils; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.SystemThemeUtils; -import fr.free.nrw.commons.utils.UiUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.inject.Inject; import javax.inject.Named; +import org.osmdroid.events.MapEventsReceiver; +import org.osmdroid.events.MapListener; +import org.osmdroid.events.ScrollEvent; +import org.osmdroid.events.ZoomEvent; +import org.osmdroid.tileprovider.tilesource.TileSourceFactory; +import org.osmdroid.util.GeoPoint; +import org.osmdroid.util.constants.GeoConstants; +import org.osmdroid.views.CustomZoomButtonsController; +import org.osmdroid.views.overlay.ItemizedIconOverlay.OnItemGestureListener; +import org.osmdroid.views.overlay.ItemizedOverlayWithFocus; +import org.osmdroid.views.overlay.MapEventsOverlay; +import org.osmdroid.views.overlay.Overlay; +import org.osmdroid.views.overlay.OverlayItem; +import org.osmdroid.views.overlay.ScaleBarOverlay; +import org.osmdroid.views.overlay.ScaleDiskOverlay; +import org.osmdroid.views.overlay.TilesOverlay; import timber.log.Timber; public class ExploreMapFragment extends CommonsDaggerSupportFragment @@ -102,20 +102,14 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment private Snackbar snackbar; private boolean isDarkTheme; private boolean isPermissionDenied; - private fr.free.nrw.commons.location.LatLng lastKnownLocation; // lask location of user + private fr.free.nrw.commons.location.LatLng lastKnownLocation; // last location of user private fr.free.nrw.commons.location.LatLng lastFocusLocation; // last location that map is focused public List mediaList; private boolean recenterToUserLocation; // true is recenter is needed (ie. when current location is in visible map boundaries) - - - private MapboxMap.OnCameraMoveListener cameraMoveListener; - private MapboxMap mapBox; - private Place lastPlaceToCenter; // the last place that we centered the map - private boolean isMapBoxReady; + private NearbyBaseMarker clickedMarker; private Marker selectedMarker; // the marker that user selected - private LatLngBounds projectorLatLngBounds; // current windows borders - private Marker currentLocationMarker; - private Polygon currentLocationPolygon; + private GeoPoint mapCenter; + private GeoPoint lastMapFocus; IntentFilter intentFilter = new IntentFilter(MapUtils.NETWORK_INTENT_ACTION); @Inject @@ -126,7 +120,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment LocationServiceManager locationManager; @Inject ExploreMapController exploreMapController; - @Inject @Named("default_preferences") + @Inject + @Named("default_preferences") JsonKvStore applicationKvStore; @Inject BookmarkLocationsDao bookmarkLocationDao; // May be needed in future if we want to integrate bookmarking explore places @@ -135,51 +130,67 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment private ExploreMapPresenter presenter; - @BindView(R.id.map_view) MapView mapView; - @BindView(R.id.bottom_sheet_details) View bottomSheetDetails; - @BindView(R.id.map_progress_bar) ProgressBar progressBar; - @BindView(R.id.fab_recenter) FloatingActionButton fabRecenter; - @BindView(R.id.search_this_area_button) Button searchThisAreaButton; - @BindView(R.id.tv_attribution) AppCompatTextView tvAttribution; + @BindView(R.id.map_view) + org.osmdroid.views.MapView mapView; + @BindView(R.id.bottom_sheet_details) + View bottomSheetDetails; + @BindView(R.id.map_progress_bar) + ProgressBar progressBar; + @BindView(R.id.fab_recenter) + FloatingActionButton fabRecenter; + @BindView(R.id.search_this_area_button) + Button searchThisAreaButton; + @BindView(R.id.tv_attribution) + AppCompatTextView tvAttribution; - @BindView(R.id.directionsButton) LinearLayout directionsButton; - @BindView(R.id.commonsButton) LinearLayout commonsButton; - @BindView(R.id.mediaDetailsButton) LinearLayout mediaDetailsButton; - @BindView(R.id.description) TextView description; - @BindView(R.id.title) TextView title; - @BindView(R.id.category) TextView distance; + @BindView(R.id.directionsButton) + LinearLayout directionsButton; + @BindView(R.id.commonsButton) + LinearLayout commonsButton; + @BindView(R.id.mediaDetailsButton) + LinearLayout mediaDetailsButton; + @BindView(R.id.description) + TextView description; + @BindView(R.id.title) + TextView title; + @BindView(R.id.category) + TextView distance; - private ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback>() { - @Override - public void onActivityResult(Map result) { - boolean areAllGranted = true; - for(final boolean b : result.values()) { - areAllGranted = areAllGranted && b; - } - - if (areAllGranted) { - locationPermissionGranted(); - } else { - if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { - DialogUtil.showAlertDialog(getActivity(), getActivity().getString(R.string.location_permission_title), - getActivity().getString(R.string.location_permission_rationale_nearby), - getActivity().getString(android.R.string.ok), - getActivity().getString(android.R.string.cancel), - () -> { - if (!(locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled())) { - showLocationOffDialog(); - } - }, - () -> isPermissionDenied = true, - null, - false); - } else { - isPermissionDenied = true; + private ActivityResultLauncher activityResultLauncher = registerForActivityResult( + new ActivityResultContracts.RequestMultiplePermissions(), + new ActivityResultCallback>() { + @Override + public void onActivityResult(Map result) { + boolean areAllGranted = true; + for (final boolean b : result.values()) { + areAllGranted = areAllGranted && b; } + if (areAllGranted) { + locationPermissionGranted(); + } else { + if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { + DialogUtil.showAlertDialog(getActivity(), + getActivity().getString(R.string.location_permission_title), + getActivity().getString(R.string.location_permission_rationale_nearby), + getActivity().getString(android.R.string.ok), + getActivity().getString(android.R.string.cancel), + () -> { + if (!(locationManager.isNetworkProviderEnabled() + || locationManager.isGPSProviderEnabled())) { + showLocationOffDialog(); + } + }, + () -> isPermissionDenied = true, + null, + false); + } else { + isPermissionDenied = true; + } + + } } - } - }); + }); @NonNull @@ -208,7 +219,6 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment @Override public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - mapView.onStart(); setSearchThisAreaButtonVisibility(false); tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); initNetworkBroadCastReceiver(); @@ -220,43 +230,93 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment isDarkTheme = systemThemeUtils.isDeviceInNightMode(); isPermissionDenied = false; - cameraMoveListener= () -> presenter.onCameraMove(mapBox.getCameraPosition().target); presenter.attachView(this); - recenterToUserLocation = false; - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(mapBoxMap -> { - mapBox = mapBoxMap; - initViews(); - presenter.setActionListeners(applicationKvStore); - mapBoxMap.setStyle(isDarkTheme? MapStyle.DARK : - MapStyle.OUTDOORS, style -> { - final UiSettings uiSettings = mapBoxMap.getUiSettings(); - uiSettings.setCompassGravity(Gravity.BOTTOM | Gravity.LEFT); - uiSettings.setCompassMargins(12, 0, 0, 24); - uiSettings.setLogoEnabled(false); - uiSettings.setAttributionEnabled(false); - uiSettings.setRotateGesturesEnabled(false); - isMapBoxReady = true; - performMapReadyActions(); - final CameraPosition cameraPosition = new CameraPosition.Builder() - .target(new com.mapbox.mapboxsdk.geometry.LatLng(51.50550, -0.07520)) - .zoom(MapUtils.ZOOM_OUT) - .build(); - mapBoxMap.setCameraPosition(cameraPosition); - final ScaleBarPlugin scaleBarPlugin = new ScaleBarPlugin(mapView, mapBoxMap); - final int color = isDarkTheme ? R.color.bottom_bar_light : R.color.bottom_bar_dark; - final ScaleBarOptions scaleBarOptions = new ScaleBarOptions(getContext()) - .setTextColor(color) - .setTextSize(R.dimen.description_text_size) - .setBarHeight(R.dimen.tiny_gap) - .setBorderWidth(R.dimen.miniscule_margin) - .setMarginTop(R.dimen.tiny_padding) - .setMarginLeft(R.dimen.tiny_padding) - .setTextBarMargin(R.dimen.tiny_padding); - scaleBarPlugin.create(scaleBarOptions); - }); + initViews(); + presenter.setActionListeners(applicationKvStore); + + org.osmdroid.config.Configuration.getInstance().load(this.getContext(), + PreferenceManager.getDefaultSharedPreferences(this.getContext())); + + mapView.setTileSource(TileSourceFactory.WIKIMEDIA); + mapView.setTilesScaledToDpi(true); + + org.osmdroid.config.Configuration.getInstance().getAdditionalHttpRequestProperties().put( + "Referer", "http://maps.wikimedia.org/" + ); + + ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(mapView); + scaleBarOverlay.setScaleBarOffset(15, 25); + Paint barPaint = new Paint(); + barPaint.setARGB(200, 255, 250, 250); + scaleBarOverlay.setBackgroundPaint(barPaint); + scaleBarOverlay.enableScaleBar(); + mapView.getOverlays().add(scaleBarOverlay); + mapView.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER); + mapView.setMultiTouchControls(true); + mapView.getController().setZoom(ZOOM_LEVEL); + performMapReadyActions(); + + mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() { + @Override + public boolean singleTapConfirmedHelper(GeoPoint p) { + if (clickedMarker != null) { + removeMarker(clickedMarker); + addMarkerToMap(clickedMarker); + mapView.invalidate(); + } else { + Timber.e("CLICKED MARKER IS NULL"); + } + if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { + // Back should first hide the bottom sheet if it is expanded + bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + } else if (isDetailsBottomSheetVisible()) { + hideBottomDetailsSheet(); + } + return true; + } + + @Override + public boolean longPressHelper(GeoPoint p) { + return false; + } + })); + + mapView.addMapListener(new MapListener() { + @Override + public boolean onScroll(ScrollEvent event) { + if (getLastMapFocus() != null) { + Location mylocation = new Location(""); + Location dest_location = new Location(""); + dest_location.setLatitude(mapView.getMapCenter().getLatitude()); + dest_location.setLongitude(mapView.getMapCenter().getLongitude()); + mylocation.setLatitude(getLastMapFocus().getLatitude()); + mylocation.setLongitude(getLastMapFocus().getLongitude()); + Float distance = mylocation.distanceTo(dest_location);//in meters + if (getLastMapFocus() != null) { + if (isNetworkConnectionEstablished() && (event.getX() > 0 + || event.getY() > 0)) { + if (distance > 2000.0) { + setSearchThisAreaButtonVisibility(true); + } else { + setSearchThisAreaButtonVisibility(false); + } + } + } else { + setSearchThisAreaButtonVisibility(false); + } + } + + return true; + } + + @Override + public boolean onZoom(ZoomEvent event) { + return false; + } + }); + } @Override @@ -265,29 +325,21 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment mapView.onResume(); presenter.attachView(this); registerNetworkReceiver(); - if (isResumed()) { - if (!isPermissionDenied && !applicationKvStore - .getBoolean("doNotAskForLocationPermission", false)) { - startTheMap(); - } else { - startMapWithoutPermission(); - } + if (isResumed()) { + if (!isPermissionDenied && !applicationKvStore + .getBoolean("doNotAskForLocationPermission", false)) { + performMapReadyActions(); + } else { + startMapWithoutPermission(); } - } - - private void startTheMap() { - mapView.onStart(); - performMapReadyActions(); + } } private void startMapWithoutPermission() { - mapView.onStart(); applicationKvStore.putBoolean("doNotAskForLocationPermission", true); lastKnownLocation = MapUtils.defaultLatLng; - MapUtils.centerMapToDefaultLatLng(mapBox); - if (mapBox != null) { - addOnCameraMoveListener(); - } + moveCameraToPosition( + new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); presenter.onMapReady(exploreMapController); } @@ -298,14 +350,16 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } private void performMapReadyActions() { - if (isMapBoxReady) { - if(!applicationKvStore.getBoolean("doNotAskForLocationPermission", false) || - PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})){ - checkPermissionsAndPerformAction(); - }else{ - isPermissionDenied = true; - addOnCameraMoveListener(); - } + if (isDarkTheme) { + mapView.getOverlayManager().getTilesOverlay() + .setColorFilter(TilesOverlay.INVERT_COLORS); + } + if (!applicationKvStore.getBoolean("doNotAskForLocationPermission", false) || + PermissionUtils.hasPermission(getActivity(), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { + checkPermissionsAndPerformAction(); + } else { + isPermissionDenied = true; } } @@ -316,32 +370,15 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } /** - * a) Creates bottom sheet behaviours from bottom sheet, sets initial states and visibility - * b) Gets the touch event on the map to perform following actions: - * if bottom sheet details are expanded or collapsed hide the bottom sheet details. + * a) Creates bottom sheet behaviours from bottom sheet, sets initial states and visibility b) + * Gets the touch event on the map to perform following actions: if bottom sheet details are + * expanded or collapsed hide the bottom sheet details. */ @SuppressLint("ClickableViewAccessibility") private void initBottomSheets() { bottomSheetDetailsBehavior = BottomSheetBehavior.from(bottomSheetDetails); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); bottomSheetDetails.setVisibility(View.VISIBLE); - - mapView.setOnTouchListener((v, event) -> { - - // Motion event is triggered two times on a touch event, one as ACTION_UP - // and other as ACTION_DOWN, we only want one trigger per touch event. - - if(event.getAction() == MotionEvent.ACTION_DOWN) { - if (bottomSheetDetailsBehavior.getState() - == BottomSheetBehavior.STATE_EXPANDED) { - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - } else if (bottomSheetDetailsBehavior.getState() - == BottomSheetBehavior.STATE_COLLAPSED) { - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - } - } - return false; - }); } /** @@ -351,7 +388,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment bottomSheetDetails.setOnClickListener(v -> { if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) { bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - } else if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { + } else if (bottomSheetDetailsBehavior.getState() + == BottomSheetBehavior.STATE_EXPANDED) { bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } }); @@ -360,25 +398,21 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment @Override public void onLocationChangedSignificantly(LatLng latLng) { Timber.d("Location significantly changed"); - if (isMapBoxReady && latLng != null &&!isUserBrowsing()) { - handleLocationUpdate(latLng,LOCATION_SIGNIFICANTLY_CHANGED); + if (latLng != null) { + handleLocationUpdate(latLng, LOCATION_SIGNIFICANTLY_CHANGED); } } - private boolean isUserBrowsing() { - final boolean isUserBrowsing = lastKnownLocation!=null && !presenter.areLocationsClose(getCameraTarget(), lastKnownLocation); - return isUserBrowsing; - } - @Override public void onLocationChangedSlightly(LatLng latLng) { Timber.d("Location slightly changed"); - if (isMapBoxReady && latLng != null &&!isUserBrowsing()) {//If the map has never ever shown the current location, lets do it know - handleLocationUpdate(latLng,LOCATION_SLIGHTLY_CHANGED); + if (latLng != null) {//If the map has never ever shown the current location, lets do it know + handleLocationUpdate(latLng, LOCATION_SLIGHTLY_CHANGED); } } - private void handleLocationUpdate(final fr.free.nrw.commons.location.LatLng latLng, final LocationServiceManager.LocationChangeType locationChangeType){ + private void handleLocationUpdate(final fr.free.nrw.commons.location.LatLng latLng, + final LocationServiceManager.LocationChangeType locationChangeType) { lastKnownLocation = latLng; exploreMapController.currentLocation = lastKnownLocation; presenter.updateMap(locationChangeType); @@ -395,42 +429,46 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } @Override - public void populatePlaces(LatLng curLatLng, LatLng searchLatLng) { + public void populatePlaces(LatLng curLatLng) { final Observable nearbyPlacesInfoObservable; if (curLatLng == null) { checkPermissionsAndPerformAction(); return; } - if (searchLatLng.equals(lastFocusLocation) || lastFocusLocation == null || recenterToUserLocation) { // Means we are checking around current location - nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(curLatLng, searchLatLng, true); + if (curLatLng.equals(getLastMapFocus())) { // Means we are checking around current location + nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(curLatLng, + getLastMapFocus(), true); } else { - nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(curLatLng, searchLatLng, false); + nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(getLastMapFocus(), + curLatLng, false); } compositeDisposable.add(nearbyPlacesInfoObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(explorePlacesInfo -> { - updateMapMarkers(explorePlacesInfo, isCurrentLocationMarkerVisible()); mediaList = explorePlacesInfo.mediaList; - lastFocusLocation = searchLatLng; + updateMapMarkers(explorePlacesInfo); + lastMapFocus = new GeoPoint(curLatLng.getLatitude(), curLatLng.getLongitude()); }, throwable -> { Timber.d(throwable); - showErrorMessage(getString(R.string.error_fetching_nearby_places)+throwable.getLocalizedMessage()); + showErrorMessage(getString(R.string.error_fetching_nearby_places) + + throwable.getLocalizedMessage()); setProgressBarVisibility(false); presenter.lockUnlockNearby(false); })); - if(recenterToUserLocation) { + if (recenterToUserLocation) { recenterToUserLocation = false; } } /** * Updates map markers according to latest situation + * * @param explorePlacesInfo holds several information as current location, marker list etc. */ - private void updateMapMarkers(final MapController.ExplorePlacesInfo explorePlacesInfo, final boolean shouldTrackPosition) { - presenter.updateMapMarkers(explorePlacesInfo, selectedMarker,shouldTrackPosition); + private void updateMapMarkers(final MapController.ExplorePlacesInfo explorePlacesInfo) { + presenter.updateMapMarkers(explorePlacesInfo, selectedMarker); } private void showErrorMessage(final String message) { @@ -447,28 +485,24 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment isPermissionDenied = false; applicationKvStore.putBoolean("doNotAskForLocationPermission", false); lastKnownLocation = locationManager.getLastLocation(); - fr.free.nrw.commons.location.LatLng target=lastFocusLocation; - if(null == lastFocusLocation){ - target = lastKnownLocation; - } + fr.free.nrw.commons.location.LatLng target = lastKnownLocation; if (lastKnownLocation != null) { - final CameraPosition position = new CameraPosition.Builder() - .target(LocationUtils.commonsLatLngToMapBoxLatLng(target)) // Sets the new camera position - .zoom(ZOOM_LEVEL) // Same zoom level - .build(); - mapBox.moveCamera(CameraUpdateFactory.newCameraPosition(position)); - } - else if(locationManager.isGPSProviderEnabled() || locationManager.isNetworkProviderEnabled()){ + GeoPoint targetP = new GeoPoint(target.getLatitude(), target.getLongitude()); + mapCenter = targetP; + mapView.getController().setCenter(targetP); + recenterMarkerToPosition(targetP); + moveCameraToPosition(targetP); + } else if (locationManager.isGPSProviderEnabled() + || locationManager.isNetworkProviderEnabled()) { locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); setProgressBarVisibility(true); - } - else { - Toast.makeText(getContext(), getString(R.string.nearby_location_not_available), Toast.LENGTH_LONG).show(); + } else { + Toast.makeText(getContext(), getString(R.string.nearby_location_not_available), + Toast.LENGTH_LONG).show(); } presenter.onMapReady(exploreMapController); registerUnregisterLocationListener(false); - addOnCameraMoveListener(); } public void registerUnregisterLocationListener(final boolean removeLocationListener) { @@ -480,27 +514,44 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment if (isPermissionDenied || curLatLng == null) { recenterToUserLocation = true; checkPermissionsAndPerformAction(); - if (!isPermissionDenied && !(locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled())) { + if (!isPermissionDenied && !(locationManager.isNetworkProviderEnabled() + || locationManager.isGPSProviderEnabled())) { showLocationOffDialog(); } return; } - addCurrentLocationMarker(curLatLng); - final CameraPosition position; - position = new CameraPosition.Builder() - .target(new com.mapbox.mapboxsdk.geometry.LatLng(curLatLng.getLatitude(), curLatLng.getLongitude(), 0)) // Sets the new camera position - .zoom(mapBox.getCameraPosition().zoom) // Same zoom level - .build(); - - mapBox.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1000); + recenterMarkerToPosition(new GeoPoint(curLatLng.getLatitude(), curLatLng.getLongitude())); + mapView.getController() + .animateTo(new GeoPoint(curLatLng.getLatitude(), curLatLng.getLongitude())); + if (lastMapFocus != null) { + Location mylocation = new Location(""); + Location dest_location = new Location(""); + dest_location.setLatitude(mapView.getMapCenter().getLatitude()); + dest_location.setLongitude(mapView.getMapCenter().getLongitude()); + mylocation.setLatitude(lastMapFocus.getLatitude()); + mylocation.setLongitude(lastMapFocus.getLongitude()); + Float distance = mylocation.distanceTo(dest_location);//in meters + if (lastMapFocus != null) { + if (isNetworkConnectionEstablished()) { + if (distance > 2000.0) { + setSearchThisAreaButtonVisibility(true); + } else { + setSearchThisAreaButtonVisibility(false); + } + } + } else { + setSearchThisAreaButtonVisibility(false); + } + } } @Override public void showLocationOffDialog() { // This creates a dialog box that prompts the user to enable location DialogUtil - .showAlertDialog(getActivity(), getString(R.string.ask_to_turn_location_on), getString(R.string.nearby_needs_location), - getString(R.string.yes), getString(R.string.no), this::openLocationSettings, null); + .showAlertDialog(getActivity(), getString(R.string.ask_to_turn_location_on), + getString(R.string.nearby_needs_location), + getString(R.string.yes), getString(R.string.no), this::openLocationSettings, null); } @Override @@ -509,11 +560,13 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); final PackageManager packageManager = getActivity().getPackageManager(); - if (intent.resolveActivity(packageManager)!= null) { + if (intent.resolveActivity(packageManager) != null) { startActivity(intent); - Toast.makeText(getContext(), R.string.recommend_high_accuracy_mode, Toast.LENGTH_LONG).show(); + Toast.makeText(getContext(), R.string.recommend_high_accuracy_mode, Toast.LENGTH_LONG) + .show(); } else { - Toast.makeText(getContext(), R.string.cannot_open_location_settings, Toast.LENGTH_LONG).show(); + Toast.makeText(getContext(), R.string.cannot_open_location_settings, Toast.LENGTH_LONG) + .show(); } } @@ -534,14 +587,16 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment /** * Same bottom sheet carries information for all nearby places, so we need to pass information * (title, description, distance and links) to view on nearby marker click + * * @param place Place of clicked nearby marker */ private void passInfoToSheet(final Place place) { directionsButton.setOnClickListener(view -> Utils.handleGeoCoordinates(getActivity(), place.getLocation())); - commonsButton.setVisibility(place.hasCommonsLink()?View.VISIBLE:View.GONE); - commonsButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); + commonsButton.setVisibility(place.hasCommonsLink() ? View.VISIBLE : View.GONE); + commonsButton.setOnClickListener( + view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); int index = 0; for (Media media : mediaList) { @@ -551,24 +606,19 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment ((ExploreMapRootFragment) getParentFragment()).onMediaClicked(finalIndex); }); } - index ++; + index++; } title.setText(place.name.substring(5, place.name.lastIndexOf("."))); distance.setText(place.distance); // Remove label since it is double information String descriptionText = place.getLongDescription() - .replace(place.getName() + " (",""); - descriptionText = (descriptionText.equals(place.getLongDescription()) ? descriptionText : descriptionText.replaceFirst(".$","")); + .replace(place.getName() + " (", ""); + descriptionText = (descriptionText.equals(place.getLongDescription()) ? descriptionText + : descriptionText.replaceFirst(".$", "")); // Set the short description after we remove place name from long description description.setText(descriptionText); } - - @Override - public void addOnCameraMoveListener() { - mapBox.addOnCameraMoveListener(cameraMoveListener); - } - @Override public void addSearchThisAreaButtonAction() { searchThisAreaButton.setOnClickListener(presenter.onSearchThisAreaClicked()); @@ -610,134 +660,6 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } } - /** - * Removes old current location marker and adds a new one to display current location - * @param curLatLng current location of user - */ - @Override - public void addCurrentLocationMarker(LatLng curLatLng) { - if (null != curLatLng && !isPermissionDenied) { - ExecutorUtils.get().submit(() -> { - mapView.post(() -> removeCurrentLocationMarker()); - Timber.d("Adds current location marker"); - - final Icon icon = IconFactory.getInstance(getContext()) - .fromResource(R.drawable.current_location_marker); - - final MarkerOptions currentLocationMarkerOptions = new MarkerOptions() - .position(new com.mapbox.mapboxsdk.geometry.LatLng(curLatLng.getLatitude(), - curLatLng.getLongitude())); - currentLocationMarkerOptions.setIcon(icon); // Set custom icon - mapView.post( - () -> currentLocationMarker = mapBox.addMarker(currentLocationMarkerOptions)); - - final List circle = UiUtils - .createCircleArray(curLatLng.getLatitude(), curLatLng.getLongitude(), - curLatLng.getAccuracy() * 2, 100); - - final PolygonOptions currentLocationPolygonOptions = new PolygonOptions() - .addAll(circle) - .strokeColor(getResources().getColor(R.color.current_marker_stroke)) - .fillColor(getResources().getColor(R.color.current_marker_fill)); - mapView.post( - () -> currentLocationPolygon = mapBox - .addPolygon(currentLocationPolygonOptions)); - }); - } else { - Timber.d("not adding current location marker..current location is null"); - } - } - - @Override - public boolean isCurrentLocationMarkerVisible() { - if (projectorLatLngBounds == null || currentLocationMarker == null) { - Timber.d("Map projection bounds are null"); - return false; - } else { - Timber.d("Current location marker %s" , projectorLatLngBounds.contains(currentLocationMarker.getPosition()) ? "visible" : "invisible"); - return projectorLatLngBounds.contains(currentLocationMarker.getPosition()); - } - } - - /** - * Sets boundaries of visible region in terms of geolocation - */ - @Override - public void setProjectorLatLngBounds() { - projectorLatLngBounds = mapBox.getProjection().getVisibleRegion().latLngBounds; - } - - /** - * Removes old current location marker - */ - private void removeCurrentLocationMarker() { - if (currentLocationMarker != null && mapBox!=null) { - mapBox.removeMarker(currentLocationMarker); - if (currentLocationPolygon != null) { - mapBox.removePolygon(currentLocationPolygon); - } - } - } - - /** - * Update map camera to trac users current position - * @param curLatLng - */ - @Override - public void updateMapToTrackPosition(LatLng curLatLng) { - Timber.d("Updates map camera to track user position"); - final CameraPosition cameraPosition; - if(isPermissionDenied){ - cameraPosition = new CameraPosition.Builder().target - (LocationUtils.commonsLatLngToMapBoxLatLng(curLatLng)).build(); - }else{ - cameraPosition = new CameraPosition.Builder().target - (LocationUtils.commonsLatLngToMapBoxLatLng(curLatLng)).build(); - } - if(null!=mapBox) { - mapBox.setCameraPosition(cameraPosition); - mapBox.animateCamera(CameraUpdateFactory - .newCameraPosition(cameraPosition), 1000); - } - } - - @Override - public LatLng getCameraTarget() { - return mapBox == null ? null : LocationUtils.mapBoxLatLngToCommonsLatLng(mapBox.getCameraPosition().target); - } - - /** - * Centers map to a given place - * @param place place to center - */ - @Override - public void centerMapToPlace(Place place) { - MapUtils.centerMapToPlace(place, mapBox, lastPlaceToCenter, getActivity()); - Timber.d("Map is centered to place"); - final double cameraShift; - if (null != place) { - lastPlaceToCenter = place; - } - - if (null != lastPlaceToCenter) { - final Configuration configuration = getActivity().getResources().getConfiguration(); - if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) { - cameraShift = CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT; - } else { - cameraShift = CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE; - } - final CameraPosition position = new CameraPosition.Builder() - .target(LocationUtils.commonsLatLngToMapBoxLatLng( - new fr.free.nrw.commons.location.LatLng( - lastPlaceToCenter.location.getLatitude() - cameraShift, - lastPlaceToCenter.getLocation().getLongitude(), - 0))) // Sets the new camera position - .zoom(mapBox.getCameraPosition().zoom) // Same zoom level - .build(); - mapBox.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1000); - } - } - @Override public LatLng getLastLocation() { if (lastKnownLocation == null) { @@ -746,11 +668,6 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment return lastKnownLocation; } - @Override - public com.mapbox.mapboxsdk.geometry.LatLng getLastFocusLocation() { - return lastFocusLocation == null? null : LocationUtils.commonsLatLngToMapBoxLatLng(lastFocusLocation); - } - @Override public void disableFABRecenter() { fabRecenter.setEnabled(false); @@ -761,35 +678,232 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment fabRecenter.setEnabled(true); } + /** + * Adds a markers to the map based on the list of NearbyBaseMarker. + * + * @param nearbyBaseMarkers The NearbyBaseMarker object representing the markers to be added. + */ @Override - public void addNearbyMarkersToMapBoxMap(List nearbyBaseMarkers, Marker selectedMarker) { - mapBox.clear(); - if (isMapBoxReady && mapBox != null) { - mapBox.addMarkers(nearbyBaseMarkers); - setMapMarkerActions(selectedMarker); + public void addMarkersToMap(List nearbyBaseMarkers) { + clearAllMarkers(); + for (int i = 0; i < nearbyBaseMarkers.size(); i++) { + addMarkerToMap(nearbyBaseMarkers.get(i)); + } + mapView.invalidate(); + } + + /** + * Adds a marker to the map based on the specified NearbyBaseMarker. + * + * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be added. + */ + private void addMarkerToMap(NearbyBaseMarker nearbyBaseMarker) { + ArrayList items = new ArrayList<>(); + Bitmap icon = nearbyBaseMarker.getMarker().getIcon().getBitmap(); + Drawable d = new BitmapDrawable(getResources(), icon); + GeoPoint point = new GeoPoint( + nearbyBaseMarker.getPlace().location.getLatitude(), + nearbyBaseMarker.getPlace().location.getLongitude()); + OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, null, + point); + item.setMarker(d); + items.add(item); + ItemizedOverlayWithFocus overlay = new ItemizedOverlayWithFocus(items, + new OnItemGestureListener() { + @Override + public boolean onItemSingleTapUp(int index, OverlayItem item) { + final Place place = nearbyBaseMarker.getPlace(); + if (clickedMarker != null) { + removeMarker(clickedMarker); + addMarkerToMap(clickedMarker); + bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } + clickedMarker = nearbyBaseMarker; + passInfoToSheet(place); + return true; + } + + @Override + public boolean onItemLongPress(int index, OverlayItem item) { + return false; + } + }, getContext()); + + overlay.setFocusItemsOnTap(true); + mapView.getOverlays().add(overlay); // Add the overlay to the map + } + + /** + * Removes a marker from the map based on the specified NearbyBaseMarker. + * + * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be removed. + */ + private void removeMarker(NearbyBaseMarker nearbyBaseMarker) { + Place place = nearbyBaseMarker.getPlace(); + List overlays = mapView.getOverlays(); + ItemizedOverlayWithFocus item; + + for (int i = 0; i < overlays.size(); i++) { + if (overlays.get(i) instanceof ItemizedOverlayWithFocus) { + item = (ItemizedOverlayWithFocus) overlays.get(i); + OverlayItem overlayItem = item.getItem(0); + + if (place.location.getLatitude() == overlayItem.getPoint().getLatitude() + && place.location.getLongitude() == overlayItem.getPoint().getLongitude()) { + mapView.getOverlays().remove(i); + mapView.invalidate(); + break; + } + } } } - private void setMapMarkerActions(final Marker selectedMarker) { - if (mapBox != null) { - mapBox.setOnInfoWindowCloseListener(marker -> { - if (marker == selectedMarker) { - presenter.markerUnselected(); + /** + * Clears all markers from the map and resets certain map overlays and gestures. After clearing + * markers, it re-adds a scale bar overlay and rotation gesture overlay to the map. + */ + @Override + public void clearAllMarkers() { + mapView.getOverlayManager().clear(); + GeoPoint geoPoint = mapCenter; + if (geoPoint != null) { + List overlays = mapView.getOverlays(); + ScaleDiskOverlay diskOverlay = + new ScaleDiskOverlay(this.getContext(), + geoPoint, 2000, GeoConstants.UnitOfMeasure.foot); + Paint circlePaint = new Paint(); + circlePaint.setColor(Color.rgb(128, 128, 128)); + circlePaint.setStyle(Paint.Style.STROKE); + circlePaint.setStrokeWidth(2f); + diskOverlay.setCirclePaint2(circlePaint); + Paint diskPaint = new Paint(); + diskPaint.setColor(Color.argb(40, 128, 128, 128)); + diskPaint.setStyle(Paint.Style.FILL_AND_STROKE); + diskOverlay.setCirclePaint1(diskPaint); + diskOverlay.setDisplaySizeMin(900); + diskOverlay.setDisplaySizeMax(1700); + mapView.getOverlays().add(diskOverlay); + org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker( + mapView); + startMarker.setPosition(geoPoint); + startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER, + org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM); + startMarker.setIcon( + ContextCompat.getDrawable(this.getContext(), R.drawable.current_location_marker)); + startMarker.setTitle("Your Location"); + startMarker.setTextLabelFontSize(24); + mapView.getOverlays().add(startMarker); + } + ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(mapView); + scaleBarOverlay.setScaleBarOffset(15, 25); + Paint barPaint = new Paint(); + barPaint.setARGB(200, 255, 250, 250); + scaleBarOverlay.setBackgroundPaint(barPaint); + scaleBarOverlay.enableScaleBar(); + mapView.getOverlays().add(scaleBarOverlay); + mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() { + @Override + public boolean singleTapConfirmedHelper(GeoPoint p) { + if (clickedMarker != null) { + removeMarker(clickedMarker); + addMarkerToMap(clickedMarker); + mapView.invalidate(); + } else { + Timber.e("CLICKED MARKER IS NULL"); } - }); + if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { + // Back should first hide the bottom sheet if it is expanded + bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + } else if (isDetailsBottomSheetVisible()) { + hideBottomDetailsSheet(); + } + return true; + } - mapBox.setOnMarkerClickListener(marker -> { - if (marker instanceof NearbyMarker) { - presenter.markerSelected(marker); - } + @Override + public boolean longPressHelper(GeoPoint p) { return false; - }); + } + })); + mapView.setMultiTouchControls(true); + } + + /** + * Recenters the map view to the specified GeoPoint and updates the marker to indicate the new + * position. + * + * @param geoPoint The GeoPoint representing the new center position for the map. + */ + private void recenterMarkerToPosition(GeoPoint geoPoint) { + if (geoPoint != null) { + mapView.getController().setCenter(geoPoint); + List overlays = mapView.getOverlays(); + for (int i = 0; i < overlays.size(); i++) { + if (overlays.get(i) instanceof org.osmdroid.views.overlay.Marker) { + mapView.getOverlays().remove(i); + } else if (overlays.get(i) instanceof ScaleDiskOverlay) { + mapView.getOverlays().remove(i); + } + } + ScaleDiskOverlay diskOverlay = + new ScaleDiskOverlay(this.getContext(), + geoPoint, 2000, GeoConstants.UnitOfMeasure.foot); + Paint circlePaint = new Paint(); + circlePaint.setColor(Color.rgb(128, 128, 128)); + circlePaint.setStyle(Paint.Style.STROKE); + circlePaint.setStrokeWidth(2f); + diskOverlay.setCirclePaint2(circlePaint); + Paint diskPaint = new Paint(); + diskPaint.setColor(Color.argb(40, 128, 128, 128)); + diskPaint.setStyle(Paint.Style.FILL_AND_STROKE); + diskOverlay.setCirclePaint1(diskPaint); + diskOverlay.setDisplaySizeMin(900); + diskOverlay.setDisplaySizeMax(1700); + mapView.getOverlays().add(diskOverlay); + org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker( + mapView); + startMarker.setPosition(geoPoint); + startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER, + org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM); + startMarker.setIcon( + ContextCompat.getDrawable(this.getContext(), R.drawable.current_location_marker)); + startMarker.setTitle("Your Location"); + startMarker.setTextLabelFontSize(24); + mapView.getOverlays().add(startMarker); } } + /** + * Moves the camera of the map view to the specified GeoPoint using an animation. + * + * @param geoPoint The GeoPoint representing the new camera position for the map. + */ + private void moveCameraToPosition(GeoPoint geoPoint) { + mapView.getController().animateTo(geoPoint); + } + @Override - public void setMapBoundaries(CameraUpdate cameaUpdate) { - mapBox.easeCamera(cameaUpdate); + public fr.free.nrw.commons.location.LatLng getLastMapFocus() { + return lastMapFocus == null ? getMapCenter() : new fr.free.nrw.commons.location.LatLng( + lastMapFocus.getLatitude(), lastMapFocus.getLongitude(), 100); + } + + @Override + public fr.free.nrw.commons.location.LatLng getMapCenter() { + fr.free.nrw.commons.location.LatLng latLnge = null; + if (mapCenter != null) { + latLnge = new fr.free.nrw.commons.location.LatLng( + mapCenter.getLatitude(), mapCenter.getLongitude(), 100); + } + return latLnge; + } + + @Override + public fr.free.nrw.commons.location.LatLng getMapFocus() { + fr.free.nrw.commons.location.LatLng mapFocusedLatLng = new fr.free.nrw.commons.location.LatLng( + mapView.getMapCenter().getLatitude(), mapView.getMapCenter().getLongitude(), 100); + return mapFocusedLatLng; } @Override @@ -827,7 +941,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } } else { if (snackbar == null) { - snackbar = Snackbar.make(getView(), R.string.no_internet, Snackbar.LENGTH_INDEFINITE); + snackbar = Snackbar.make(getView(), R.string.no_internet, + Snackbar.LENGTH_INDEFINITE); setSearchThisAreaButtonVisibility(false); setProgressBarVisibility(false); } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java index fc20bdbfe..03ce30c7e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java @@ -1,9 +1,11 @@ package fr.free.nrw.commons.explore.map; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; +import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.SEARCH_CUSTOM_AREA; +import android.location.Location; import android.view.View; import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; @@ -25,9 +27,9 @@ import timber.log.Timber; public class ExploreMapPresenter implements ExploreMapContract.UserActions, NearbyBaseMarkerThumbCallback { + BookmarkLocationsDao bookmarkLocationDao; private boolean isNearbyLocked; - private boolean placesLoadedOnce; private LatLng curLatLng; private ExploreMapController exploreMapController; @@ -54,7 +56,7 @@ public class ExploreMapPresenter ); private ExploreMapContract.View exploreMapFragmentView = DUMMY; - public ExploreMapPresenter(BookmarkLocationsDao bookmarkLocationDao){ + public ExploreMapPresenter(BookmarkLocationsDao bookmarkLocationDao) { this.bookmarkLocationDao = bookmarkLocationDao; } @@ -71,14 +73,6 @@ public class ExploreMapPresenter return; } - LatLng lastLocation = exploreMapFragmentView.getLastLocation(); - curLatLng = lastLocation; - - if (curLatLng == null) { - Timber.d("Skipping update of nearby places as location is unavailable"); - return; - } - /** * Significant changed - Markers and current location will be updated together * Slightly changed - Only current position marker will be updated @@ -87,24 +81,21 @@ public class ExploreMapPresenter Timber.d("LOCATION_SIGNIFICANTLY_CHANGED"); lockUnlockNearby(true); exploreMapFragmentView.setProgressBarVisibility(true); - exploreMapFragmentView.populatePlaces(curLatLng, lastLocation); - + exploreMapFragmentView.populatePlaces(exploreMapFragmentView.getMapCenter()); } else if (locationChangeType.equals(SEARCH_CUSTOM_AREA)) { Timber.d("SEARCH_CUSTOM_AREA"); lockUnlockNearby(true); exploreMapFragmentView.setProgressBarVisibility(true); - exploreMapFragmentView.populatePlaces(curLatLng, exploreMapFragmentView.getCameraTarget()); + exploreMapFragmentView.populatePlaces(exploreMapFragmentView.getMapFocus()); } else { // Means location changed slightly, ie user is walking or driving. Timber.d("Means location changed slightly"); - if (exploreMapFragmentView.isCurrentLocationMarkerVisible()){ // Means user wants to see their live location - exploreMapFragmentView.recenterMap(curLatLng); - } } } /** * Nearby updates takes time, since they are network operations. During update time, we don't * want to get any other calls from user. So locking nearby. + * * @param isNearbyLocked true means lock, false means unlock */ @Override @@ -143,29 +134,10 @@ public class ExploreMapPresenter return exploreMapFragmentView.backButtonClicked(); } - @Override - public void onCameraMove(com.mapbox.mapboxsdk.geometry.LatLng latLng) { - exploreMapFragmentView.setProjectorLatLngBounds(); - // If our nearby markers are calculated at least once - if (exploreMapController.latestSearchLocation != null) { - double distance = latLng.distanceTo - (LocationUtils.commonsLatLngToMapBoxLatLng(exploreMapController.latestSearchLocation)); - if (exploreMapFragmentView.isNetworkConnectionEstablished()) { - if (distance > exploreMapController.latestSearchRadius && exploreMapController.latestSearchRadius != 0) { - exploreMapFragmentView.setSearchThisAreaButtonVisibility(true); - } else { - exploreMapFragmentView.setSearchThisAreaButtonVisibility(false); - } - } - } else { - exploreMapFragmentView.setSearchThisAreaButtonVisibility(false); - } - } - public void onMapReady(ExploreMapController exploreMapController) { this.exploreMapController = exploreMapController; exploreMapFragmentView.addSearchThisAreaButtonAction(); - if(null != exploreMapFragmentView) { + if (null != exploreMapFragmentView) { exploreMapFragmentView.addSearchThisAreaButtonAction(); initializeMapOperations(); } @@ -177,66 +149,49 @@ public class ExploreMapPresenter exploreMapFragmentView.addSearchThisAreaButtonAction(); } - public Observable loadAttractionsFromLocation(LatLng curLatLng, LatLng searchLatLng, boolean checkingAroundCurrent) { + public Observable loadAttractionsFromLocation(LatLng curLatLng, + LatLng searchLatLng, boolean checkingAroundCurrent) { return Observable .fromCallable(() -> exploreMapController - .loadAttractionsFromLocation(curLatLng, searchLatLng,checkingAroundCurrent)); + .loadAttractionsFromLocation(curLatLng, searchLatLng, checkingAroundCurrent)); } /** * Populates places for custom location, should be used for finding nearby places around a * location where you are not at. + * * @param explorePlacesInfo This variable has placeToCenter list information and distances. */ public void updateMapMarkers( - MapController.ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker, boolean shouldTrackPosition) { - exploreMapFragmentView.setMapBoundaries(CameraUpdateFactory.newLatLngBounds(getLatLngBounds(explorePlacesInfo.boundaryCoordinates), 50)); - prepareNearbyBaseMarkers(explorePlacesInfo, selectedMarker, shouldTrackPosition); + MapController.ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker) { + if (explorePlacesInfo.mediaList != null) { + prepareNearbyBaseMarkers(explorePlacesInfo, selectedMarker); + } else { + //TODO: SHOW SNACKBAR + lockUnlockNearby(false); // So that new location updates wont come + exploreMapFragmentView.setProgressBarVisibility(false); + } } - void prepareNearbyBaseMarkers(MapController.ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker, boolean shouldTrackPosition) { + void prepareNearbyBaseMarkers(MapController.ExplorePlacesInfo explorePlacesInfo, + Marker selectedMarker) { exploreMapController - .loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.curLatLng, // Curlatlang will be used to calculate distances + .loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.curLatLng, + // Curlatlang will be used to calculate distances explorePlacesInfo.explorePlaceList, exploreMapFragmentView.getContext(), this, selectedMarker, - shouldTrackPosition, explorePlacesInfo); } @Override - public void onNearbyBaseMarkerThumbsReady(List baseMarkers, ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker, boolean shouldTrackPosition) { - if(null != exploreMapFragmentView) { - exploreMapFragmentView.addNearbyMarkersToMapBoxMap(baseMarkers, selectedMarker); - exploreMapFragmentView.addCurrentLocationMarker(explorePlacesInfo.curLatLng); - if(shouldTrackPosition){ - exploreMapFragmentView.updateMapToTrackPosition(explorePlacesInfo.curLatLng); - } + public void onNearbyBaseMarkerThumbsReady(List baseMarkers, + ExplorePlacesInfo explorePlacesInfo, Marker selectedMarker) { + if (null != exploreMapFragmentView) { + exploreMapFragmentView.addMarkersToMap(baseMarkers); lockUnlockNearby(false); // So that new location updates wont come exploreMapFragmentView.setProgressBarVisibility(false); - handleCenteringTaskIfAny(); - } - } - - private LatLngBounds getLatLngBounds(LatLng[] boundaries) { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(LocationUtils.commonsLatLngToMapBoxLatLng(boundaries[0])) - .include(LocationUtils.commonsLatLngToMapBoxLatLng(boundaries[1])) - .include(LocationUtils.commonsLatLngToMapBoxLatLng(boundaries[2])) - .include(LocationUtils.commonsLatLngToMapBoxLatLng(boundaries[3])) - .build(); - return latLngBounds; - } - - /** - * Some centering task may need to wait for map to be ready, if they are requested before - * map is ready. So we will remember it when the map is ready - */ - private void handleCenteringTaskIfAny() { - if (!placesLoadedOnce) { - placesLoadedOnce = true; - exploreMapFragmentView.centerMapToPlace(null); } } @@ -245,7 +200,7 @@ public class ExploreMapPresenter // Lock map operations during search this area operation exploreMapFragmentView.setSearchThisAreaButtonVisibility(false); - if (searchCloseToCurrentLocation()){ + if (searchCloseToCurrentLocation()) { updateMap(LOCATION_SIGNIFICANTLY_CHANGED); } else { updateMap(SEARCH_CUSTOM_AREA); @@ -254,40 +209,29 @@ public class ExploreMapPresenter } /** - * Returns true if search this area button is used around our current location, so that - * we can continue following our current location again + * Returns true if search this area button is used around our current location, so that we can + * continue following our current location again + * * @return Returns true if search this area button is used around our current location */ public boolean searchCloseToCurrentLocation() { - if (null == exploreMapFragmentView.getLastFocusLocation() || exploreMapController.latestSearchRadius == 0) { + if (null == exploreMapFragmentView.getLastMapFocus()) { return true; } - double distance = LocationUtils.commonsLatLngToMapBoxLatLng(exploreMapFragmentView.getCameraTarget()) - .distanceTo(exploreMapFragmentView.getLastFocusLocation()); - if (distance > exploreMapController.currentLocationSearchRadius * 3 / 4) { + + Location mylocation = new Location(""); + Location dest_location = new Location(""); + dest_location.setLatitude(exploreMapFragmentView.getMapFocus().getLatitude()); + dest_location.setLongitude(exploreMapFragmentView.getMapFocus().getLongitude()); + mylocation.setLatitude(exploreMapFragmentView.getLastMapFocus().getLatitude()); + mylocation.setLongitude(exploreMapFragmentView.getLastMapFocus().getLongitude()); + Float distance = mylocation.distanceTo(dest_location); + + if (distance > 2000.0 * 3 / 4) { return false; } else { return true; } } - @Override - public void markerUnselected() { - exploreMapFragmentView.hideBottomDetailsSheet(); - } - - @Override - public void markerSelected(Marker marker) { - exploreMapFragmentView.displayBottomSheetWithInfo(marker); - } - - public boolean areLocationsClose(LatLng cameraTarget, LatLng lastKnownLocation) { - double distance = LocationUtils.commonsLatLngToMapBoxLatLng(cameraTarget) - .distanceTo(LocationUtils.commonsLatLngToMapBoxLatLng(lastKnownLocation)); - if (distance > exploreMapController.currentLocationSearchRadius * 3 / 4) { - return false; - } else { - return true; - } - } } diff --git a/app/src/main/res/layout/fragment_explore_map.xml b/app/src/main/res/layout/fragment_explore_map.xml index 9d1246f80..dcaf1d2e1 100644 --- a/app/src/main/res/layout/fragment_explore_map.xml +++ b/app/src/main/res/layout/fragment_explore_map.xml @@ -1,72 +1,70 @@ - - + + + + android:visibility="visible" /> - - - - - - - - - - - -