From ed6576b3857aa4d4f493c1f54b029934a7ccb651 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sun, 13 Jul 2025 22:53:24 -0500 Subject: [PATCH] Convert ExploreMapController to kotlin --- .../explore/map/ExploreMapController.java | 213 ----------------- .../explore/map/ExploreMapController.kt | 219 ++++++++++++++++++ .../explore/map/ExploreMapPresenter.java | 3 +- 3 files changed, 220 insertions(+), 215 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java create mode 100644 app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt 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 deleted file mode 100644 index c944f75a1..000000000 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java +++ /dev/null @@ -1,213 +0,0 @@ -package fr.free.nrw.commons.explore.map; - -import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween; -import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.transition.Transition; -import fr.free.nrw.commons.BaseMarker; -import fr.free.nrw.commons.MapController; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.nearby.Place; -import fr.free.nrw.commons.utils.ImageUtils; -import fr.free.nrw.commons.utils.LocationUtils; -import fr.free.nrw.commons.utils.PlaceUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -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 - public double latestSearchRadius = 0; // Any last search radius - public double currentLocationSearchRadius = 0; // Search radius of only searches around current location - - - @Inject - public ExploreMapController(ExploreMapCalls explorePlaces) { - this.exploreMapCalls = explorePlaces; - } - - /** - * Takes location as parameter and returns ExplorePlaces info that holds currentLatLng, mediaList, - * explorePlaceList and boundaryCoordinates - * - * @param currentLatLng 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 currentLatLng, mediaList, explorePlaceList and - * boundaryCoordinates - */ - public ExplorePlacesInfo loadAttractionsFromLocation(LatLng currentLatLng, LatLng searchLatLng, - boolean checkingAroundCurrentLocation) { - - if (searchLatLng == null) { - Timber.d("Loading attractions explore map, but search is null"); - return null; - } - - ExplorePlacesInfo explorePlacesInfo = new ExplorePlacesInfo(); - try { - explorePlacesInfo.currentLatLng = currentLatLng; - latestSearchLocation = searchLatLng; - - List mediaList = exploreMapCalls.callCommonsQuery(searchLatLng); - LatLng[] boundaryCoordinates = {mediaList.get(0).getCoordinates(), // south - mediaList.get(0).getCoordinates(), // north - mediaList.get(0).getCoordinates(), // west - mediaList.get(0).getCoordinates()};// east, init with a random location - - if (searchLatLng != null) { - Timber.d("Sorting places by distance..."); - final Map distances = new HashMap<>(); - for (Media media : mediaList) { - distances.put(media, - computeDistanceBetween(media.getCoordinates(), searchLatLng)); - // Find boundaries with basic find max approach - if (media.getCoordinates().getLatitude() - < boundaryCoordinates[0].getLatitude()) { - boundaryCoordinates[0] = media.getCoordinates(); - } - if (media.getCoordinates().getLatitude() - > boundaryCoordinates[1].getLatitude()) { - boundaryCoordinates[1] = media.getCoordinates(); - } - if (media.getCoordinates().getLongitude() - < boundaryCoordinates[2].getLongitude()) { - boundaryCoordinates[2] = media.getCoordinates(); - } - if (media.getCoordinates().getLongitude() - > boundaryCoordinates[3].getLongitude()) { - boundaryCoordinates[3] = media.getCoordinates(); - } - } - } - explorePlacesInfo.mediaList = mediaList; - explorePlacesInfo.explorePlaceList = PlaceUtils.mediaToExplorePlace(mediaList); - explorePlacesInfo.boundaryCoordinates = boundaryCoordinates; - - // Sets latestSearchRadius to maximum distance among boundaries and search location - for (LatLng bound : boundaryCoordinates) { - double distance = LocationUtils.calculateDistance(bound.getLatitude(), - bound.getLongitude(), searchLatLng.getLatitude(), searchLatLng.getLongitude()); - if (distance > latestSearchRadius) { - latestSearchRadius = distance; - } - } - - // Our radius searched around us, will be used to understand when user search their own location, we will follow them - if (checkingAroundCurrentLocation) { - currentLocationSearchRadius = latestSearchRadius; - currentLocation = currentLatLng; - } - } catch (Exception e) { - e.printStackTrace(); - } - return explorePlacesInfo; - } - - /** - * 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( - LatLng currentLatLng, - final List placeList, - Context context, - NearbyBaseMarkerThumbCallback callback, - ExplorePlacesInfo explorePlacesInfo) { - List baseMarkerList = new ArrayList<>(); - - if (placeList == null) { - return baseMarkerList; - } - - VectorDrawableCompat vectorDrawable = null; - try { - vectorDrawable = VectorDrawableCompat.create( - context.getResources(), R.drawable.ic_custom_map_marker_dark, context.getTheme()); - - } catch (Resources.NotFoundException e) { - // ignore when running tests. - } - if (vectorDrawable != null) { - for (Place explorePlace : placeList) { - final BaseMarker baseMarker = new BaseMarker(); - String distance = formatDistanceBetween(currentLatLng, explorePlace.location); - explorePlace.setDistance(distance); - - baseMarker.setTitle( - explorePlace.name.substring(5, explorePlace.name.lastIndexOf("."))); - baseMarker.setPosition( - new fr.free.nrw.commons.location.LatLng( - explorePlace.location.getLatitude(), - explorePlace.location.getLongitude(), 0)); - baseMarker.setPlace(explorePlace); - - Glide.with(context) - .asBitmap() - .load(explorePlace.getThumb()) - .placeholder(R.drawable.image_placeholder_96) - .apply(new RequestOptions().override(96, 96).centerCrop()) - .into(new CustomTarget() { - // We add icons to markers when bitmaps are ready - @Override - public void onResourceReady(@NonNull Bitmap resource, - @Nullable Transition transition) { - baseMarker.setIcon( - ImageUtils.addRedBorder(resource, 6, context)); - baseMarkerList.add(baseMarker); - if (baseMarkerList.size() - == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback - callback.onNearbyBaseMarkerThumbsReady(baseMarkerList, - explorePlacesInfo); - } - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - } - - // We add thumbnail icon for images that couldn't be loaded - @Override - public void onLoadFailed(@Nullable final Drawable errorDrawable) { - super.onLoadFailed(errorDrawable); - baseMarker.fromResource(context, R.drawable.image_placeholder_96); - baseMarkerList.add(baseMarker); - if (baseMarkerList.size() - == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback - callback.onNearbyBaseMarkerThumbsReady(baseMarkerList, - explorePlacesInfo); - } - } - }); - } - } - return baseMarkerList; - } - - interface NearbyBaseMarkerThumbCallback { - - // Callback to notify thumbnails of explore markers are added as icons and ready - void onNearbyBaseMarkerThumbsReady(List baseMarkers, - ExplorePlacesInfo explorePlacesInfo); - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt new file mode 100644 index 000000000..0873572d1 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt @@ -0,0 +1,219 @@ +package fr.free.nrw.commons.explore.map + +import android.content.Context +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import fr.free.nrw.commons.BaseMarker +import fr.free.nrw.commons.MapController +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.R +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.nearby.Place +import fr.free.nrw.commons.utils.ImageUtils.addRedBorder +import fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween +import fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween +import fr.free.nrw.commons.utils.LocationUtils.calculateDistance +import fr.free.nrw.commons.utils.PlaceUtils.mediaToExplorePlace +import timber.log.Timber +import javax.inject.Inject + +class ExploreMapController @Inject constructor( + private val exploreMapCalls: ExploreMapCalls +) : MapController() { + // Can be current and camera target on search this area button is used + private var latestSearchLocation: LatLng? = null + + // Any last search radius + private var latestSearchRadius: Double = 0.0 + + // Search radius of only searches around current location + private var currentLocationSearchRadius: Double = 0.0 + + @JvmField + // current location of user + var currentLocation: LatLng? = null + + /** + * Takes location as parameter and returns ExplorePlaces info that holds currentLatLng, mediaList, + * explorePlaceList and boundaryCoordinates + * + * @param currentLatLng 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 currentLatLng, mediaList, explorePlaceList and + * boundaryCoordinates + */ + fun loadAttractionsFromLocation( + currentLatLng: LatLng?, searchLatLng: LatLng?, + checkingAroundCurrentLocation: Boolean + ): ExplorePlacesInfo? { + if (searchLatLng == null) { + Timber.d("Loading attractions explore map, but search is null") + return null + } + + val explorePlacesInfo = ExplorePlacesInfo() + try { + explorePlacesInfo.currentLatLng = currentLatLng + latestSearchLocation = searchLatLng + + val mediaList = exploreMapCalls.callCommonsQuery(searchLatLng) + val boundaryCoordinates = arrayOf( + mediaList[0].coordinates!!, // south + mediaList[0].coordinates!!, // north + mediaList[0].coordinates!!, // west + mediaList[0].coordinates!! + ) // east, init with a random location + + Timber.d("Sorting places by distance...") + val distances: MutableMap = HashMap() + for (media in mediaList) { + distances[media] = computeDistanceBetween(media.coordinates!!, searchLatLng) + // Find boundaries with basic find max approach + if (media.coordinates!!.latitude + < boundaryCoordinates[0]!!.latitude + ) { + boundaryCoordinates[0] = media.coordinates!! + } + if (media.coordinates!!.latitude + > boundaryCoordinates[1]!!.latitude + ) { + boundaryCoordinates[1] = media.coordinates!! + } + if (media.coordinates!!.longitude + < boundaryCoordinates[2]!!.longitude + ) { + boundaryCoordinates[2] = media.coordinates!! + } + if (media.coordinates!!.longitude + > boundaryCoordinates[3]!!.longitude + ) { + boundaryCoordinates[3] = media.coordinates!! + } + } + explorePlacesInfo.mediaList = mediaList + explorePlacesInfo.explorePlaceList = mediaToExplorePlace(mediaList) + explorePlacesInfo.boundaryCoordinates = boundaryCoordinates + + // Sets latestSearchRadius to maximum distance among boundaries and search location + for ((latitude, longitude) in boundaryCoordinates) { + val distance = calculateDistance( + latitude, + longitude, searchLatLng.latitude, searchLatLng.longitude + ) + if (distance > latestSearchRadius) { + latestSearchRadius = distance + } + } + + // Our radius searched around us, will be used to understand when user search their own location, we will follow them + if (checkingAroundCurrentLocation) { + currentLocationSearchRadius = latestSearchRadius + currentLocation = currentLatLng + } + } catch (e: Exception) { + Timber.e(e) + } + return explorePlacesInfo + } + + interface NearbyBaseMarkerThumbCallback { + // Callback to notify thumbnails of explore markers are added as icons and ready + fun onNearbyBaseMarkerThumbsReady( + baseMarkers: List?, + explorePlacesInfo: ExplorePlacesInfo? + ) + } + + companion object { + /** + * 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 + */ + fun loadAttractionsFromLocationToBaseMarkerOptions( + currentLatLng: LatLng?, + placeList: List?, + context: Context, + callback: NearbyBaseMarkerThumbCallback, + explorePlacesInfo: ExplorePlacesInfo? + ): List { + val baseMarkerList: MutableList = ArrayList() + + if (placeList == null) { + return baseMarkerList + } + + var vectorDrawable: VectorDrawableCompat? = null + try { + vectorDrawable = VectorDrawableCompat.create( + context.resources, R.drawable.ic_custom_map_marker_dark, context.theme + ) + } catch (e: Resources.NotFoundException) { + // ignore when running tests. + } + if (vectorDrawable != null) { + for (explorePlace in placeList) { + val baseMarker = BaseMarker() + val distance = formatDistanceBetween(currentLatLng, explorePlace.location) + explorePlace.setDistance(distance) + + baseMarker.title = + explorePlace.name.substring(5, explorePlace.name.lastIndexOf(".")) + baseMarker.position = LatLng( + explorePlace.location.latitude, + explorePlace.location.longitude, 0f + ) + baseMarker.place = explorePlace + + Glide.with(context) + .asBitmap() + .load(explorePlace.thumb) + .placeholder(R.drawable.image_placeholder_96) + .apply(RequestOptions().override(96, 96).centerCrop()) + .into(object : CustomTarget() { + // We add icons to markers when bitmaps are ready + override fun onResourceReady( + resource: Bitmap, + transition: Transition? + ) { + baseMarker.icon = addRedBorder(resource, 6, context) + baseMarkerList.add(baseMarker) + if (baseMarkerList.size == placeList.size) { + // if true, we added all markers to list and can trigger thumbs ready callback + callback.onNearbyBaseMarkerThumbsReady( + baseMarkerList, + explorePlacesInfo + ) + } + } + + override fun onLoadCleared(placeholder: Drawable?) = Unit + + // We add thumbnail icon for images that couldn't be loaded + override fun onLoadFailed(errorDrawable: Drawable?) { + super.onLoadFailed(errorDrawable) + baseMarker.fromResource(context, R.drawable.image_placeholder_96) + baseMarkerList.add(baseMarker) + if (baseMarkerList.size == placeList.size) { + // if true, we added all markers to list and can trigger thumbs ready callback + callback.onNearbyBaseMarkerThumbsReady( + baseMarkerList, + explorePlacesInfo + ) + } + } + }) + } + } + return baseMarkerList + } + } +} 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 70f785b40..cb6b8ce47 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 @@ -180,8 +180,7 @@ public class ExploreMapPresenter } void prepareNearbyBaseMarkers(MapController.ExplorePlacesInfo explorePlacesInfo) { - exploreMapController - .loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.currentLatLng, + ExploreMapController.Companion.loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.currentLatLng, // Curlatlang will be used to calculate distances (List) explorePlacesInfo.explorePlaceList, exploreMapFragmentView.getContext(),