Made Nearby show all pins that could be presented on the screen, rather than a circle (#5553)

* Changed nearby from circle to rectangle

* Fixed bug

* Removed MAPBOX Token

* Fixed minor issues

* Fixed minor issues

* Changed query files

* Changed monuments query file

* Fixed Unit Tests

* Fixed failing tests

* Fixed errors due to merging
This commit is contained in:
Kanahia 2024-03-24 19:54:39 +05:30 committed by GitHub
parent f404ac9b47
commit 724e4db0fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 576 additions and 219 deletions

View file

@ -83,19 +83,27 @@ import io.reactivex.schedulers.Schedulers;
import timber.log.Timber; import timber.log.Timber;
public class ContributionsFragment public class ContributionsFragment
extends CommonsDaggerSupportFragment extends CommonsDaggerSupportFragment
implements implements
OnBackStackChangedListener, OnBackStackChangedListener,
LocationUpdateListener, LocationUpdateListener,
MediaDetailProvider, MediaDetailProvider,
SensorEventListener, SensorEventListener,
ICampaignsView, ContributionsContract.View, Callback{ ICampaignsView, ContributionsContract.View, Callback {
@Inject @Named("default_preferences") JsonKvStore store;
@Inject NearbyController nearbyController; @Inject
@Inject OkHttpJsonApiClient okHttpJsonApiClient; @Named("default_preferences")
@Inject CampaignsPresenter presenter; JsonKvStore store;
@Inject LocationServiceManager locationManager; @Inject
@Inject NotificationController notificationController; NearbyController nearbyController;
@Inject
OkHttpJsonApiClient okHttpJsonApiClient;
@Inject
CampaignsPresenter presenter;
@Inject
LocationServiceManager locationManager;
@Inject
NotificationController notificationController;
private CompositeDisposable compositeDisposable = new CompositeDisposable(); private CompositeDisposable compositeDisposable = new CompositeDisposable();
@ -129,29 +137,31 @@ public class ContributionsFragment
private SensorManager mSensorManager; private SensorManager mSensorManager;
private Sensor mLight; private Sensor mLight;
private float direction; private float direction;
private ActivityResultLauncher<String[]> nearbyLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() { private ActivityResultLauncher<String[]> nearbyLocationPermissionLauncher = registerForActivityResult(
@Override new ActivityResultContracts.RequestMultiplePermissions(),
public void onActivityResult(Map<String, Boolean> result) { new ActivityResultCallback<Map<String, Boolean>>() {
boolean areAllGranted = true; @Override
for (final boolean b : result.values()) { public void onActivityResult(Map<String, Boolean> result) {
areAllGranted = areAllGranted && b; boolean areAllGranted = true;
} for (final boolean b : result.values()) {
areAllGranted = areAllGranted && b;
}
if (areAllGranted) { if (areAllGranted) {
onLocationPermissionGranted(); onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& store.getBoolean("displayLocationPermissionForCardView", true)
&& !store.getBoolean("doNotAskForLocationPermission", false)
&& (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) {
binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
showNearbyCardPermissionRationale();
} else { } else {
displayYouWontSeeNearbyMessage(); if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& store.getBoolean("displayLocationPermissionForCardView", true)
&& !store.getBoolean("doNotAskForLocationPermission", false)
&& (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) {
binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
showNearbyCardPermissionRationale();
} else {
displayYouWontSeeNearbyMessage();
}
} }
} }
} });
});
@NonNull @NonNull
public static ContributionsFragment newInstance() { public static ContributionsFragment newInstance() {
@ -175,7 +185,8 @@ public class ContributionsFragment
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentContributionsBinding.inflate(inflater, container, false); binding = FragmentContributionsBinding.inflate(inflater, container, false);
@ -192,6 +203,7 @@ public class ContributionsFragment
} }
}); });
if (savedInstanceState != null) { if (savedInstanceState != null) {
mediaDetailPagerFragment = (MediaDetailPagerFragment) getChildFragmentManager() mediaDetailPagerFragment = (MediaDetailPagerFragment) getChildFragmentManager()
.findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG); .findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG);
@ -206,9 +218,9 @@ public class ContributionsFragment
}else { }else {
upDateUploadCount(); upDateUploadCount();
} }
if(shouldShowMediaDetailsFragment){ if (shouldShowMediaDetailsFragment) {
showMediaDetailPagerFragment(); showMediaDetailPagerFragment();
}else{ } else {
if (mediaDetailPagerFragment != null) { if (mediaDetailPagerFragment != null) {
removeFragment(mediaDetailPagerFragment); removeFragment(mediaDetailPagerFragment);
} }
@ -234,10 +246,13 @@ public class ContributionsFragment
} }
@Override @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull final Menu menu,
@NonNull final MenuInflater inflater) {
// Removing contributions menu items for ProfileActivity // Removing contributions menu items for ProfileActivity
if (getActivity() instanceof ProfileActivity) { return; } if (getActivity() instanceof ProfileActivity) {
return;
}
inflater.inflate(R.menu.contribution_activity_notification_menu, menu); inflater.inflate(R.menu.contribution_activity_notification_menu, menu);
@ -259,7 +274,7 @@ public class ContributionsFragment
throwable -> Timber.e(throwable, "Error occurred while loading notifications"))); throwable -> Timber.e(throwable, "Error occurred while loading notifications")));
} }
public void scrollToTop( ){ public void scrollToTop() {
if (contributionsListFragment != null) { if (contributionsListFragment != null) {
contributionsListFragment.scrollToTop(); contributionsListFragment.scrollToTop();
} }
@ -329,13 +344,15 @@ public class ContributionsFragment
binding.cardViewNearby.setVisibility(View.GONE); binding.cardViewNearby.setVisibility(View.GONE);
} }
} }
showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG, mediaDetailPagerFragment); showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG,
mediaDetailPagerFragment);
} }
private void showMediaDetailPagerFragment() { private void showMediaDetailPagerFragment() {
// hide nearby card view on media detail is visible // hide nearby card view on media detail is visible
setupViewForMediaDetails(); setupViewForMediaDetails();
showFragment(mediaDetailPagerFragment, MEDIA_DETAIL_PAGER_FRAGMENT_TAG, contributionsListFragment); showFragment(mediaDetailPagerFragment, MEDIA_DETAIL_PAGER_FRAGMENT_TAG,
contributionsListFragment);
} }
private void setupViewForMediaDetails() { private void setupViewForMediaDetails() {
@ -363,7 +380,8 @@ public class ContributionsFragment
showContributionsListFragment(); showContributionsListFragment();
} }
showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG, mediaDetailPagerFragment); showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG,
mediaDetailPagerFragment);
} }
/** /**
@ -386,7 +404,7 @@ public class ContributionsFragment
transaction.addToBackStack(tag); transaction.addToBackStack(tag);
transaction.commit(); transaction.commit();
getChildFragmentManager().executePendingTransactions(); getChildFragmentManager().executePendingTransactions();
}else if (!fragment.isAdded() && otherFragment != null ) { } else if (!fragment.isAdded() && otherFragment != null) {
transaction.hide(otherFragment); transaction.hide(otherFragment);
transaction.add(R.id.root_frame, fragment, tag); transaction.add(R.id.root_frame, fragment, tag);
transaction.addToBackStack(tag); transaction.addToBackStack(tag);
@ -411,21 +429,21 @@ public class ContributionsFragment
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
private void setUploadCount() { private void setUploadCount() {
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(okHttpJsonApiClient
.getUploadCount(((MainActivity)getActivity()).sessionManager.getCurrentAccount().name) .getUploadCount(((MainActivity) getActivity()).sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(this::displayUploadCount, .subscribe(this::displayUploadCount,
t -> Timber.e(t, "Fetching upload count failed") t -> Timber.e(t, "Fetching upload count failed")
)); ));
} }
private void displayUploadCount(Integer uploadCount) { private void displayUploadCount(Integer uploadCount) {
if (getActivity().isFinishing() if (getActivity().isFinishing()
|| getResources() == null) { || getResources() == null) {
return; return;
} }
((MainActivity)getActivity()).setNumOfUploads(uploadCount); ((MainActivity) getActivity()).setNumOfUploads(uploadCount);
} }
@ -460,7 +478,7 @@ public class ContributionsFragment
if (mediaDetailPagerFragment == null && !isUserProfile) { if (mediaDetailPagerFragment == null && !isUserProfile) {
if (store.getBoolean("displayNearbyCardView", true)) { if (store.getBoolean("displayNearbyCardView", true)) {
checkPermissionsAndShowNearbyCardView(); checkPermissionsAndShowNearbyCardView();
// Calling nearby card to keep showing it even when user clicks on it and comes back // Calling nearby card to keep showing it even when user clicks on it and comes back
try { try {
updateClosestNearbyCardViewInfo(); updateClosestNearbyCardViewInfo();
@ -489,9 +507,9 @@ public class ContributionsFragment
if (PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { if (PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) {
onLocationPermissionGranted(); onLocationPermissionGranted();
} else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& store.getBoolean("displayLocationPermissionForCardView", true) && store.getBoolean("displayLocationPermissionForCardView", true)
&& !store.getBoolean("doNotAskForLocationPermission", false) && !store.getBoolean("doNotAskForLocationPermission", false)
&& (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) { && (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) {
binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
showNearbyCardPermissionRationale(); showNearbyCardPermissionRationale();
} }
@ -508,16 +526,17 @@ public class ContributionsFragment
private void showNearbyCardPermissionRationale() { private void showNearbyCardPermissionRationale() {
DialogUtil.showAlertDialog(getActivity(), DialogUtil.showAlertDialog(getActivity(),
getString(R.string.nearby_card_permission_title), getString(R.string.nearby_card_permission_title),
getString(R.string.nearby_card_permission_explanation), getString(R.string.nearby_card_permission_explanation),
this::requestLocationPermission, this::requestLocationPermission,
this::displayYouWontSeeNearbyMessage, this::displayYouWontSeeNearbyMessage,
checkBoxView, checkBoxView,
false); false);
} }
private void displayYouWontSeeNearbyMessage() { private void displayYouWontSeeNearbyMessage() {
ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.unable_to_display_nearest_place)); ViewUtil.showLongToast(getActivity(),
getResources().getString(R.string.unable_to_display_nearest_place));
store.putBoolean("doNotAskForLocationPermission", true); store.putBoolean("doNotAskForLocationPermission", true);
} }
@ -525,18 +544,21 @@ public class ContributionsFragment
private void updateClosestNearbyCardViewInfo() { private void updateClosestNearbyCardViewInfo() {
curLatLng = locationManager.getLastLocation(); curLatLng = locationManager.getLastLocation();
compositeDisposable.add(Observable.fromCallable(() -> nearbyController compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curLatLng, curLatLng, true, false, false)) // thanks to boolean, it will only return closest result .loadAttractionsFromLocation(curLatLng, curLatLng, true,
.subscribeOn(Schedulers.io()) false)) // thanks to boolean, it will only return closest result
.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io())
.subscribe(this::updateNearbyNotification, .observeOn(AndroidSchedulers.mainThread())
throwable -> { .subscribe(this::updateNearbyNotification,
Timber.d(throwable); throwable -> {
updateNearbyNotification(null); Timber.d(throwable);
})); updateNearbyNotification(null);
}));
} }
private void updateNearbyNotification(@Nullable NearbyController.NearbyPlacesInfo nearbyPlacesInfo) { private void updateNearbyNotification(
if (nearbyPlacesInfo != null && nearbyPlacesInfo.placeList != null && nearbyPlacesInfo.placeList.size() > 0) { @Nullable NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
if (nearbyPlacesInfo != null && nearbyPlacesInfo.placeList != null
&& nearbyPlacesInfo.placeList.size() > 0) {
Place closestNearbyPlace = null; Place closestNearbyPlace = null;
// Find the first nearby place that has no image and exists // Find the first nearby place that has no image and exists
for (Place place : nearbyPlacesInfo.placeList) { for (Place place : nearbyPlacesInfo.placeList) {
@ -546,9 +568,9 @@ public class ContributionsFragment
} }
} }
if(closestNearbyPlace == null) { if (closestNearbyPlace == null) {
binding.cardViewNearby.setVisibility(View.GONE); binding.cardViewNearby.setVisibility(View.GONE);
}else{ } else {
String distance = formatDistanceBetween(curLatLng, closestNearbyPlace.location); String distance = formatDistanceBetween(curLatLng, closestNearbyPlace.location);
closestNearbyPlace.setDistance(distance); closestNearbyPlace.setDistance(distance);
direction = (float) computeBearing(curLatLng, closestNearbyPlace.location); direction = (float) computeBearing(curLatLng, closestNearbyPlace.location);
@ -567,7 +589,7 @@ public class ContributionsFragment
@Override @Override
public void onDestroy() { public void onDestroy() {
try{ try {
compositeDisposable.clear(); compositeDisposable.clear();
getChildFragmentManager().removeOnBackStackChangedListener(this); getChildFragmentManager().removeOnBackStackChangedListener(this);
locationManager.unregisterLocationManager(); locationManager.unregisterLocationManager();
@ -587,7 +609,7 @@ public class ContributionsFragment
@Override @Override
public void onLocationChangedSlightly(LatLng latLng) { public void onLocationChangedSlightly(LatLng latLng) {
/* Update closest nearby notification card onLocationChangedSlightly /* Update closest nearby notification card onLocationChangedSlightly
*/ */
try { try {
updateClosestNearbyCardViewInfo(); updateClosestNearbyCardViewInfo();
} catch (Exception e) { } catch (Exception e) {
@ -601,7 +623,8 @@ public class ContributionsFragment
updateClosestNearbyCardViewInfo(); updateClosestNearbyCardViewInfo();
} }
@Override public void onViewCreated(@NonNull View view, @Override
public void onViewCreated(@NonNull View view,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
} }
@ -626,11 +649,13 @@ public class ContributionsFragment
} }
} }
@Override public void showMessage(String message) { @Override
public void showMessage(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
} }
@Override public void showCampaigns(Campaign campaign) { @Override
public void showCampaigns(Campaign campaign) {
if (campaign != null && !isUserProfile) { if (campaign != null && !isUserProfile) {
if (binding!=null) { if (binding!=null) {
binding.campaignsView.setCampaign(campaign); binding.campaignsView.setCampaign(campaign);
@ -638,7 +663,8 @@ public class ContributionsFragment
} }
} }
@Override public void onDestroyView() { @Override
public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
presenter.onDetachView(); presenter.onDetachView();
} }
@ -652,6 +678,7 @@ public class ContributionsFragment
/** /**
* Restarts the upload process for a contribution * Restarts the upload process for a contribution
*
* @param contribution * @param contribution
*/ */
public void restartUpload(Contribution contribution) { public void restartUpload(Contribution contribution) {
@ -659,6 +686,7 @@ public class ContributionsFragment
contributionsPresenter.saveContribution(contribution); contributionsPresenter.saveContribution(contribution);
Timber.d("Restarting for %s", contribution.toString()); Timber.d("Restarting for %s", contribution.toString());
} }
/** /**
* Retry upload when it is failed * Retry upload when it is failed
* *
@ -667,7 +695,8 @@ public class ContributionsFragment
@Override @Override
public void retryUpload(Contribution contribution) { public void retryUpload(Contribution contribution) {
if (NetworkUtils.isInternetConnectionEstablished(getContext())) { if (NetworkUtils.isInternetConnectionEstablished(getContext())) {
if (contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) { if (contribution.getState() == STATE_PAUSED
|| contribution.getState() == Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) {
restartUpload(contribution); restartUpload(contribution);
} else if (contribution.getState() == STATE_FAILED) { } else if (contribution.getState() == STATE_FAILED) {
int retries = contribution.getRetries(); int retries = contribution.getRetries();
@ -675,9 +704,10 @@ public class ContributionsFragment
/* Limit the number of retries for a failed upload /* Limit the number of retries for a failed upload
to handle cases like invalid filename as such uploads to handle cases like invalid filename as such uploads
will never be successful */ will never be successful */
if(retries < MAX_RETRIES) { if (retries < MAX_RETRIES) {
contribution.setRetries(retries + 1); contribution.setRetries(retries + 1);
Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(), retries + 1); Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(),
retries + 1);
restartUpload(contribution); restartUpload(contribution);
} else { } else {
// TODO: Show the exact reason for failure // TODO: Show the exact reason for failure
@ -695,6 +725,7 @@ public class ContributionsFragment
/** /**
* Pauses the upload * Pauses the upload
*
* @param contribution * @param contribution
*/ */
@Override @Override
@ -718,15 +749,15 @@ public class ContributionsFragment
/** /**
* Replace whatever is in the current contributionsFragmentContainer view with * Replace whatever is in the current contributionsFragmentContainer view with
* mediaDetailPagerFragment, and preserve previous state in back stack. Called when user selects a * mediaDetailPagerFragment, and preserve previous state in back stack. Called when user selects
* contribution. * a contribution.
*/ */
@Override @Override
public void showDetail(int position, boolean isWikipediaButtonDisplayed) { public void showDetail(int position, boolean isWikipediaButtonDisplayed) {
if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment.isVisible()) { if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment.isVisible()) {
mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true); mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true);
if(isUserProfile) { if (isUserProfile) {
((ProfileActivity)getActivity()).setScroll(false); ((ProfileActivity) getActivity()).setScroll(false);
} }
showMediaDetailPagerFragment(); showMediaDetailPagerFragment();
} }
@ -758,17 +789,19 @@ public class ContributionsFragment
binding.cardViewNearby.setVisibility(View.GONE); binding.cardViewNearby.setVisibility(View.GONE);
} }
removeFragment(mediaDetailPagerFragment); removeFragment(mediaDetailPagerFragment);
showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG, mediaDetailPagerFragment); showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG,
if(isUserProfile) { mediaDetailPagerFragment);
if (isUserProfile) {
// Fragment is associated with ProfileActivity // Fragment is associated with ProfileActivity
// Enable ParentViewPager Scroll // Enable ParentViewPager Scroll
((ProfileActivity)getActivity()).setScroll(true); ((ProfileActivity) getActivity()).setScroll(true);
}else { } else {
fetchCampaigns(); fetchCampaigns();
} }
if (getActivity() instanceof MainActivity) { if (getActivity() instanceof MainActivity) {
// Fragment is associated with MainActivity // Fragment is associated with MainActivity
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false); ((BaseActivity) getActivity()).getSupportActionBar()
.setDisplayHomeAsUpEnabled(false);
((MainActivity) getActivity()).showTabs(); ((MainActivity) getActivity()).showTabs();
} }
return true; return true;
@ -788,11 +821,11 @@ public class ContributionsFragment
void upDateUploadCount() { void upDateUploadCount() {
WorkManager.getInstance(getContext()) WorkManager.getInstance(getContext())
.getWorkInfosForUniqueWorkLiveData(UploadWorker.class.getSimpleName()).observe( .getWorkInfosForUniqueWorkLiveData(UploadWorker.class.getSimpleName()).observe(
getViewLifecycleOwner(), workInfos -> { getViewLifecycleOwner(), workInfos -> {
if (workInfos.size() > 0) { if (workInfos.size() > 0) {
setUploadCount(); setUploadCount();
} }
}); });
} }
@ -803,7 +836,7 @@ public class ContributionsFragment
*/ */
@Override @Override
public void refreshNominatedMedia(int index) { public void refreshNominatedMedia(int index) {
if(mediaDetailPagerFragment != null && !contributionsListFragment.isVisible()) { if (mediaDetailPagerFragment != null && !contributionsListFragment.isVisible()) {
removeFragment(mediaDetailPagerFragment); removeFragment(mediaDetailPagerFragment);
mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true); mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true);
mediaDetailPagerFragment.showImage(index); mediaDetailPagerFragment.showImage(index);
@ -811,20 +844,20 @@ public class ContributionsFragment
} }
} }
// click listener to toggle description that means uses can press the limited connection // click listener to toggle description that means uses can press the limited connection
// banner and description will hide. Tap again to show description. // banner and description will hide. Tap again to show description.
private View.OnClickListener toggleDescriptionListener = new View.OnClickListener() { private View.OnClickListener toggleDescriptionListener = new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
View view2 = binding.limitedConnectionDescriptionTextView; View view2 = binding.limitedConnectionDescriptionTextView;
if (view2.getVisibility() == View.GONE) { if (view2.getVisibility() == View.GONE) {
view2.setVisibility(View.VISIBLE); view2.setVisibility(View.VISIBLE);
} else { } else {
view2.setVisibility(View.GONE); view2.setVisibility(View.GONE);
} }
} }
}; };
/** /**
* When the device rotates, rotate the Nearby banner's compass arrow in tandem. * When the device rotates, rotate the Nearby banner's compass arrow in tandem.

View file

@ -269,16 +269,15 @@ public class OkHttpJsonApiClient {
/** /**
* Make API Call to get Nearby Places * Make API Call to get Nearby Places
* *
* @param cur Search lat long * @param cur Search lat long
* @param language Language * @param language Language
* @param radius Search Radius * @param radius Search Radius
* @param shouldQueryForMonuments : Should we query for monuments
* @return * @return
* @throws Exception * @throws Exception
*/ */
@Nullable @Nullable
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius, public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius,
final boolean shouldQueryForMonuments, final String customQuery) final String customQuery)
throws Exception { throws Exception {
Timber.d("Fetching nearby items at radius %s", radius); Timber.d("Fetching nearby items at radius %s", radius);
@ -286,10 +285,9 @@ public class OkHttpJsonApiClient {
final String wikidataQuery; final String wikidataQuery;
if (customQuery != null) { if (customQuery != null) {
wikidataQuery = customQuery; wikidataQuery = customQuery;
} else if (!shouldQueryForMonuments) {
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query.rq");
} else { } else {
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query_monuments.rq"); wikidataQuery = FileUtils.readFromResource(
"/queries/radius_query_for_upload_wizard.rq");
} }
final String query = wikidataQuery final String query = wikidataQuery
.replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius)) .replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius))
@ -307,6 +305,74 @@ public class OkHttpJsonApiClient {
.url(urlBuilder.build()) .url(urlBuilder.build())
.build(); .build();
final Response response = okHttpClient.newCall(request).execute();
if (response.body() != null && response.isSuccessful()) {
final String json = response.body().string();
final NearbyResponse nearbyResponse = gson.fromJson(json, NearbyResponse.class);
final List<NearbyResultItem> bindings = nearbyResponse.getResults().getBindings();
final List<Place> places = new ArrayList<>();
for (final NearbyResultItem item : bindings) {
final Place placeFromNearbyItem = Place.from(item);
placeFromNearbyItem.setMonument(false);
places.add(placeFromNearbyItem);
}
return places;
}
throw new Exception(response.message());
}
/**
* Retrieves nearby places based on screen coordinates and optional query parameters.
*
* @param screenTopRight The top right corner of the screen (latitude, longitude).
* @param screenBottomLeft The bottom left corner of the screen (latitude, longitude).
* @param language The language for the query.
* @param shouldQueryForMonuments Flag indicating whether to include monuments in the query.
* @param customQuery Optional custom SPARQL query to use instead of default
* queries.
* @return A list of nearby places.
* @throws Exception If an error occurs during the retrieval process.
*/
@Nullable
public List<Place> getNearbyPlaces(
final fr.free.nrw.commons.location.LatLng screenTopRight,
final fr.free.nrw.commons.location.LatLng screenBottomLeft, final String language,
final boolean shouldQueryForMonuments, final String customQuery)
throws Exception {
Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null));
final String wikidataQuery;
if (customQuery != null) {
wikidataQuery = customQuery;
} else if (!shouldQueryForMonuments) {
wikidataQuery = FileUtils.readFromResource("/queries/rectangle_query_for_nearby.rq");
} else {
wikidataQuery = FileUtils.readFromResource(
"/queries/rectangle_query_for_nearby_monuments.rq");
}
final double westCornerLat = screenTopRight.getLatitude();
final double westCornerLong = screenTopRight.getLongitude();
final double eastCornerLat = screenBottomLeft.getLatitude();
final double eastCornerLong = screenBottomLeft.getLongitude();
final String query = wikidataQuery
.replace("${LAT_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLat))
.replace("${LONG_WEST}", String.format(Locale.ROOT, "%.4f", westCornerLong))
.replace("${LAT_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLat))
.replace("${LONG_EAST}", String.format(Locale.ROOT, "%.4f", eastCornerLong))
.replace("${LANG}", language);
final HttpUrl.Builder urlBuilder = HttpUrl
.parse(sparqlQueryUrl)
.newBuilder()
.addQueryParameter("query", query)
.addQueryParameter("format", "json");
final Request request = new Request.Builder()
.url(urlBuilder.build())
.build();
final Response response = okHttpClient.newCall(request).execute(); final Response response = okHttpClient.newCall(request).execute();
if (response.body() != null && response.isSuccessful()) { if (response.body() != null && response.isSuccessful()) {
final String json = response.body().string(); final String json = response.body().string();
@ -330,18 +396,16 @@ public class OkHttpJsonApiClient {
/** /**
* Make API Call to get Nearby Places Implementation does not expects a custom query * Make API Call to get Nearby Places Implementation does not expects a custom query
* *
* @param cur Search lat long * @param cur Search lat long
* @param language Language * @param language Language
* @param radius Search Radius * @param radius Search Radius
* @param shouldQueryForMonuments : Should we query for monuments
* @return * @return
* @throws Exception * @throws Exception
*/ */
@Nullable @Nullable
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius, public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius)
final boolean shouldQueryForMonuments)
throws Exception { throws Exception {
return getNearbyPlaces(cur, language, radius, shouldQueryForMonuments, null); return getNearbyPlaces(cur, language, radius, null);
} }
/** /**

View file

@ -39,34 +39,34 @@ public class NearbyController extends MapController {
/** /**
* Prepares Place list to make their distance information update later. * Prepares Place list to make their distance information update later.
* *
* @param curLatLng current location for user * @param curLatLng current location for user
* @param searchLatLng the location user wants to search around * @param searchLatLng the location user wants to search around
* @param returnClosestResult if this search is done to find closest result or all results * @param returnClosestResult if this search is done to find closest result or all results
* @param customQuery if this search is done via an advanced query * @param customQuery if this search is done via an advanced query
* @return NearbyPlacesInfo a variable holds Place list without distance information * @return NearbyPlacesInfo a variable holds Place list without distance information and
* and boundary coordinates of current Place List * boundary coordinates of current Place List
*/ */
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, final LatLng searchLatLng, public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
final LatLng searchLatLng,
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation, final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
final boolean shouldQueryForMonuments, @Nullable final String customQuery) throws Exception { @Nullable final String customQuery) throws Exception {
Timber.d("Loading attractions near %s", searchLatLng); Timber.d("Loading attractions near %s", searchLatLng);
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo(); NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
if (searchLatLng == null) { if (searchLatLng == null) {
Timber.d("Loading attractions nearby, but curLatLng is null"); Timber.d("Loading attractions nearby, but curLatLng is null");
return null; return null;
} }
List<Place> places = nearbyPlaces List<Place> places = nearbyPlaces
.radiusExpander(searchLatLng, Locale.getDefault().getLanguage(), returnClosestResult, .radiusExpander(searchLatLng, Locale.getDefault().getLanguage(), returnClosestResult,
shouldQueryForMonuments, customQuery); customQuery);
if (null != places && places.size() > 0) { if (null != places && places.size() > 0) {
LatLng[] boundaryCoordinates = {places.get(0).location, // south LatLng[] boundaryCoordinates = {
places.get(0).location, // north places.get(0).location, // south
places.get(0).location, // west places.get(0).location, // north
places.get(0).location};// east, init with a random location places.get(0).location, // west
places.get(0).location};// east, init with a random location
if (curLatLng != null) { if (curLatLng != null) {
Timber.d("Sorting places by distance..."); Timber.d("Sorting places by distance...");
@ -88,11 +88,11 @@ public class NearbyController extends MapController {
} }
} }
Collections.sort(places, Collections.sort(places,
(lhs, rhs) -> { (lhs, rhs) -> {
double lhsDistance = distances.get(lhs); double lhsDistance = distances.get(lhs);
double rhsDistance = distances.get(rhs); double rhsDistance = distances.get(rhs);
return (int) (lhsDistance - rhsDistance); return (int) (lhsDistance - rhsDistance);
} }
); );
} }
nearbyPlacesInfo.curLatLng = curLatLng; nearbyPlacesInfo.curLatLng = curLatLng;
@ -104,11 +104,11 @@ public class NearbyController extends MapController {
if (!returnClosestResult) { if (!returnClosestResult) {
// To remember latest search either around user or any point on map // To remember latest search either around user or any point on map
latestSearchLocation = searchLatLng; latestSearchLocation = searchLatLng;
latestSearchRadius = nearbyPlaces.radius*1000; // to meter latestSearchRadius = nearbyPlaces.radius * 1000; // to meter
// Our radius searched around us, will be used to understand when user search their own location, we will follow them // Our radius searched around us, will be used to understand when user search their own location, we will follow them
if (checkingAroundCurrentLocation) { if (checkingAroundCurrentLocation) {
currentLocationSearchRadius = nearbyPlaces.radius*1000; // to meter currentLocationSearchRadius = nearbyPlaces.radius * 1000; // to meter
currentLocation = curLatLng; currentLocation = curLatLng;
} }
} }
@ -118,6 +118,98 @@ public class NearbyController extends MapController {
return nearbyPlacesInfo; return nearbyPlacesInfo;
} }
/**
* Prepares Place list to make their distance information update later.
*
* @param curLatLng The current latitude and longitude.
* @param screenTopRight The top right corner of the screen (latitude,
* longitude).
* @param screenBottomLeft The bottom left corner of the screen (latitude,
* longitude).
* @param searchLatLng The latitude and longitude of the search location.
* @param returnClosestResult Flag indicating whether to return the closest result.
* @param checkingAroundCurrentLocation Flag indicating whether to check around the current
* location.
* @param shouldQueryForMonuments Flag indicating whether to include monuments in the
* query.
* @param customQuery Optional custom SPARQL query to use instead of default
* queries.
* @return An object containing information about nearby places.
* @throws Exception If an error occurs during the retrieval process.
*/
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
final fr.free.nrw.commons.location.LatLng screenTopRight,
final fr.free.nrw.commons.location.LatLng screenBottomLeft, final LatLng searchLatLng,
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
final boolean shouldQueryForMonuments, @Nullable final String customQuery)
throws Exception {
Timber.d("Loading attractions near %s", searchLatLng);
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
if (searchLatLng == null) {
Timber.d("Loading attractions nearby, but curLatLng is null");
return null;
}
List<Place> places = nearbyPlaces.getFromWikidataQuery(screenTopRight, screenBottomLeft,
Locale.getDefault().getLanguage(), shouldQueryForMonuments, customQuery);
if (null != places && places.size() > 0) {
LatLng[] boundaryCoordinates = {
places.get(0).location, // south
places.get(0).location, // north
places.get(0).location, // west
places.get(0).location};// east, init with a random location
if (curLatLng != null) {
Timber.d("Sorting places by distance...");
final Map<Place, Double> distances = new HashMap<>();
for (Place place : places) {
distances.put(place, computeDistanceBetween(place.location, curLatLng));
// Find boundaries with basic find max approach
if (place.location.getLatitude() < boundaryCoordinates[0].getLatitude()) {
boundaryCoordinates[0] = place.location;
}
if (place.location.getLatitude() > boundaryCoordinates[1].getLatitude()) {
boundaryCoordinates[1] = place.location;
}
if (place.location.getLongitude() < boundaryCoordinates[2].getLongitude()) {
boundaryCoordinates[2] = place.location;
}
if (place.location.getLongitude() > boundaryCoordinates[3].getLongitude()) {
boundaryCoordinates[3] = place.location;
}
}
Collections.sort(places,
(lhs, rhs) -> {
double lhsDistance = distances.get(lhs);
double rhsDistance = distances.get(rhs);
return (int) (lhsDistance - rhsDistance);
}
);
}
nearbyPlacesInfo.curLatLng = curLatLng;
nearbyPlacesInfo.searchLatLng = searchLatLng;
nearbyPlacesInfo.placeList = places;
nearbyPlacesInfo.boundaryCoordinates = boundaryCoordinates;
// Returning closes result means we use the controller for nearby card. So no need to set search this area flags
if (!returnClosestResult) {
// To remember latest search either around user or any point on map
latestSearchLocation = searchLatLng;
latestSearchRadius = nearbyPlaces.radius * 1000; // to meter
// Our radius searched around us, will be used to understand when user search their own location, we will follow them
if (checkingAroundCurrentLocation) {
currentLocationSearchRadius = nearbyPlaces.radius * 1000; // to meter
currentLocation = curLatLng;
}
}
}
return nearbyPlacesInfo;
}
/** /**
* Prepares Place list to make their distance information update later. * Prepares Place list to make their distance information update later.
* *
@ -129,10 +221,10 @@ public class NearbyController extends MapController {
*/ */
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
final LatLng searchLatLng, final LatLng searchLatLng,
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation, final boolean returnClosestResult, final boolean checkingAroundCurrentLocation)
final boolean shouldQueryForMonuments) throws Exception { throws Exception {
return loadAttractionsFromLocation(curLatLng, searchLatLng, returnClosestResult, return loadAttractionsFromLocation(curLatLng, searchLatLng, returnClosestResult,
checkingAroundCurrentLocation, shouldQueryForMonuments, null); checkingAroundCurrentLocation, null);
} }
/** /**
@ -171,12 +263,14 @@ public class NearbyController extends MapController {
/** /**
* Updates makerLabelList item isBookmarked value * Updates makerLabelList item isBookmarked value
* @param place place which is bookmarked *
* @param place place which is bookmarked
* @param isBookmarked true is bookmarked, false if bookmark removed * @param isBookmarked true is bookmarked, false if bookmark removed
*/ */
@MainThread @MainThread
public static void updateMarkerLabelListBookmark(Place place, boolean isBookmarked) { public static void updateMarkerLabelListBookmark(Place place, boolean isBookmarked) {
for (ListIterator<MarkerPlaceGroup> iter = markerLabelList.listIterator(); iter.hasNext();) { for (ListIterator<MarkerPlaceGroup> iter = markerLabelList.listIterator();
iter.hasNext(); ) {
MarkerPlaceGroup markerPlaceGroup = iter.next(); MarkerPlaceGroup markerPlaceGroup = iter.next();
if (markerPlaceGroup.getPlace().getWikiDataEntityId().equals(place.getWikiDataEntityId())) { if (markerPlaceGroup.getPlace().getWikiDataEntityId().equals(place.getWikiDataEntityId())) {
iter.set(new MarkerPlaceGroup(isBookmarked, place)); iter.set(new MarkerPlaceGroup(isBookmarked, place));

View file

@ -27,6 +27,7 @@ public class NearbyPlaces {
/** /**
* Reads Wikidata query to check nearby wikidata items which needs picture, with a circular * Reads Wikidata query to check nearby wikidata items which needs picture, with a circular
* search. As a point is center of a circle with a radius will be set later. * search. As a point is center of a circle with a radius will be set later.
*
* @param okHttpJsonApiClient * @param okHttpJsonApiClient
*/ */
@Inject @Inject
@ -36,15 +37,15 @@ public class NearbyPlaces {
/** /**
* Expands the radius as needed for the Wikidata query * Expands the radius as needed for the Wikidata query
* @param curLatLng coordinates of search location *
* @param lang user's language * @param curLatLng coordinates of search location
* @param lang user's language
* @param returnClosestResult true if only the nearest point is desired * @param returnClosestResult true if only the nearest point is desired
* @param customQuery * @param customQuery
* @return list of places obtained * @return list of places obtained
*/ */
List<Place> radiusExpander(final LatLng curLatLng, final String lang, List<Place> radiusExpander(final LatLng curLatLng, final String lang,
final boolean returnClosestResult final boolean returnClosestResult, @Nullable final String customQuery) throws Exception {
, final boolean shouldQueryForMonuments, @Nullable final String customQuery) throws Exception {
final int minResults; final int minResults;
final double maxRadius; final double maxRadius;
@ -63,15 +64,15 @@ public class NearbyPlaces {
radius = INITIAL_RADIUS; radius = INITIAL_RADIUS;
} }
// Increase the radius gradually to find a satisfactory number of nearby places // Increase the radius gradually to find a satisfactory number of nearby places
while (radius <= maxRadius) { while (radius <= maxRadius) {
places = getFromWikidataQuery(curLatLng, lang, radius, shouldQueryForMonuments, customQuery); places = getFromWikidataQuery(curLatLng, lang, radius, customQuery);
Timber.d("%d results at radius: %f", places.size(), radius); Timber.d("%d results at radius: %f", places.size(), radius);
if (places.size() >= minResults) { if (places.size() >= minResults) {
break; break;
}
radius *= RADIUS_MULTIPLIER;
} }
radius *= RADIUS_MULTIPLIER;
}
// make sure we will be able to send at least one request next time // make sure we will be able to send at least one request next time
if (radius > maxRadius) { if (radius > maxRadius) {
radius = maxRadius; radius = maxRadius;
@ -81,18 +82,41 @@ public class NearbyPlaces {
/** /**
* Runs the Wikidata query to populate the Places around search location * Runs the Wikidata query to populate the Places around search location
* @param cur coordinates of search location *
* @param lang user's language * @param cur coordinates of search location
* @param radius radius for search, as determined by radiusExpander() * @param lang user's language
* @param shouldQueryForMonuments should the query include properites for monuments * @param radius radius for search, as determined by radiusExpander()
* @param customQuery * @param customQuery
* @return list of places obtained * @return list of places obtained
* @throws IOException if query fails * @throws IOException if query fails
*/ */
public List<Place> getFromWikidataQuery(final LatLng cur, final String lang, public List<Place> getFromWikidataQuery(final LatLng cur, final String lang,
final double radius, final boolean shouldQueryForMonuments, final double radius,
@Nullable final String customQuery) throws Exception { @Nullable final String customQuery) throws Exception {
return okHttpJsonApiClient return okHttpJsonApiClient
.getNearbyPlaces(cur, lang, radius, shouldQueryForMonuments, customQuery); .getNearbyPlaces(cur, lang, radius, customQuery);
}
/**
* Retrieves a list of places from a Wikidata query based on screen coordinates and optional
* parameters.
*
* @param screenTopRight The top right corner of the screen (latitude, longitude).
* @param screenBottomLeft The bottom left corner of the screen (latitude, longitude).
* @param lang The language for the query.
* @param shouldQueryForMonuments Flag indicating whether to include monuments in the query.
* @param customQuery Optional custom SPARQL query to use instead of default
* queries.
* @return A list of places obtained from the Wikidata query.
* @throws Exception If an error occurs during the retrieval process.
*/
public List<Place> getFromWikidataQuery(
final fr.free.nrw.commons.location.LatLng screenTopRight,
final fr.free.nrw.commons.location.LatLng screenBottomLeft, final String lang,
final boolean shouldQueryForMonuments,
@Nullable final String customQuery) throws Exception {
return okHttpJsonApiClient
.getNearbyPlaces(screenTopRight, screenBottomLeft, lang, shouldQueryForMonuments,
customQuery);
} }
} }

View file

@ -116,6 +116,7 @@ import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import kotlin.Unit; import kotlin.Unit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.osmdroid.api.IGeoPoint;
import org.osmdroid.events.MapEventsReceiver; import org.osmdroid.events.MapEventsReceiver;
import org.osmdroid.events.MapListener; import org.osmdroid.events.MapListener;
import org.osmdroid.events.ScrollEvent; import org.osmdroid.events.ScrollEvent;
@ -489,7 +490,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
final AdvanceQueryFragment fragment = new AdvanceQueryFragment(); final AdvanceQueryFragment fragment = new AdvanceQueryFragment();
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
try { try {
bundle.putString("query", FileUtils.readFromResource("/queries/nearby_query.rq")); bundle.putString("query", FileUtils.readFromResource("/queries/radius_query_for_upload_wizard.rq"));
} catch (IOException e) { } catch (IOException e) {
Timber.e(e); Timber.e(e);
} }
@ -1119,11 +1120,52 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override @Override
public void populatePlaces(final fr.free.nrw.commons.location.LatLng curlatLng) { public void populatePlaces(final fr.free.nrw.commons.location.LatLng curlatLng) {
if (curlatLng.equals(getLastMapFocus())) { // Means we are checking around current location IGeoPoint screenTopRight = mapView.getProjection().fromPixels(mapView.getWidth(), 0);
populatePlacesForCurrentLocation(getLastMapFocus(), curlatLng, null); IGeoPoint screenBottomLeft = mapView.getProjection().fromPixels(0, mapView.getHeight());
fr.free.nrw.commons.location.LatLng screenTopRightLatLng = new fr.free.nrw.commons.location.LatLng(
screenBottomLeft.getLatitude(), screenBottomLeft.getLongitude(), 0);
fr.free.nrw.commons.location.LatLng screenBottomLeftLatLng = new fr.free.nrw.commons.location.LatLng(
screenTopRight.getLatitude(), screenTopRight.getLongitude(), 0);
// When the nearby fragment is opened immediately upon app launch, the {screenTopRightLatLng}
// and {screenBottomLeftLatLng} variables return {LatLng(0.0,0.0)} as output.
// To address this issue, A small delta value {delta = 0.02} is used to adjust the latitude
// and longitude values for {ZOOM_LEVEL = 14f}.
// This adjustment helps in calculating the east and west corner LatLng accurately.
// Note: This only happens when the nearby fragment is opened immediately upon app launch,
// otherwise {screenTopRightLatLng} and {screenBottomLeftLatLng} are used to determine
// the east and west corner LatLng.
if (screenTopRightLatLng.getLatitude() == 0.0 && screenTopRightLatLng.getLongitude() == 0.0
&& screenBottomLeftLatLng.getLatitude() == 0.0
&& screenBottomLeftLatLng.getLongitude() == 0.0) {
final double delta = 0.02;
final double westCornerLat = curlatLng.getLatitude() - delta;
final double westCornerLong = curlatLng.getLongitude() - delta;
final double eastCornerLat = curlatLng.getLatitude() + delta;
final double eastCornerLong = curlatLng.getLongitude() + delta;
screenTopRightLatLng = new fr.free.nrw.commons.location.LatLng(westCornerLat,
westCornerLong, 0);
screenBottomLeftLatLng = new fr.free.nrw.commons.location.LatLng(eastCornerLat,
eastCornerLong, 0);
if (curlatLng.equals(
getLastMapFocus())) { // Means we are checking around current location
populatePlacesForCurrentLocation(getLastMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, curlatLng, null);
} else {
populatePlacesForAnotherLocation(getLastMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, curlatLng, null);
}
} else { } else {
populatePlacesForAnotherLocation(getLastMapFocus(), curlatLng, null); if (curlatLng.equals(
getLastMapFocus())) { // Means we are checking around current location
populatePlacesForCurrentLocation(getLastMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, curlatLng, null);
} else {
populatePlacesForAnotherLocation(getLastMapFocus(), screenTopRightLatLng,
screenBottomLeftLatLng, curlatLng, null);
}
} }
if (recenterToUserLocation) { if (recenterToUserLocation) {
recenterToUserLocation = false; recenterToUserLocation = false;
} }
@ -1136,12 +1178,20 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
populatePlaces(curlatLng); populatePlaces(curlatLng);
return; return;
} }
IGeoPoint screenTopRight = mapView.getProjection().fromPixels(mapView.getWidth(), 0);
IGeoPoint screenBottomLeft = mapView.getProjection().fromPixels(0, mapView.getHeight());
fr.free.nrw.commons.location.LatLng screenTopRightLatLng = new fr.free.nrw.commons.location.LatLng(
screenBottomLeft.getLatitude(), screenBottomLeft.getLongitude(), 0);
fr.free.nrw.commons.location.LatLng screenBottomLeftLatLng = new fr.free.nrw.commons.location.LatLng(
screenTopRight.getLatitude(), screenTopRight.getLongitude(), 0);
if (curlatLng.equals(lastFocusLocation) || lastFocusLocation == null if (curlatLng.equals(lastFocusLocation) || lastFocusLocation == null
|| recenterToUserLocation) { // Means we are checking around current location || recenterToUserLocation) { // Means we are checking around current location
populatePlacesForCurrentLocation(lastKnownLocation, curlatLng, customQuery); populatePlacesForCurrentLocation(lastKnownLocation, screenTopRightLatLng,
screenBottomLeftLatLng, curlatLng, customQuery);
} else { } else {
populatePlacesForAnotherLocation(lastKnownLocation, curlatLng, customQuery); populatePlacesForAnotherLocation(lastKnownLocation, screenTopRightLatLng,
screenBottomLeftLatLng, curlatLng, customQuery);
} }
if (recenterToUserLocation) { if (recenterToUserLocation) {
recenterToUserLocation = false; recenterToUserLocation = false;
@ -1150,11 +1200,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private void populatePlacesForCurrentLocation( private void populatePlacesForCurrentLocation(
final fr.free.nrw.commons.location.LatLng curlatLng, final fr.free.nrw.commons.location.LatLng curlatLng,
final fr.free.nrw.commons.location.LatLng screenTopRight,
final fr.free.nrw.commons.location.LatLng screenBottomLeft,
final fr.free.nrw.commons.location.LatLng searchLatLng, final fr.free.nrw.commons.location.LatLng searchLatLng,
@Nullable final String customQuery) { @Nullable final String customQuery) {
final Observable<NearbyController.NearbyPlacesInfo> nearbyPlacesInfoObservable = Observable final Observable<NearbyController.NearbyPlacesInfo> nearbyPlacesInfoObservable = Observable
.fromCallable(() -> nearbyController .fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curlatLng, searchLatLng, .loadAttractionsFromLocation(curlatLng, screenTopRight, screenBottomLeft,
searchLatLng,
false, true, Utils.isMonumentsEnabled(new Date()), customQuery)); false, true, Utils.isMonumentsEnabled(new Date()), customQuery));
compositeDisposable.add(nearbyPlacesInfoObservable compositeDisposable.add(nearbyPlacesInfoObservable
@ -1182,11 +1235,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private void populatePlacesForAnotherLocation( private void populatePlacesForAnotherLocation(
final fr.free.nrw.commons.location.LatLng curlatLng, final fr.free.nrw.commons.location.LatLng curlatLng,
final fr.free.nrw.commons.location.LatLng screenTopRight,
final fr.free.nrw.commons.location.LatLng screenBottomLeft,
final fr.free.nrw.commons.location.LatLng searchLatLng, final fr.free.nrw.commons.location.LatLng searchLatLng,
@Nullable final String customQuery) { @Nullable final String customQuery) {
final Observable<NearbyPlacesInfo> nearbyPlacesInfoObservable = Observable final Observable<NearbyPlacesInfo> nearbyPlacesInfoObservable = Observable
.fromCallable(() -> nearbyController .fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curlatLng, searchLatLng, .loadAttractionsFromLocation(curlatLng, screenTopRight, screenBottomLeft,
searchLatLng,
false, true, Utils.isMonumentsEnabled(new Date()), customQuery)); false, true, Utils.isMonumentsEnabled(new Date()), customQuery));
compositeDisposable.add(nearbyPlacesInfoObservable compositeDisposable.add(nearbyPlacesInfoObservable

View file

@ -345,6 +345,7 @@ public class UploadRepository {
/** /**
* Returns nearest place matching the passed latitude and longitude * Returns nearest place matching the passed latitude and longitude
*
* @param decLatitude * @param decLatitude
* @param decLongitude * @param decLongitude
* @return * @return
@ -354,11 +355,11 @@ public class UploadRepository {
try { try {
final List<Place> fromWikidataQuery = nearbyPlaces.getFromWikidataQuery(new LatLng( final List<Place> fromWikidataQuery = nearbyPlaces.getFromWikidataQuery(new LatLng(
decLatitude, decLongitude, 0.0f), decLatitude, decLongitude, 0.0f),
Locale.getDefault().getLanguage(), Locale.getDefault().getLanguage(),
NEARBY_RADIUS_IN_KILO_METERS, false, null); NEARBY_RADIUS_IN_KILO_METERS, null);
return (fromWikidataQuery != null && fromWikidataQuery.size() > 0) ? fromWikidataQuery return (fromWikidataQuery != null && fromWikidataQuery.size() > 0) ? fromWikidataQuery
.get(0) : null; .get(0) : null;
}catch (final Exception e) { } catch (final Exception e) {
Timber.e("Error fetching nearby places: %s", e.getMessage()); Timber.e("Error fetching nearby places: %s", e.getMessage());
return null; return null;
} }

View file

@ -49,8 +49,10 @@ class FileProcessor @Inject constructor(
/** /**
* Processes filePath coordinates, either from EXIF data or user location * Processes filePath coordinates, either from EXIF data or user location
*/ */
fun processFileCoordinates(similarImageInterface: SimilarImageInterface, fun processFileCoordinates(
filePath: String?, inAppPictureLocation: LatLng?) similarImageInterface: SimilarImageInterface,
filePath: String?, inAppPictureLocation: LatLng?
)
: ImageCoordinates { : ImageCoordinates {
val exifInterface: ExifInterface? = try { val exifInterface: ExifInterface? = try {
ExifInterface(filePath!!) ExifInterface(filePath!!)
@ -207,8 +209,7 @@ class FileProcessor @Inject constructor(
okHttpJsonApiClient.getNearbyPlaces( okHttpJsonApiClient.getNearbyPlaces(
imageCoordinates.latLng, imageCoordinates.latLng,
Locale.getDefault().language, Locale.getDefault().language,
it, it
false
) )
} }
} }

View file

@ -85,7 +85,7 @@ public class FileUtils {
/** /**
* Read and return the content of a resource filePath as string. * Read and return the content of a resource filePath as string.
* *
* @param fileName asset filePath's path (e.g. "/queries/nearby_query.rq") * @param fileName asset filePath's path (e.g. "/queries/radius_query_for_upload_wizard.rq")
* @return the content of the filePath * @return the content of the filePath
*/ */
public static String readFromResource(String fileName) throws IOException { public static String readFromResource(String fileName) throws IOException {

View file

@ -0,0 +1,56 @@
SELECT
?item
(SAMPLE(?location) as ?location)
(SAMPLE(?label) AS ?label)
(SAMPLE(?description) AS ?description)
(SAMPLE(?class) AS ?class)
(SAMPLE(?classLabel) AS ?classLabel)
(SAMPLE(?pic) AS ?pic)
(SAMPLE(?destroyed) AS ?destroyed)
(SAMPLE(?endTime) AS ?endTime)
(SAMPLE(?wikipediaArticle) AS ?wikipediaArticle)
(SAMPLE(?commonsArticle) AS ?commonsArticle)
(SAMPLE(?commonsCategory) AS ?commonsCategory)
WHERE {
# Around given location
SERVICE wikibase:box {
?item wdt:P625 ?location.
bd:serviceParam wikibase:cornerWest "Point(${LONG_WEST} ${LAT_WEST})"^^geo:wktLiteral.
bd:serviceParam wikibase:cornerEast "Point(${LONG_EAST} ${LAT_EAST})"^^geo:wktLiteral.
}
OPTIONAL {
?item p:P31/ps:P31 ?class.
}
# Get picture
OPTIONAL {?item wdt:P18 ?pic}
# Get existence
OPTIONAL {?item wdt:P576 ?destroyed}
OPTIONAL {?item wdt:P582 ?endTime}
# Get Commons category
OPTIONAL {?item wdt:P373 ?commonsCategory}
# Get Wikipedia article
OPTIONAL {
?wikipediaArticle schema:about ?item.
?wikipediaArticle schema:isPartOf <https://${LANG}.wikipedia.org/>.
}
# Get Commons article
OPTIONAL {
?commonsArticle schema:about ?item.
?commonsArticle schema:isPartOf <https://commons.wikimedia.org/>.
}
# Labels and descriptions
SERVICE wikibase:label {
bd:serviceParam wikibase:language "${LANG},en,fr,de,es,ja,ru,it,zh,pt,ar,fa,pl,nl,id,uk,he,sv,cs,ko,vi,ca,no,fi,hu,tr,th,hi,bn,ceb,ro,sw,kk,da,eo,sr,lt,sk,bg,sl,eu,et,hr,ms,el,arz,ur,ta,te,nn,gl,az,af,bs,be,ml,ka,is,sq,uz,la,br,mk,lv,azb,mr,sh,tl,cy,ckb,ast,be-tarask,zh-yue,hy,pa,as,my,kn,ne,si,tt,ha,war,zh-min-nan,vo,min,lmo,ht,lb,gu,tg,sco,ku,new,bpy,nds,io,pms,su,oc,jv,nap,ba,scn,wa,bar,an,ksh,szl,fy,frr,als,ia,ga,yi,mg,gd,vec,ce,sa,mai,xmf,sd,wuu,mrj,mhr,km,roa-tara,am,roa-rup,map-bms,bh,mnw,shn,bcl,co,cv,dv,nds-nl,fo,hif,fur,gan,glk,hak,ilo,pam,csb,avk,lij,li,gv,mi,mt,nah,nrm,se,nov,qu,os,pi,pag,ps,pdc,rm,bat-smg,sc,to,tk,hsb,fiu-vro,vls,yo,diq,zh-classical,frp,lad,kw,mn,haw,ang,ln,ie,wo,tpi,ty,crh,nv,jbo,ay,pcd,zea,eml,ky,ig,or,cbk-zam,kg,arc,rmy,ab,gn,so,kab,ug,stq,udm,ext,mzn,pap,cu,sah,tet,sn,lo,pnb,iu,na,got,bo,dsb,chr,cdo,om,sm,ee,ti,av,bm,zu,pnt,cr,pih,ss,ve,bi,rw,ch,xh,kl,ik,bug,dz,ts,tn,kv,tum,xal,st,tw,bxr,ak,ny,fj,lbe,za,ks,ff,lg,sg,rn,chy,mwl,lez,bjn,gom,tyv,vep,nso,kbd,ltg,rue,pfl,gag,koi,krc,ace,olo,kaa,mdf,myv,srn,ady,jam,tcy,dty,atj,kbp,din,lfn,gor,inh,sat,hyw,nqo,ban,szy,awa,ary,lld,smn,skr,mad,dag,shi,nia,ki,gcr".
?item rdfs:label ?label.
?item schema:description ?description.
?class rdfs:label ?classLabel.
}
}
GROUP BY ?item

View file

@ -14,10 +14,10 @@ SELECT
(SAMPLE(?monument) AS ?monument) (SAMPLE(?monument) AS ?monument)
WHERE { WHERE {
# Around given location # Around given location
SERVICE wikibase:around { SERVICE wikibase:box {
?item wdt:P625 ?location. ?item wdt:P625 ?location.
bd:serviceParam wikibase:center "Point(${LONG} ${LAT})"^^geo:wktLiteral. # Longitude latitude bd:serviceParam wikibase:cornerWest "Point(${LONG_WEST} ${LAT_WEST})"^^geo:wktLiteral.
bd:serviceParam wikibase:radius "${RAD}". # Radius in kilometers. bd:serviceParam wikibase:cornerEast "Point(${LONG_EAST} ${LAT_EAST})"^^geo:wktLiteral.
} }
OPTIONAL { OPTIONAL {

View file

@ -62,7 +62,8 @@ class OkHttpJsonApiClientTests {
fun testGetNearbyPlacesCustomQuery() { fun testGetNearbyPlacesCustomQuery() {
Mockito.`when`(response.message).thenReturn("test") Mockito.`when`(response.message).thenReturn("test")
try { try {
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, true, "test") okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, "test")
okHttpJsonApiClient.getNearbyPlaces(latLng, latLng, "test", true, "test")
} catch (e: Exception) { } catch (e: Exception) {
assert(e.message.equals("test")) assert(e.message.equals("test"))
} }
@ -75,7 +76,8 @@ class OkHttpJsonApiClientTests {
fun testGetNearbyPlaces() { fun testGetNearbyPlaces() {
Mockito.`when`(response.message).thenReturn("test") Mockito.`when`(response.message).thenReturn("test")
try { try {
okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, true) okHttpJsonApiClient.getNearbyPlaces(latLng, "test", 10.0, null)
okHttpJsonApiClient.getNearbyPlaces(latLng, latLng, "test", true, null)
} catch (e: Exception) { } catch (e: Exception) {
assert(e.message.equals("test")) assert(e.message.equals("test"))
} }

View file

@ -35,6 +35,12 @@ class NearbyControllerTest {
@Mock @Mock
private lateinit var nearbyPlaces: NearbyPlaces private lateinit var nearbyPlaces: NearbyPlaces
@Mock
private lateinit var screenTopRight: LatLng
@Mock
private lateinit var screenBottomLeft: LatLng
@Mock @Mock
private lateinit var searchLatLong: LatLng private lateinit var searchLatLong: LatLng
@ -56,41 +62,48 @@ class NearbyControllerTest {
@Test @Test
fun testLoadAttractionsForLocationTest() { fun testLoadAttractionsForLocationTest() {
`when`(nearbyPlaces.radiusExpander(any(), any(), any(), any(), any())) `when`(nearbyPlaces.radiusExpander(any(), any(), any(), any()))
.thenReturn(Collections.emptyList()) .thenReturn(Collections.emptyList())
nearbyController.loadAttractionsFromLocation( nearbyController.loadAttractionsFromLocation(
searchLatLong,
currentLatLng, currentLatLng,
false, searchLatLong,
false, false,
true, true,
customQuery customQuery
) )
nearbyController.loadAttractionsFromLocation(
currentLatLng,
screenTopRight,
screenBottomLeft,
searchLatLong,
false,
true,
false,
customQuery
)
Mockito.verify(nearbyPlaces).radiusExpander( Mockito.verify(nearbyPlaces).radiusExpander(
eq(currentLatLng), eq(searchLatLong),
any(String::class.java), any(String::class.java),
eq(false), eq(false),
eq(true),
eq(customQuery) eq(customQuery)
) )
} }
@Test @Test
fun testLoadAttractionsForLocationTestNoQuery() { fun testLoadAttractionsForLocationTestNoQuery() {
`when`(nearbyPlaces.radiusExpander(any(), any(), any(), any(), anyOrNull())) `when`(nearbyPlaces.radiusExpander(any(), any(), any(), anyOrNull()))
.thenReturn(Collections.emptyList()) .thenReturn(Collections.emptyList())
nearbyController.loadAttractionsFromLocation( nearbyController.loadAttractionsFromLocation(
searchLatLong,
currentLatLng, currentLatLng,
searchLatLong,
false, false,
false, true,
true null
) )
Mockito.verify(nearbyPlaces).radiusExpander( Mockito.verify(nearbyPlaces).radiusExpander(
eq(currentLatLng), eq(searchLatLong),
any(String::class.java), any(String::class.java),
eq(false), eq(false),
eq(true),
eq(null) eq(null)
) )
} }
@ -102,8 +115,7 @@ class NearbyControllerTest {
currentLatLng, currentLatLng,
null, null,
false, false,
false, true,
false,
customQuery customQuery
), null ), null
) )
@ -136,7 +148,7 @@ class NearbyControllerTest {
`when`( `when`(
nearbyPlaces.radiusExpander( nearbyPlaces.radiusExpander(
searchLatLong, Locale.getDefault().language, false, searchLatLong, Locale.getDefault().language, false,
false, customQuery customQuery
) )
).thenReturn(mutableListOf(place1, place2)) ).thenReturn(mutableListOf(place1, place2))
val result = nearbyController.loadAttractionsFromLocation( val result = nearbyController.loadAttractionsFromLocation(
@ -144,6 +156,15 @@ class NearbyControllerTest {
searchLatLong, searchLatLong,
false, false,
true, true,
customQuery
)
nearbyController.loadAttractionsFromLocation(
currentLatLng,
screenTopRight,
screenBottomLeft,
searchLatLong,
false,
true,
false, false,
customQuery customQuery
) )
@ -178,7 +199,7 @@ class NearbyControllerTest {
`when`( `when`(
nearbyPlaces.radiusExpander( nearbyPlaces.radiusExpander(
searchLatLong, Locale.getDefault().language, false, searchLatLong, Locale.getDefault().language, false,
false, customQuery customQuery
) )
).thenReturn(mutableListOf(place1, place2)) ).thenReturn(mutableListOf(place1, place2))
val result = nearbyController.loadAttractionsFromLocation( val result = nearbyController.loadAttractionsFromLocation(
@ -186,7 +207,6 @@ class NearbyControllerTest {
searchLatLong, searchLatLong,
false, false,
true, true,
false,
customQuery customQuery
) )
assertEquals(result.curLatLng, currentLatLng) assertEquals(result.curLatLng, currentLatLng)
@ -220,7 +240,7 @@ class NearbyControllerTest {
`when`( `when`(
nearbyPlaces.radiusExpander( nearbyPlaces.radiusExpander(
searchLatLong, Locale.getDefault().language, false, searchLatLong, Locale.getDefault().language, false,
false, customQuery customQuery
) )
).thenReturn(mutableListOf(place1, place2)) ).thenReturn(mutableListOf(place1, place2))
val result = nearbyController.loadAttractionsFromLocation( val result = nearbyController.loadAttractionsFromLocation(
@ -228,7 +248,6 @@ class NearbyControllerTest {
searchLatLong, searchLatLong,
false, false,
true, true,
false,
customQuery customQuery
) )
assertEquals(result.curLatLng, currentLatLng) assertEquals(result.curLatLng, currentLatLng)

View file

@ -28,12 +28,11 @@ class NearbyPlacesTest {
@Test @Test
fun testRadiusExpander() { fun testRadiusExpander() {
nearbyPlaces.radiusExpander(currentLatLong, "test", true, true, "test") nearbyPlaces.radiusExpander(currentLatLong, "test", true, "test")
verify(okHttpJsonApiClient, times(5)).getNearbyPlaces( verify(okHttpJsonApiClient, times(5)).getNearbyPlaces(
eq(currentLatLong), eq(currentLatLong),
eq("test"), eq("test"),
any(), any(),
eq(true),
eq("test") eq("test")
) )
} }

View file

@ -217,8 +217,10 @@ class UploadRepositoryUnitTest {
@Test @Test
fun testSetSelectedExistingDepictions() { fun testSetSelectedExistingDepictions() {
assertEquals(repository.setSelectedExistingDepictions(listOf("")), assertEquals(
uploadModel.setSelectedExistingDepictions(listOf(""))) repository.setSelectedExistingDepictions(listOf("")),
uploadModel.setSelectedExistingDepictions(listOf(""))
)
} }
@Test @Test
@ -264,7 +266,7 @@ class UploadRepositoryUnitTest {
nearbyPlaces.getFromWikidataQuery( nearbyPlaces.getFromWikidataQuery(
LatLng(0.0, 0.0, 0.0f), LatLng(0.0, 0.0, 0.0f),
java.util.Locale.getDefault().language, 0.1, java.util.Locale.getDefault().language, 0.1,
false, null null
) )
).thenReturn(listOf(place)) ).thenReturn(listOf(place))
assertEquals( assertEquals(
@ -287,7 +289,7 @@ class UploadRepositoryUnitTest {
nearbyPlaces.getFromWikidataQuery( nearbyPlaces.getFromWikidataQuery(
LatLng(0.0, 0.0, 0.0f), LatLng(0.0, 0.0, 0.0f),
java.util.Locale.getDefault().language, 0.1, java.util.Locale.getDefault().language, 0.1,
false, null null
) )
).thenThrow(Exception()) ).thenThrow(Exception())
assertEquals( assertEquals(
@ -348,19 +350,25 @@ class UploadRepositoryUnitTest {
@Test @Test
fun testGetSelectedExistingCategories() { fun testGetSelectedExistingCategories() {
assertEquals(repository.selectedExistingCategories, assertEquals(
categoriesModel.getSelectedExistingCategories()) repository.selectedExistingCategories,
categoriesModel.getSelectedExistingCategories()
)
} }
@Test @Test
fun testSetSelectedExistingCategories() { fun testSetSelectedExistingCategories() {
assertEquals(repository.setSelectedExistingCategories(listOf("Test")), assertEquals(
categoriesModel.setSelectedExistingCategories(mutableListOf("Test"))) repository.setSelectedExistingCategories(listOf("Test")),
categoriesModel.setSelectedExistingCategories(mutableListOf("Test"))
)
} }
@Test @Test
fun testGetCategories() { fun testGetCategories() {
assertEquals(repository.getCategories(listOf("Test")), assertEquals(
categoriesModel.getCategoriesByName(mutableListOf("Test"))) repository.getCategories(listOf("Test")),
categoriesModel.getCategoriesByName(mutableListOf("Test"))
)
} }
} }