Skip to content

Commit a0ff15c

Browse files
Added advanced query support in Nearby (commons-app#4714)
* Added feature for advanced query options in Nearby * Fix unit-tests coverage * wip-tests * Added log to identify if the nearby request is via an advanced query * Rolledback test-dependency updates * Fix tests * build fix * Added basic tests for relevant method in OkHttpJsonApiClient & NearbyController * Added NearbyParentFragmentPresenter Tests * Added AdvancedQueryFragment Unit Tests * Added more tests for NearbyParentFragmentPresenter * Reset ContributionsFragment with Upstream * Overload method loadNearbyPlaces for CustomQuery * Added more tests * Added more tests * Fixed tests for NearbyParentFragmentPresenter
1 parent bd00ce2 commit a0ff15c

19 files changed

+823
-47
lines changed

app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ public enum LocationChangeType{
268268
LOCATION_NOT_CHANGED,
269269
PERMISSION_JUST_GRANTED,
270270
MAP_UPDATED,
271-
SEARCH_CUSTOM_AREA
271+
SEARCH_CUSTOM_AREA,
272+
CUSTOM_QUERY
272273
}
273274
}

app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java

+33-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import androidx.annotation.NonNull;
88
import androidx.annotation.Nullable;
99
import com.google.gson.Gson;
10+
import fr.free.nrw.commons.auth.SessionManager;
1011
import fr.free.nrw.commons.campaigns.CampaignResponseDTO;
1112
import fr.free.nrw.commons.explore.depictions.DepictsClient;
1213
import fr.free.nrw.commons.location.LatLng;
@@ -265,14 +266,27 @@ public Single<FeedbackResponse> getAchievements(String userName) {
265266
});
266267
}
267268

269+
/**
270+
* Make API Call to get Nearby Places
271+
*
272+
* @param cur Search lat long
273+
* @param language Language
274+
* @param radius Search Radius
275+
* @param shouldQueryForMonuments : Should we query for monuments
276+
* @return
277+
* @throws Exception
278+
*/
268279
@Nullable
269280
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius,
270-
final boolean shouldQueryForMonuments)
281+
final boolean shouldQueryForMonuments, final String customQuery)
271282
throws Exception {
272283

273284
Timber.d("Fetching nearby items at radius %s", radius);
285+
Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null));
274286
final String wikidataQuery;
275-
if (!shouldQueryForMonuments) {
287+
if (customQuery != null) {
288+
wikidataQuery = customQuery;
289+
} else if (!shouldQueryForMonuments) {
276290
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query.rq");
277291
} else {
278292
wikidataQuery = FileUtils.readFromResource("/queries/nearby_query_monuments.rq");
@@ -313,6 +327,23 @@ public List<Place> getNearbyPlaces(final LatLng cur, final String language, fina
313327
throw new Exception(response.message());
314328
}
315329

330+
/**
331+
* Make API Call to get Nearby Places Implementation does not expects a custom query
332+
*
333+
* @param cur Search lat long
334+
* @param language Language
335+
* @param radius Search Radius
336+
* @param shouldQueryForMonuments : Should we query for monuments
337+
* @return
338+
* @throws Exception
339+
*/
340+
@Nullable
341+
public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius,
342+
final boolean shouldQueryForMonuments)
343+
throws Exception {
344+
return getNearbyPlaces(cur, language, radius, shouldQueryForMonuments, null);
345+
}
346+
316347
/**
317348
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
318349
* bridge -> suspended bridge, aqueduct, etc

app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import android.graphics.Bitmap;
66

77
import androidx.annotation.MainThread;
8+
import androidx.annotation.Nullable;
89
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
910

1011
import com.mapbox.mapboxsdk.annotations.IconFactory;
@@ -54,12 +55,13 @@ public NearbyController(NearbyPlaces nearbyPlaces) {
5455
* @param curLatLng current location for user
5556
* @param searchLatLng the location user wants to search around
5657
* @param returnClosestResult if this search is done to find closest result or all results
58+
* @param customQuery if this search is done via an advanced query
5759
* @return NearbyPlacesInfo a variable holds Place list without distance information
5860
* and boundary coordinates of current Place List
5961
*/
6062
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, final LatLng searchLatLng,
6163
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
62-
final boolean shouldQueryForMonuments) throws Exception {
64+
final boolean shouldQueryForMonuments, @Nullable final String customQuery) throws Exception {
6365

6466
Timber.d("Loading attractions near %s", searchLatLng);
6567
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
@@ -70,7 +72,7 @@ public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, fina
7072
}
7173
List<Place> places = nearbyPlaces
7274
.radiusExpander(searchLatLng, Locale.getDefault().getLanguage(), returnClosestResult,
73-
shouldQueryForMonuments);
75+
shouldQueryForMonuments, customQuery);
7476

7577
if (null != places && places.size() > 0) {
7678
LatLng[] boundaryCoordinates = {places.get(0).location, // south
@@ -128,10 +130,27 @@ public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng, fina
128130
return nearbyPlacesInfo;
129131
}
130132
else {
131-
return null;
133+
return nearbyPlacesInfo;
132134
}
133135
}
134136

137+
/**
138+
* Prepares Place list to make their distance information update later.
139+
*
140+
* @param curLatLng current location for user
141+
* @param searchLatLng the location user wants to search around
142+
* @param returnClosestResult if this search is done to find closest result or all results
143+
* @return NearbyPlacesInfo a variable holds Place list without distance information and
144+
* boundary coordinates of current Place List
145+
*/
146+
public NearbyPlacesInfo loadAttractionsFromLocation(final LatLng curLatLng,
147+
final LatLng searchLatLng,
148+
final boolean returnClosestResult, final boolean checkingAroundCurrentLocation,
149+
final boolean shouldQueryForMonuments) throws Exception {
150+
return loadAttractionsFromLocation(curLatLng, searchLatLng, returnClosestResult,
151+
checkingAroundCurrentLocation, shouldQueryForMonuments, null);
152+
}
153+
135154
/**
136155
* Loads attractions from location for list view, we need to return Place data type.
137156
*

app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package fr.free.nrw.commons.nearby;
22

3+
import androidx.annotation.Nullable;
34
import java.io.IOException;
45
import java.util.Collections;
56
import java.util.List;
@@ -38,10 +39,12 @@ public NearbyPlaces(OkHttpJsonApiClient okHttpJsonApiClient) {
3839
* @param curLatLng coordinates of search location
3940
* @param lang user's language
4041
* @param returnClosestResult true if only the nearest point is desired
42+
* @param customQuery
4143
* @return list of places obtained
4244
*/
43-
List<Place> radiusExpander(final LatLng curLatLng, final String lang, final boolean returnClosestResult
44-
, final boolean shouldQueryForMonuments) throws Exception {
45+
List<Place> radiusExpander(final LatLng curLatLng, final String lang,
46+
final boolean returnClosestResult
47+
, final boolean shouldQueryForMonuments, @Nullable final String customQuery) throws Exception {
4548

4649
final int minResults;
4750
final double maxRadius;
@@ -62,13 +65,12 @@ List<Place> radiusExpander(final LatLng curLatLng, final String lang, final bool
6265

6366
// Increase the radius gradually to find a satisfactory number of nearby places
6467
while (radius <= maxRadius) {
65-
places = getFromWikidataQuery(curLatLng, lang, radius, shouldQueryForMonuments);
68+
places = getFromWikidataQuery(curLatLng, lang, radius, shouldQueryForMonuments, customQuery);
6669
Timber.d("%d results at radius: %f", places.size(), radius);
6770
if (places.size() >= minResults) {
6871
break;
69-
} else {
70-
radius *= RADIUS_MULTIPLIER;
7172
}
73+
radius *= RADIUS_MULTIPLIER;
7274
}
7375
// make sure we will be able to send at least one request next time
7476
if (radius > maxRadius) {
@@ -83,11 +85,14 @@ List<Place> radiusExpander(final LatLng curLatLng, final String lang, final bool
8385
* @param lang user's language
8486
* @param radius radius for search, as determined by radiusExpander()
8587
* @param shouldQueryForMonuments should the query include properites for monuments
88+
* @param customQuery
8689
* @return list of places obtained
8790
* @throws IOException if query fails
8891
*/
8992
public List<Place> getFromWikidataQuery(final LatLng cur, final String lang,
90-
final double radius, final boolean shouldQueryForMonuments) throws Exception {
91-
return okHttpJsonApiClient.getNearbyPlaces(cur, lang, radius, shouldQueryForMonuments);
93+
final double radius, final boolean shouldQueryForMonuments,
94+
@Nullable final String customQuery) throws Exception {
95+
return okHttpJsonApiClient
96+
.getNearbyPlaces(cur, lang, radius, shouldQueryForMonuments, customQuery);
9297
}
9398
}

app/src/main/java/fr/free/nrw/commons/nearby/contract/NearbyParentFragmentContract.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
import android.content.Context;
44

5+
import androidx.annotation.Nullable;
56
import com.mapbox.mapboxsdk.annotations.Marker;
67

8+
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType;
79
import java.util.List;
810

911
import fr.free.nrw.commons.kvstore.JsonKvStore;
1012
import fr.free.nrw.commons.location.LatLng;
11-
import fr.free.nrw.commons.location.LocationServiceManager;
1213
import fr.free.nrw.commons.nearby.Label;
1314
import fr.free.nrw.commons.nearby.NearbyBaseMarker;
1415
import fr.free.nrw.commons.nearby.Place;
@@ -19,6 +20,7 @@ interface View {
1920
boolean isNetworkConnectionEstablished();
2021
void listOptionMenuItemClicked();
2122
void populatePlaces(LatLng curlatLng);
23+
void populatePlaces(LatLng curlatLng, String customQuery);
2224
boolean isListBottomSheetExpanded();
2325
void checkPermissionsAndPerformAction();
2426
void displayLoginSkippedWarning();
@@ -62,7 +64,7 @@ interface View {
6264

6365
LatLng getCameraTarget();
6466

65-
void centerMapToPlace(Place placeToCenter);
67+
void centerMapToPlace(@Nullable Place placeToCenter);
6668

6769
void updateListFragment(List<Place> placeList);
6870

@@ -72,14 +74,20 @@ interface View {
7274

7375
boolean isCurrentLocationMarkerVisible();
7476
void setProjectorLatLngBounds();
77+
78+
boolean isAdvancedQueryFragmentVisible();
79+
80+
void showHideAdvancedQueryFragment(boolean shouldShow);
81+
82+
void centerMapToPosition(@Nullable LatLng searchLatLng);
7583
}
7684

7785
interface NearbyListView {
7886
void updateListFragment(List<Place> placeList);
7987
}
8088

8189
interface UserActions {
82-
void updateMapAndList(LocationServiceManager.LocationChangeType locationChangeType);
90+
void updateMapAndList(LocationChangeType locationChangeType);
8391
void lockUnlockNearby(boolean isNearbyLocked);
8492

8593
void attachView(View view);
@@ -96,5 +104,7 @@ interface UserActions {
96104

97105
void searchViewGainedFocus();
98106
void setCheckboxUnknown();
107+
108+
void setAdvancedQuery(String query);
99109
}
100110
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package fr.free.nrw.commons.nearby.fragments
2+
3+
import android.content.Context.INPUT_METHOD_SERVICE
4+
import android.os.Bundle
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.view.inputmethod.InputMethodManager
9+
import androidx.appcompat.widget.AppCompatButton
10+
import androidx.appcompat.widget.AppCompatEditText
11+
import androidx.fragment.app.Fragment
12+
import fr.free.nrw.commons.R
13+
import kotlinx.android.synthetic.main.fragment_advance_query.*
14+
15+
class AdvanceQueryFragment : Fragment() {
16+
17+
lateinit var originalQuery: String
18+
lateinit var callback: Callback
19+
lateinit var etQuery: AppCompatEditText
20+
lateinit var btnApply: AppCompatButton
21+
lateinit var btnReset: AppCompatButton
22+
override fun onCreateView(
23+
inflater: LayoutInflater,
24+
container: ViewGroup?,
25+
savedInstanceState: Bundle?
26+
): View? {
27+
val view = inflater.inflate(R.layout.fragment_advance_query, container, false)
28+
originalQuery = arguments?.getString("query")!!
29+
setHasOptionsMenu(false)
30+
return view
31+
}
32+
33+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
34+
super.onViewCreated(view, savedInstanceState)
35+
etQuery = view.findViewById(R.id.et_query)
36+
btnApply = view.findViewById(R.id.btn_apply)
37+
btnReset = view.findViewById(R.id.btn_reset)
38+
39+
etQuery.setText(originalQuery)
40+
btnReset.setOnClickListener {
41+
btnReset.post {
42+
etQuery.setText(originalQuery)
43+
etQuery.clearFocus()
44+
hideKeyBoard()
45+
callback.reset()
46+
}
47+
}
48+
49+
btnApply.setOnClickListener {
50+
btnApply.post {
51+
etQuery.clearFocus()
52+
hideKeyBoard()
53+
callback.apply(etQuery.text.toString())
54+
callback.close()
55+
}
56+
}
57+
}
58+
59+
fun hideKeyBoard() {
60+
val inputMethodManager =
61+
context?.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager?
62+
inputMethodManager?.hideSoftInputFromWindow(view?.windowToken, 0)
63+
}
64+
65+
interface Callback {
66+
fun reset()
67+
fun apply(query: String)
68+
fun close()
69+
}
70+
}

0 commit comments

Comments
 (0)