mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 14:53:59 +01:00 
			
		
		
		
	Convert OkHttpJsonApiClient and CategoryApi to kotlin
This commit is contained in:
		
							parent
							
								
									33548fa57d
								
							
						
					
					
						commit
						79de03964e
					
				
					 7 changed files with 633 additions and 783 deletions
				
			
		|  | @ -52,12 +52,12 @@ class CampaignsPresenter @Inject constructor( | |||
|                 return | ||||
|             } | ||||
| 
 | ||||
|             okHttpJsonApiClient.campaigns | ||||
|             okHttpJsonApiClient.getCampaigns() | ||||
|                 .observeOn(mainThreadScheduler) | ||||
|                 .subscribeOn(ioScheduler) | ||||
|                 .doOnSubscribe { disposable = it } | ||||
|                 .subscribe({ campaignResponseDTO -> | ||||
|                     val campaigns = campaignResponseDTO.campaigns?.toMutableList() | ||||
|                     val campaigns = campaignResponseDTO?.campaigns?.toMutableList() | ||||
|                     if (campaigns.isNullOrEmpty()) { | ||||
|                         Timber.e("The campaigns list is empty") | ||||
|                         view!!.showCampaigns(null) | ||||
|  |  | |||
|  | @ -1,99 +0,0 @@ | |||
| package fr.free.nrw.commons.mwapi; | ||||
| 
 | ||||
| import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX; | ||||
| 
 | ||||
| import com.google.gson.Gson; | ||||
| import fr.free.nrw.commons.BuildConfig; | ||||
| import fr.free.nrw.commons.category.CategoryItem; | ||||
| import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage; | ||||
| import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse; | ||||
| import io.reactivex.Single; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.LinkedHashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import javax.inject.Inject; | ||||
| import okhttp3.HttpUrl; | ||||
| import okhttp3.OkHttpClient; | ||||
| import okhttp3.Request; | ||||
| import okhttp3.Response; | ||||
| import okhttp3.ResponseBody; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| /** | ||||
|  * Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates | ||||
|  * with nearby Commons categories. Parses the results using GSON to obtain a list of relevant | ||||
|  * categories.  Note: that caller is responsible for executing the request() method on a background | ||||
|  * thread. | ||||
|  */ | ||||
| public class CategoryApi { | ||||
| 
 | ||||
|     private final OkHttpClient okHttpClient; | ||||
|     private final Gson gson; | ||||
| 
 | ||||
|     @Inject | ||||
|     public CategoryApi(final OkHttpClient okHttpClient, final Gson gson) { | ||||
|         this.okHttpClient = okHttpClient; | ||||
|         this.gson = gson; | ||||
|     } | ||||
| 
 | ||||
|     public Single<List<CategoryItem>> request(String coords) { | ||||
|         return Single.fromCallable(() -> { | ||||
|             HttpUrl apiUrl = buildUrl(coords); | ||||
|             Timber.d("URL: %s", apiUrl.toString()); | ||||
| 
 | ||||
|             Request request = new Request.Builder().get().url(apiUrl).build(); | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             ResponseBody body = response.body(); | ||||
|             if (body == null) { | ||||
|                 return Collections.emptyList(); | ||||
|             } | ||||
| 
 | ||||
|             MwQueryResponse apiResponse = gson.fromJson(body.charStream(), MwQueryResponse.class); | ||||
|             Set<CategoryItem> categories = new LinkedHashSet<>(); | ||||
|             if (apiResponse != null && apiResponse.query() != null && apiResponse.query().pages() != null) { | ||||
|                 for (MwQueryPage page : apiResponse.query().pages()) { | ||||
|                     if (page.categories() != null) { | ||||
|                         for (MwQueryPage.Category category : page.categories()) { | ||||
|                             categories.add(new CategoryItem(category.title().replace(CATEGORY_PREFIX, ""), "", "", false)); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return new ArrayList<>(categories); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Builds URL with image coords for MediaWiki API calls | ||||
|      * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 | ||||
|      * | ||||
|      * @param coords Coordinates to build query with | ||||
|      * @return URL for API query | ||||
|      */ | ||||
|     private HttpUrl buildUrl(final String coords) { | ||||
|         return HttpUrl | ||||
|                 .parse(BuildConfig.WIKIMEDIA_API_HOST) | ||||
|                 .newBuilder() | ||||
|                 .addQueryParameter("action", "query") | ||||
|                 .addQueryParameter("prop", "categories|coordinates|pageprops") | ||||
|                 .addQueryParameter("format", "json") | ||||
|                 .addQueryParameter("clshow", "!hidden") | ||||
|                 .addQueryParameter("coprop", "type|name|dim|country|region|globe") | ||||
|                 .addQueryParameter("codistancefrompoint", coords) | ||||
|                 .addQueryParameter("generator", "geosearch") | ||||
|                 .addQueryParameter("ggscoord", coords) | ||||
|                 .addQueryParameter("ggsradius", "10000") | ||||
|                 .addQueryParameter("ggslimit", "10") | ||||
|                 .addQueryParameter("ggsnamespace", "6") | ||||
|                 .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") | ||||
|                 .addQueryParameter("ggsprimary", "all") | ||||
|                 .addQueryParameter("formatversion", "2") | ||||
|                 .build(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										83
									
								
								app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.kt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.kt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| package fr.free.nrw.commons.mwapi | ||||
| 
 | ||||
| import com.google.gson.Gson | ||||
| import fr.free.nrw.commons.BuildConfig | ||||
| import fr.free.nrw.commons.category.CATEGORY_PREFIX | ||||
| import fr.free.nrw.commons.category.CategoryItem | ||||
| import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse | ||||
| import io.reactivex.Single | ||||
| import okhttp3.HttpUrl | ||||
| import okhttp3.HttpUrl.Companion.toHttpUrlOrNull | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.Request | ||||
| import timber.log.Timber | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| /** | ||||
|  * Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates | ||||
|  * with nearby Commons categories. Parses the results using GSON to obtain a list of relevant | ||||
|  * categories.  Note: that caller is responsible for executing the request() method on a background | ||||
|  * thread. | ||||
|  */ | ||||
| class CategoryApi @Inject constructor( | ||||
|     private val okHttpClient: OkHttpClient, | ||||
|     private val gson: Gson | ||||
| ) { | ||||
|     private val apiUrl : HttpUrl by lazy { BuildConfig.WIKIMEDIA_API_HOST.toHttpUrlOrNull()!! } | ||||
| 
 | ||||
|     fun request(coords: String): Single<List<CategoryItem>> = Single.fromCallable { | ||||
|         val apiUrl = buildUrl(coords) | ||||
|         Timber.d("URL: %s", apiUrl.toString()) | ||||
| 
 | ||||
|         val request: Request = Request.Builder().get().url(apiUrl).build() | ||||
|         val response = okHttpClient.newCall(request).execute() | ||||
|         val body = response.body ?: return@fromCallable emptyList<CategoryItem>() | ||||
| 
 | ||||
|         val apiResponse = gson.fromJson(body.charStream(), MwQueryResponse::class.java) | ||||
|         val categories: MutableSet<CategoryItem> = mutableSetOf() | ||||
|         if (apiResponse?.query() != null && apiResponse.query()!!.pages() != null) { | ||||
|             for (page in apiResponse.query()!!.pages()!!) { | ||||
|                 if (page.categories() != null) { | ||||
|                     for (category in page.categories()!!) { | ||||
|                         categories.add( | ||||
|                             CategoryItem( | ||||
|                                 name = category.title().replace(CATEGORY_PREFIX, ""), | ||||
|                                 description = "", | ||||
|                                 thumbnail = "", | ||||
|                                 isSelected = false | ||||
|                             ) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         ArrayList<CategoryItem>(categories) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Builds URL with image coords for MediaWiki API calls | ||||
|      * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 | ||||
|      * | ||||
|      * @param coords Coordinates to build query with | ||||
|      * @return URL for API query | ||||
|      */ | ||||
|     private fun buildUrl(coords: String): HttpUrl = apiUrl.newBuilder() | ||||
|         .addQueryParameter("action", "query") | ||||
|         .addQueryParameter("prop", "categories|coordinates|pageprops") | ||||
|         .addQueryParameter("format", "json") | ||||
|         .addQueryParameter("clshow", "!hidden") | ||||
|         .addQueryParameter("coprop", "type|name|dim|country|region|globe") | ||||
|         .addQueryParameter("codistancefrompoint", coords) | ||||
|         .addQueryParameter("generator", "geosearch") | ||||
|         .addQueryParameter("ggscoord", coords) | ||||
|         .addQueryParameter("ggsradius", "10000") | ||||
|         .addQueryParameter("ggslimit", "10") | ||||
|         .addQueryParameter("ggsnamespace", "6") | ||||
|         .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") | ||||
|         .addQueryParameter("ggsprimary", "all") | ||||
|         .addQueryParameter("formatversion", "2") | ||||
|         .build() | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1,677 +0,0 @@ | |||
| package fr.free.nrw.commons.mwapi; | ||||
| 
 | ||||
| import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.LEADERBOARD_END_POINT; | ||||
| import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.UPDATE_AVATAR_END_POINT; | ||||
| 
 | ||||
| import android.text.TextUtils; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import com.google.gson.Gson; | ||||
| import fr.free.nrw.commons.campaigns.CampaignResponseDTO; | ||||
| import fr.free.nrw.commons.explore.depictions.DepictsClient; | ||||
| import fr.free.nrw.commons.location.LatLng; | ||||
| import fr.free.nrw.commons.nearby.Place; | ||||
| import fr.free.nrw.commons.nearby.model.ItemsClass; | ||||
| import fr.free.nrw.commons.nearby.model.NearbyResponse; | ||||
| import fr.free.nrw.commons.nearby.model.NearbyResultItem; | ||||
| import fr.free.nrw.commons.nearby.model.PlaceBindings; | ||||
| import fr.free.nrw.commons.profile.achievements.FeaturedImages; | ||||
| import fr.free.nrw.commons.profile.achievements.FeedbackResponse; | ||||
| import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse; | ||||
| import fr.free.nrw.commons.profile.leaderboard.UpdateAvatarResponse; | ||||
| import fr.free.nrw.commons.upload.FileUtils; | ||||
| import fr.free.nrw.commons.upload.structure.depictions.DepictedItem; | ||||
| import fr.free.nrw.commons.utils.ConfigUtils; | ||||
| import fr.free.nrw.commons.wikidata.model.GetWikidataEditCountResponse; | ||||
| import io.reactivex.Observable; | ||||
| import io.reactivex.Single; | ||||
| import java.io.IOException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| import java.util.regex.Matcher; | ||||
| import java.util.regex.Pattern; | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Singleton; | ||||
| import okhttp3.HttpUrl; | ||||
| import okhttp3.OkHttpClient; | ||||
| import okhttp3.Request; | ||||
| import okhttp3.Response; | ||||
| import okhttp3.ResponseBody; | ||||
| import org.jetbrains.annotations.NotNull; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| /** | ||||
|  * Test methods in ok http api client | ||||
|  */ | ||||
| @Singleton | ||||
| public class OkHttpJsonApiClient { | ||||
| 
 | ||||
|     private final OkHttpClient okHttpClient; | ||||
|     private final DepictsClient depictsClient; | ||||
|     private final HttpUrl wikiMediaToolforgeUrl; | ||||
|     private final String sparqlQueryUrl; | ||||
|     private final String campaignsUrl; | ||||
|     private final Gson gson; | ||||
| 
 | ||||
| 
 | ||||
|     @Inject | ||||
|     public OkHttpJsonApiClient(OkHttpClient okHttpClient, | ||||
|         DepictsClient depictsClient, | ||||
|         HttpUrl wikiMediaToolforgeUrl, | ||||
|         String sparqlQueryUrl, | ||||
|         String campaignsUrl, | ||||
|         Gson gson) { | ||||
|         this.okHttpClient = okHttpClient; | ||||
|         this.depictsClient = depictsClient; | ||||
|         this.wikiMediaToolforgeUrl = wikiMediaToolforgeUrl; | ||||
|         this.sparqlQueryUrl = sparqlQueryUrl; | ||||
|         this.campaignsUrl = campaignsUrl; | ||||
|         this.gson = gson; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * The method will gradually calls the leaderboard API and fetches the leaderboard | ||||
|      * | ||||
|      * @param userName username of leaderboard user | ||||
|      * @param duration duration for leaderboard | ||||
|      * @param category category for leaderboard | ||||
|      * @param limit    page size limit for list | ||||
|      * @param offset   offset for the list | ||||
|      * @return LeaderboardResponse object | ||||
|      */ | ||||
|     @NonNull | ||||
|     public Observable<LeaderboardResponse> getLeaderboard(String userName, String duration, | ||||
|         String category, String limit, String offset) { | ||||
|         final String fetchLeaderboardUrlTemplate = wikiMediaToolforgeUrl | ||||
|             + LEADERBOARD_END_POINT; | ||||
|         String url = String.format(Locale.ENGLISH, | ||||
|             fetchLeaderboardUrlTemplate, | ||||
|             userName, | ||||
|             duration, | ||||
|             category, | ||||
|             limit, | ||||
|             offset); | ||||
|         HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); | ||||
|         urlBuilder.addQueryParameter("user", userName); | ||||
|         urlBuilder.addQueryParameter("duration", duration); | ||||
|         urlBuilder.addQueryParameter("category", category); | ||||
|         urlBuilder.addQueryParameter("limit", limit); | ||||
|         urlBuilder.addQueryParameter("offset", offset); | ||||
|         Timber.i("Url %s", urlBuilder.toString()); | ||||
|         Request request = new Request.Builder() | ||||
|             .url(urlBuilder.toString()) | ||||
|             .build(); | ||||
|         return Observable.fromCallable(() -> { | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             if (response != null && response.body() != null && response.isSuccessful()) { | ||||
|                 String json = response.body().string(); | ||||
|                 if (json == null) { | ||||
|                     return new LeaderboardResponse(); | ||||
|                 } | ||||
|                 Timber.d("Response for leaderboard is %s", json); | ||||
|                 try { | ||||
|                     return gson.fromJson(json, LeaderboardResponse.class); | ||||
|                 } catch (Exception e) { | ||||
|                     return new LeaderboardResponse(); | ||||
|                 } | ||||
|             } | ||||
|             return new LeaderboardResponse(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This method will update the leaderboard user avatar | ||||
|      * | ||||
|      * @param username username to update | ||||
|      * @param avatar   url of the new avatar | ||||
|      * @return UpdateAvatarResponse object | ||||
|      */ | ||||
|     @NonNull | ||||
|     public Single<UpdateAvatarResponse> setAvatar(String username, String avatar) { | ||||
|         final String urlTemplate = wikiMediaToolforgeUrl | ||||
|             + UPDATE_AVATAR_END_POINT; | ||||
|         return Single.fromCallable(() -> { | ||||
|             String url = String.format(Locale.ENGLISH, | ||||
|                 urlTemplate, | ||||
|                 username, | ||||
|                 avatar); | ||||
|             HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); | ||||
|             urlBuilder.addQueryParameter("user", username); | ||||
|             urlBuilder.addQueryParameter("avatar", avatar); | ||||
|             Timber.i("Url %s", urlBuilder.toString()); | ||||
|             Request request = new Request.Builder() | ||||
|                 .url(urlBuilder.toString()) | ||||
|                 .build(); | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             if (response != null && response.body() != null && response.isSuccessful()) { | ||||
|                 String json = response.body().string(); | ||||
|                 if (json == null) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 try { | ||||
|                     return gson.fromJson(json, UpdateAvatarResponse.class); | ||||
|                 } catch (Exception e) { | ||||
|                     return new UpdateAvatarResponse(); | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public Single<Integer> getUploadCount(String userName) { | ||||
|         HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder(); | ||||
|         urlBuilder | ||||
|             .addPathSegments("uploadsbyuser.py") | ||||
|             .addQueryParameter("user", userName); | ||||
| 
 | ||||
|         if (ConfigUtils.isBetaFlavour()) { | ||||
|             urlBuilder.addQueryParameter("labs", "commonswiki"); | ||||
|         } | ||||
| 
 | ||||
|         Request request = new Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build(); | ||||
| 
 | ||||
|         return Single.fromCallable(() -> { | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             if (response != null && response.isSuccessful()) { | ||||
|                 ResponseBody responseBody = response.body(); | ||||
|                 if (null != responseBody) { | ||||
|                     String responseBodyString = responseBody.string().trim(); | ||||
|                     if (!TextUtils.isEmpty(responseBodyString)) { | ||||
|                         try { | ||||
|                             return Integer.parseInt(responseBodyString); | ||||
|                         } catch (NumberFormatException e) { | ||||
|                             Timber.e(e); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             return 0; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     public Single<Integer> getWikidataEdits(String userName) { | ||||
|         HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder(); | ||||
|         urlBuilder | ||||
|             .addPathSegments("wikidataedits.py") | ||||
|             .addQueryParameter("user", userName); | ||||
| 
 | ||||
|         if (ConfigUtils.isBetaFlavour()) { | ||||
|             urlBuilder.addQueryParameter("labs", "commonswiki"); | ||||
|         } | ||||
| 
 | ||||
|         Request request = new Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build(); | ||||
| 
 | ||||
|         return Single.fromCallable(() -> { | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             if (response != null && | ||||
|                 response.isSuccessful() && response.body() != null) { | ||||
|                 String json = response.body().string(); | ||||
|                 if (json == null) { | ||||
|                     return 0; | ||||
|                 } | ||||
|                 // Extract JSON from response | ||||
|                 json = json.substring(json.indexOf('{')); | ||||
|                 GetWikidataEditCountResponse countResponse = gson | ||||
|                     .fromJson(json, GetWikidataEditCountResponse.class); | ||||
|                 if (null != countResponse) { | ||||
|                     return countResponse.getWikidataEditCount(); | ||||
|                 } | ||||
|             } | ||||
|             return 0; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This takes userName as input, which is then used to fetch the feedback/achievements | ||||
|      * statistics using OkHttp and JavaRx. This function return JSONObject | ||||
|      * | ||||
|      * @param userName MediaWiki user name | ||||
|      * @return | ||||
|      */ | ||||
|     public Single<FeedbackResponse> getAchievements(String userName) { | ||||
|         final String fetchAchievementUrlTemplate = | ||||
|             wikiMediaToolforgeUrl + (ConfigUtils.isBetaFlavour() ? "/feedback.py?labs=commonswiki" | ||||
|                 : "/feedback.py"); | ||||
|         return Single.fromCallable(() -> { | ||||
|             String url = String.format( | ||||
|                 Locale.ENGLISH, | ||||
|                 fetchAchievementUrlTemplate, | ||||
|                 userName); | ||||
|             HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); | ||||
|             urlBuilder.addQueryParameter("user", userName); | ||||
|             Request request = new Request.Builder() | ||||
|                 .url(urlBuilder.toString()) | ||||
|                 .build(); | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             if (response != null && response.body() != null && response.isSuccessful()) { | ||||
|                 String json = response.body().string(); | ||||
|                 if (json == null) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 // Extract JSON from response | ||||
|                 json = json.substring(json.indexOf('{')); | ||||
|                 Timber.d("Response for achievements is %s", json); | ||||
|                 try { | ||||
|                     return gson.fromJson(json, FeedbackResponse.class); | ||||
|                 } catch (Exception e) { | ||||
|                     return new FeedbackResponse(0, 0, 0, new FeaturedImages(0, 0), 0, ""); | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|             } | ||||
|             return null; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make API Call to get Nearby Places | ||||
|      * | ||||
|      * @param cur      Search lat long | ||||
|      * @param language Language | ||||
|      * @param radius   Search Radius | ||||
|      * @return | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Nullable | ||||
|     public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius, | ||||
|         final String customQuery) | ||||
|         throws Exception { | ||||
| 
 | ||||
|         Timber.d("Fetching nearby items at radius %s", radius); | ||||
|         Timber.d("CUSTOM_SPARQL: %s", String.valueOf(customQuery != null)); | ||||
|         final String wikidataQuery; | ||||
|         if (customQuery != null) { | ||||
|             wikidataQuery = customQuery; | ||||
|         } else { | ||||
|             wikidataQuery = FileUtils.readFromResource( | ||||
|                 "/queries/radius_query_for_upload_wizard.rq"); | ||||
|         } | ||||
|         final String query = wikidataQuery | ||||
|             .replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius)) | ||||
|             .replace("${LAT}", String.format(Locale.ROOT, "%.4f", cur.getLatitude())) | ||||
|             .replace("${LONG}", String.format(Locale.ROOT, "%.4f", cur.getLongitude())) | ||||
|             .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(); | ||||
|         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(); | ||||
|         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); | ||||
|                 if (shouldQueryForMonuments && item.getMonument() != null) { | ||||
|                     placeFromNearbyItem.setMonument(true); | ||||
|                 } else { | ||||
|                     placeFromNearbyItem.setMonument(false); | ||||
|                 } | ||||
|                 places.add(placeFromNearbyItem); | ||||
|             } | ||||
|             return places; | ||||
|         } | ||||
|         throw new Exception(response.message()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves a list of places based on the provided list of places and language. | ||||
|      * | ||||
|      * @param placeList A list of Place objects for which to fetch information. | ||||
|      * @param language  The language code to use for the query. | ||||
|      * @return A list of Place objects with additional information retrieved from Wikidata, or null | ||||
|      * if an error occurs. | ||||
|      * @throws IOException If there is an issue with reading the resource file or executing the HTTP | ||||
|      *                     request. | ||||
|      */ | ||||
|     @Nullable | ||||
|     public List<Place> getPlaces( | ||||
|         final List<Place> placeList, final String language) throws IOException { | ||||
|         final String wikidataQuery = FileUtils.readFromResource("/queries/query_for_item.rq"); | ||||
|         String qids = ""; | ||||
|         for (final Place place : placeList) { | ||||
|             qids += "\n" + ("wd:" + place.getWikiDataEntityId()); | ||||
|         } | ||||
|         final String query = wikidataQuery | ||||
|             .replace("${ENTITY}", qids) | ||||
|             .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(); | ||||
| 
 | ||||
|         try (Response response = okHttpClient.newCall(request).execute()) { | ||||
|             if (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); | ||||
|                     places.add(placeFromNearbyItem); | ||||
|                 } | ||||
|                 return places; | ||||
|             } else { | ||||
|                 throw new IOException("Unexpected response code: " + response.code()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make API Call to get Places | ||||
|      * | ||||
|      * @param leftLatLng  Left lat long | ||||
|      * @param rightLatLng Right lat long | ||||
|      * @return | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Nullable | ||||
|     public String getPlacesAsKML(final LatLng leftLatLng, final LatLng rightLatLng) | ||||
|         throws Exception { | ||||
|         String kmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + | ||||
|             "<!--Created by Wikimedia Commons Android app -->\n" + | ||||
|             "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" + | ||||
|             "    <Document>"; | ||||
|         List<PlaceBindings> placeBindings = runQuery(leftLatLng, | ||||
|             rightLatLng); | ||||
|         if (placeBindings != null) { | ||||
|             for (PlaceBindings item : placeBindings) { | ||||
|                 if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) { | ||||
|                     String input = item.getLocation().getValue(); | ||||
|                     Pattern pattern = Pattern.compile( | ||||
|                         "Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)"); | ||||
|                     Matcher matcher = pattern.matcher(input); | ||||
| 
 | ||||
|                     if (matcher.find()) { | ||||
|                         String longStr = matcher.group(1); | ||||
|                         String latStr = matcher.group(2); | ||||
|                         String itemUrl = item.getItem().getValue(); | ||||
|                         String itemName = item.getLabel().getValue().replace("&", "&"); | ||||
|                         String itemLatitude = latStr; | ||||
|                         String itemLongitude = longStr; | ||||
|                         String itemClass = item.getClas().getValue(); | ||||
| 
 | ||||
|                         String formattedItemName = | ||||
|                             !itemClass.isEmpty() ? itemName + " (" + itemClass + ")" | ||||
|                                 : itemName; | ||||
| 
 | ||||
|                         String kmlEntry = "\n        <Placemark>\n" + | ||||
|                             "            <name>" + formattedItemName + "</name>\n" + | ||||
|                             "            <description>" + itemUrl + "</description>\n" + | ||||
|                             "            <Point>\n" + | ||||
|                             "                <coordinates>" + itemLongitude + "," | ||||
|                             + itemLatitude | ||||
|                             + "</coordinates>\n" + | ||||
|                             "            </Point>\n" + | ||||
|                             "        </Placemark>"; | ||||
|                         kmlString = kmlString + kmlEntry; | ||||
|                     } else { | ||||
|                         Timber.e("No match found"); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         kmlString = kmlString + "\n    </Document>\n" + | ||||
|             "</kml>\n"; | ||||
|         return kmlString; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make API Call to get Places | ||||
|      * | ||||
|      * @param leftLatLng  Left lat long | ||||
|      * @param rightLatLng Right lat long | ||||
|      * @return | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Nullable | ||||
|     public String getPlacesAsGPX(final LatLng leftLatLng, final LatLng rightLatLng) | ||||
|         throws Exception { | ||||
|         String gpxString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + | ||||
|             "<gpx\n" + | ||||
|             " version=\"1.0\"\n" + | ||||
|             " creator=\"Wikimedia Commons Android app\"\n" + | ||||
|             " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" + | ||||
|             " xmlns=\"http://www.topografix.com/GPX/1/0\"\n" + | ||||
|             " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">" | ||||
|             + "\n<bounds minlat=\"$MIN_LATITUDE\" minlon=\"$MIN_LONGITUDE\" maxlat=\"$MAX_LATITUDE\" maxlon=\"$MAX_LONGITUDE\"/>"; | ||||
| 
 | ||||
|         List<PlaceBindings> placeBindings = runQuery(leftLatLng, rightLatLng); | ||||
|         if (placeBindings != null) { | ||||
|             for (PlaceBindings item : placeBindings) { | ||||
|                 if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) { | ||||
|                     String input = item.getLocation().getValue(); | ||||
|                     Pattern pattern = Pattern.compile( | ||||
|                         "Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)"); | ||||
|                     Matcher matcher = pattern.matcher(input); | ||||
| 
 | ||||
|                     if (matcher.find()) { | ||||
|                         String longStr = matcher.group(1); | ||||
|                         String latStr = matcher.group(2); | ||||
|                         String itemUrl = item.getItem().getValue(); | ||||
|                         String itemName = item.getLabel().getValue().replace("&", "&"); | ||||
|                         String itemLatitude = latStr; | ||||
|                         String itemLongitude = longStr; | ||||
|                         String itemClass = item.getClas().getValue(); | ||||
| 
 | ||||
|                         String formattedItemName = | ||||
|                             !itemClass.isEmpty() ? itemName + " (" + itemClass + ")" | ||||
|                                 : itemName; | ||||
| 
 | ||||
|                         String gpxEntry = | ||||
|                             "\n    <wpt lat=\"" + itemLatitude + "\" lon=\"" + itemLongitude | ||||
|                                 + "\">\n" + | ||||
|                                 "        <name>" + itemName + "</name>\n" + | ||||
|                                 "        <url>" + itemUrl + "</url>\n" + | ||||
|                                 "    </wpt>"; | ||||
|                         gpxString = gpxString + gpxEntry; | ||||
| 
 | ||||
|                     } else { | ||||
|                         Timber.e("No match found"); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|         gpxString = gpxString + "\n</gpx>"; | ||||
|         return gpxString; | ||||
|     } | ||||
| 
 | ||||
|     private List<PlaceBindings> runQuery(final LatLng currentLatLng, final LatLng nextLatLng) | ||||
|         throws IOException { | ||||
| 
 | ||||
|         final String wikidataQuery = FileUtils.readFromResource("/queries/places_query.rq"); | ||||
|         final String query = wikidataQuery | ||||
|             .replace("${LONGITUDE}", | ||||
|                 String.format(Locale.ROOT, "%.2f", currentLatLng.getLongitude())) | ||||
|             .replace("${LATITUDE}", String.format(Locale.ROOT, "%.4f", currentLatLng.getLatitude())) | ||||
|             .replace("${NEXT_LONGITUDE}", | ||||
|                 String.format(Locale.ROOT, "%.4f", nextLatLng.getLongitude())) | ||||
|             .replace("${NEXT_LATITUDE}", | ||||
|                 String.format(Locale.ROOT, "%.4f", nextLatLng.getLatitude())); | ||||
| 
 | ||||
|         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(); | ||||
|         if (response.body() != null && response.isSuccessful()) { | ||||
|             final String json = response.body().string(); | ||||
|             final ItemsClass item = gson.fromJson(json, ItemsClass.class); | ||||
|             return item.getResults().getBindings(); | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Make API Call to get Nearby Places Implementation does not expects a custom query | ||||
|      * | ||||
|      * @param cur      Search lat long | ||||
|      * @param language Language | ||||
|      * @param radius   Search Radius | ||||
|      * @return | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Nullable | ||||
|     public List<Place> getNearbyPlaces(final LatLng cur, final String language, final double radius) | ||||
|         throws Exception { | ||||
|         return getNearbyPlaces(cur, language, radius, null); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example: | ||||
|      * bridge -> suspended bridge, aqueduct, etc | ||||
|      */ | ||||
|     public Single<List<DepictedItem>> getChildDepictions(String qid, int startPosition, | ||||
|         int limit) throws IOException { | ||||
|         return depictedItemsFrom( | ||||
|             sparqlQuery(qid, startPosition, limit, "/queries/subclasses_query.rq")); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example: | ||||
|      * bridge -> suspended bridge, aqueduct, etc | ||||
|      */ | ||||
|     public Single<List<DepictedItem>> getParentDepictions(String qid, int startPosition, | ||||
|         int limit) throws IOException { | ||||
|         return depictedItemsFrom(sparqlQuery(qid, startPosition, limit, | ||||
|             "/queries/parentclasses_query.rq")); | ||||
|     } | ||||
| 
 | ||||
|     private Single<List<DepictedItem>> depictedItemsFrom(Request request) { | ||||
|         return depictsClient.toDepictions(Single.fromCallable(() -> { | ||||
|             try (ResponseBody body = okHttpClient.newCall(request).execute().body()) { | ||||
|                 return gson.fromJson(body.string(), SparqlResponse.class); | ||||
|             } | ||||
|         }).doOnError(Timber::e)); | ||||
|     } | ||||
| 
 | ||||
|     @NotNull | ||||
|     private Request sparqlQuery(String qid, int startPosition, int limit, String fileName) | ||||
|         throws IOException { | ||||
|         String query = FileUtils.readFromResource(fileName) | ||||
|             .replace("${QID}", qid) | ||||
|             .replace("${LANG}", "\"" + Locale.getDefault().getLanguage() + "\"") | ||||
|             .replace("${LIMIT}", "" + limit) | ||||
|             .replace("${OFFSET}", "" + startPosition); | ||||
|         HttpUrl.Builder urlBuilder = HttpUrl | ||||
|             .parse(sparqlQueryUrl) | ||||
|             .newBuilder() | ||||
|             .addQueryParameter("query", query) | ||||
|             .addQueryParameter("format", "json"); | ||||
|         return new Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build(); | ||||
|     } | ||||
| 
 | ||||
|     public Single<CampaignResponseDTO> getCampaigns() { | ||||
|         return Single.fromCallable(() -> { | ||||
|             Request request = new Request.Builder().url(campaignsUrl) | ||||
|                 .build(); | ||||
|             Response response = okHttpClient.newCall(request).execute(); | ||||
|             if (response != null && response.body() != null && response.isSuccessful()) { | ||||
|                 String json = response.body().string(); | ||||
|                 if (json == null) { | ||||
|                     return null; | ||||
|                 } | ||||
|                 return gson.fromJson(json, CampaignResponseDTO.class); | ||||
|             } | ||||
|             return null; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,543 @@ | |||
| package fr.free.nrw.commons.mwapi | ||||
| 
 | ||||
| import android.text.TextUtils | ||||
| import com.google.gson.Gson | ||||
| import fr.free.nrw.commons.campaigns.CampaignResponseDTO | ||||
| import fr.free.nrw.commons.explore.depictions.DepictsClient | ||||
| import fr.free.nrw.commons.location.LatLng | ||||
| import fr.free.nrw.commons.nearby.Place | ||||
| import fr.free.nrw.commons.nearby.model.ItemsClass | ||||
| import fr.free.nrw.commons.nearby.model.NearbyResponse | ||||
| import fr.free.nrw.commons.nearby.model.PlaceBindings | ||||
| import fr.free.nrw.commons.profile.achievements.FeaturedImages | ||||
| import fr.free.nrw.commons.profile.achievements.FeedbackResponse | ||||
| import fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants | ||||
| import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse | ||||
| import fr.free.nrw.commons.profile.leaderboard.UpdateAvatarResponse | ||||
| import fr.free.nrw.commons.upload.FileUtils | ||||
| import fr.free.nrw.commons.upload.structure.depictions.DepictedItem | ||||
| import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour | ||||
| import fr.free.nrw.commons.wikidata.model.GetWikidataEditCountResponse | ||||
| import io.reactivex.Observable | ||||
| import io.reactivex.Single | ||||
| import okhttp3.HttpUrl | ||||
| import okhttp3.HttpUrl.Companion.toHttpUrlOrNull | ||||
| import okhttp3.OkHttpClient | ||||
| import okhttp3.Request | ||||
| import okhttp3.Response | ||||
| import timber.log.Timber | ||||
| import java.io.IOException | ||||
| import java.util.Locale | ||||
| import java.util.regex.Pattern | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
| 
 | ||||
| /** | ||||
|  * Test methods in ok http api client | ||||
|  */ | ||||
| @Singleton | ||||
| class OkHttpJsonApiClient @Inject constructor( | ||||
|     private val okHttpClient: OkHttpClient, | ||||
|     private val depictsClient: DepictsClient, | ||||
|     private val wikiMediaToolforgeUrl: HttpUrl, | ||||
|     private val sparqlQueryUrl: String, | ||||
|     private val campaignsUrl: String, | ||||
|     private val gson: Gson | ||||
| ) { | ||||
|     fun getLeaderboard( | ||||
|         userName: String, duration: String?, | ||||
|         category: String?, limit: String?, offset: String? | ||||
|     ): Observable<LeaderboardResponse> { | ||||
|         val fetchLeaderboardUrlTemplate = | ||||
|             wikiMediaToolforgeUrl.toString() + LeaderboardConstants.LEADERBOARD_END_POINT | ||||
|         val url = String.format(Locale.ENGLISH, | ||||
|             fetchLeaderboardUrlTemplate, userName, duration, category, limit, offset) | ||||
|         val urlBuilder: HttpUrl.Builder = url.toHttpUrlOrNull()!!.newBuilder() | ||||
|             .addQueryParameter("user", userName) | ||||
|             .addQueryParameter("duration", duration) | ||||
|             .addQueryParameter("category", category) | ||||
|             .addQueryParameter("limit", limit) | ||||
|             .addQueryParameter("offset", offset) | ||||
|         Timber.i("Url %s", urlBuilder.toString()) | ||||
|         val request: Request = Request.Builder() | ||||
|             .url(urlBuilder.toString()) | ||||
|             .build() | ||||
|         return Observable.fromCallable({ | ||||
|             val response: Response = okHttpClient.newCall(request).execute() | ||||
|             if (response.body != null && response.isSuccessful) { | ||||
|                 val json: String = response.body!!.string() | ||||
|                 Timber.d("Response for leaderboard is %s", json) | ||||
|                 try { | ||||
|                     return@fromCallable gson.fromJson<LeaderboardResponse>( | ||||
|                         json, | ||||
|                         LeaderboardResponse::class.java | ||||
|                     ) | ||||
|                 } catch (e: Exception) { | ||||
|                     return@fromCallable LeaderboardResponse() | ||||
|                 } | ||||
|             } | ||||
|             LeaderboardResponse() | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fun setAvatar(username: String, avatar: String?): Single<UpdateAvatarResponse?> { | ||||
|         val urlTemplate = wikiMediaToolforgeUrl | ||||
|             .toString() + LeaderboardConstants.UPDATE_AVATAR_END_POINT | ||||
|         return Single.fromCallable<UpdateAvatarResponse?>({ | ||||
|             val url = String.format(Locale.ENGLISH, urlTemplate, username, avatar) | ||||
|             val urlBuilder: HttpUrl.Builder = url.toHttpUrlOrNull()!!.newBuilder() | ||||
|                 .addQueryParameter("user", username) | ||||
|                 .addQueryParameter("avatar", avatar) | ||||
|             Timber.i("Url %s", urlBuilder.toString()) | ||||
|             val request: Request = Request.Builder() | ||||
|                 .url(urlBuilder.toString()) | ||||
|                 .build() | ||||
|             val response: Response = okHttpClient.newCall(request).execute() | ||||
|             if (response.body != null && response.isSuccessful) { | ||||
|                 val json: String = response.body!!.string() ?: return@fromCallable null | ||||
|                 try { | ||||
|                     return@fromCallable gson.fromJson<UpdateAvatarResponse>( | ||||
|                         json, | ||||
|                         UpdateAvatarResponse::class.java | ||||
|                     ) | ||||
|                 } catch (e: Exception) { | ||||
|                     return@fromCallable UpdateAvatarResponse() | ||||
|                 } | ||||
|             } | ||||
|             null | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fun getUploadCount(userName: String?): Single<Int> { | ||||
|         val urlBuilder: HttpUrl.Builder = wikiMediaToolforgeUrl.newBuilder() | ||||
|             .addPathSegments("uploadsbyuser.py") | ||||
|             .addQueryParameter("user", userName) | ||||
| 
 | ||||
|         if (isBetaFlavour) { | ||||
|             urlBuilder.addQueryParameter("labs", "commonswiki") | ||||
|         } | ||||
| 
 | ||||
|         val request: Request = Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build() | ||||
| 
 | ||||
|         return Single.fromCallable<Int>({ | ||||
|             val response: Response = okHttpClient.newCall(request).execute() | ||||
|             if (response != null && response.isSuccessful) { | ||||
|                 val responseBody = response.body | ||||
|                 if (null != responseBody) { | ||||
|                     val responseBodyString = responseBody.string().trim { it <= ' ' } | ||||
|                     if (!TextUtils.isEmpty(responseBodyString)) { | ||||
|                         try { | ||||
|                             return@fromCallable responseBodyString.toInt() | ||||
|                         } catch (e: NumberFormatException) { | ||||
|                             Timber.e(e) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             0 | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fun getWikidataEdits(userName: String?): Single<Int> { | ||||
|         val urlBuilder: HttpUrl.Builder = wikiMediaToolforgeUrl.newBuilder() | ||||
|             .addPathSegments("wikidataedits.py") | ||||
|             .addQueryParameter("user", userName) | ||||
| 
 | ||||
|         if (isBetaFlavour) { | ||||
|             urlBuilder.addQueryParameter("labs", "commonswiki") | ||||
|         } | ||||
| 
 | ||||
|         val request: Request = Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build() | ||||
| 
 | ||||
|         return Single.fromCallable<Int>({ | ||||
|             val response: Response = okHttpClient.newCall(request).execute() | ||||
|             if (response != null && response.isSuccessful && response.body != null) { | ||||
|                 var json: String = response.body!!.string() | ||||
|                 // Extract JSON from response | ||||
|                 json = json.substring(json.indexOf('{')) | ||||
|                 val countResponse = gson | ||||
|                     .fromJson( | ||||
|                         json, | ||||
|                         GetWikidataEditCountResponse::class.java | ||||
|                     ) | ||||
|                 if (null != countResponse) { | ||||
|                     return@fromCallable countResponse.wikidataEditCount | ||||
|                 } | ||||
|             } | ||||
|             0 | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fun getAchievements(userName: String?): Single<FeedbackResponse?> { | ||||
|         val suffix = if (isBetaFlavour) "/feedback.py?labs=commonswiki" else "/feedback.py" | ||||
|         val fetchAchievementUrlTemplate = wikiMediaToolforgeUrl.toString() + suffix | ||||
|         return Single.fromCallable<FeedbackResponse?>({ | ||||
|             val url = String.format( | ||||
|                 Locale.ENGLISH, | ||||
|                 fetchAchievementUrlTemplate, | ||||
|                 userName | ||||
|             ) | ||||
|             val urlBuilder: HttpUrl.Builder = url.toHttpUrlOrNull()!!.newBuilder() | ||||
|                 .addQueryParameter("user", userName) | ||||
|             val request: Request = Request.Builder() | ||||
|                 .url(urlBuilder.toString()) | ||||
|                 .build() | ||||
|             val response: Response = okHttpClient.newCall(request).execute() | ||||
|             if (response.body != null && response.isSuccessful) { | ||||
|                 var json: String = response.body!!.string() | ||||
|                 // Extract JSON from response | ||||
|                 json = json.substring(json.indexOf('{')) | ||||
|                 Timber.d("Response for achievements is %s", json) | ||||
|                 try { | ||||
|                     return@fromCallable gson.fromJson<FeedbackResponse>( | ||||
|                         json, | ||||
|                         FeedbackResponse::class.java | ||||
|                     ) | ||||
|                 } catch (e: Exception) { | ||||
|                     return@fromCallable FeedbackResponse(0, 0, 0, FeaturedImages(0, 0), 0, "") | ||||
|                 } | ||||
|             } | ||||
|             null | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     @JvmOverloads | ||||
|     @Throws(Exception::class) | ||||
|     fun getNearbyPlaces( | ||||
|         cur: LatLng, language: String, radius: Double, | ||||
|         customQuery: String? = null | ||||
|     ): List<Place>? { | ||||
|         Timber.d("Fetching nearby items at radius %s", radius) | ||||
|         Timber.d("CUSTOM_SPARQL: %s", (customQuery != null).toString()) | ||||
|         val wikidataQuery: String = if (customQuery != null) { | ||||
|             customQuery | ||||
|         } else { | ||||
|             FileUtils.readFromResource("/queries/radius_query_for_upload_wizard.rq") | ||||
|         } | ||||
|         val query = wikidataQuery | ||||
|             .replace("\${RAD}", String.format(Locale.ROOT, "%.2f", radius)) | ||||
|             .replace("\${LAT}", String.format(Locale.ROOT, "%.4f", cur.latitude)) | ||||
|             .replace("\${LONG}", String.format(Locale.ROOT, "%.4f", cur.longitude)) | ||||
|             .replace("\${LANG}", language) | ||||
| 
 | ||||
|         val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!! | ||||
|             .newBuilder() | ||||
|             .addQueryParameter("query", query) | ||||
|             .addQueryParameter("format", "json") | ||||
| 
 | ||||
|         val request: Request = Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build() | ||||
| 
 | ||||
|         val response = okHttpClient.newCall(request).execute() | ||||
|         if (response.body != null && response.isSuccessful) { | ||||
|             val json = response.body!!.string() | ||||
|             val nearbyResponse = gson.fromJson(json, NearbyResponse::class.java) | ||||
|             val bindings = nearbyResponse.results.bindings | ||||
|             val places: MutableList<Place> = ArrayList() | ||||
|             for (item in bindings) { | ||||
|                 val placeFromNearbyItem = Place.from(item) | ||||
|                 placeFromNearbyItem.isMonument = false | ||||
|                 places.add(placeFromNearbyItem) | ||||
|             } | ||||
|             return places | ||||
|         } | ||||
|         throw Exception(response.message) | ||||
|     } | ||||
| 
 | ||||
|     @Throws(Exception::class) | ||||
|     fun getNearbyPlaces( | ||||
|         screenTopRight: LatLng, | ||||
|         screenBottomLeft: LatLng, language: String, | ||||
|         shouldQueryForMonuments: Boolean, customQuery: String? | ||||
|     ): List<Place>? { | ||||
|         Timber.d("CUSTOM_SPARQL: %s", (customQuery != null).toString()) | ||||
| 
 | ||||
|         val wikidataQuery: String = if (customQuery != null) { | ||||
|             customQuery | ||||
|         } else if (!shouldQueryForMonuments) { | ||||
|             FileUtils.readFromResource("/queries/rectangle_query_for_nearby.rq") | ||||
|         } else { | ||||
|             FileUtils.readFromResource("/queries/rectangle_query_for_nearby_monuments.rq") | ||||
|         } | ||||
| 
 | ||||
|         val westCornerLat = screenTopRight.latitude | ||||
|         val westCornerLong = screenTopRight.longitude | ||||
|         val eastCornerLat = screenBottomLeft.latitude | ||||
|         val eastCornerLong = screenBottomLeft.longitude | ||||
| 
 | ||||
|         val 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) | ||||
|         val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!! | ||||
|             .newBuilder() | ||||
|             .addQueryParameter("query", query) | ||||
|             .addQueryParameter("format", "json") | ||||
| 
 | ||||
|         val request: Request = Request.Builder() | ||||
|             .url(urlBuilder.build()) | ||||
|             .build() | ||||
| 
 | ||||
|         val response = okHttpClient.newCall(request).execute() | ||||
|         if (response.body != null && response.isSuccessful) { | ||||
|             val json = response.body!!.string() | ||||
|             val nearbyResponse = gson.fromJson(json, NearbyResponse::class.java) | ||||
|             val bindings = nearbyResponse.results.bindings | ||||
|             val places: MutableList<Place> = ArrayList() | ||||
|             for (item in bindings) { | ||||
|                 val placeFromNearbyItem = Place.from(item) | ||||
|                 if (shouldQueryForMonuments && item.getMonument() != null) { | ||||
|                     placeFromNearbyItem.isMonument = true | ||||
|                 } else { | ||||
|                     placeFromNearbyItem.isMonument = false | ||||
|                 } | ||||
|                 places.add(placeFromNearbyItem) | ||||
|             } | ||||
|             return places | ||||
|         } | ||||
|         throw Exception(response.message) | ||||
|     } | ||||
| 
 | ||||
|     @Throws(IOException::class) | ||||
|     fun getPlaces( | ||||
|         placeList: List<Place>, language: String | ||||
|     ): List<Place>? { | ||||
|         val wikidataQuery = FileUtils.readFromResource("/queries/query_for_item.rq") | ||||
|         var qids = "" | ||||
|         for (place in placeList) { | ||||
|             qids += """ | ||||
| ${"wd:" + place.wikiDataEntityId}""" | ||||
|         } | ||||
|         val query = wikidataQuery | ||||
|             .replace("\${ENTITY}", qids) | ||||
|             .replace("\${LANG}", language) | ||||
|         val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!! | ||||
|             .newBuilder() | ||||
|             .addQueryParameter("query", query) | ||||
|             .addQueryParameter("format", "json") | ||||
| 
 | ||||
|         val request: Request = Request.Builder().url(urlBuilder.build()).build() | ||||
| 
 | ||||
|         okHttpClient.newCall(request).execute().use { response -> | ||||
|             if (response.isSuccessful) { | ||||
|                 val json = response.body!!.string() | ||||
|                 val nearbyResponse = gson.fromJson(json, NearbyResponse::class.java) | ||||
|                 val bindings = nearbyResponse.results.bindings | ||||
|                 val places: MutableList<Place> = ArrayList() | ||||
|                 for (item in bindings) { | ||||
|                     val placeFromNearbyItem = Place.from(item) | ||||
|                     places.add(placeFromNearbyItem) | ||||
|                 } | ||||
|                 return places | ||||
|             } else { | ||||
|                 throw IOException("Unexpected response code: " + response.code) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Throws(Exception::class) | ||||
|     fun getPlacesAsKML(leftLatLng: LatLng, rightLatLng: LatLng): String? { | ||||
|         var kmlString = """<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!--Created by Wikimedia Commons Android app --> | ||||
| <kml xmlns="http://www.opengis.net/kml/2.2"> | ||||
|     <Document>""" | ||||
|         val placeBindings = runQuery( | ||||
|             leftLatLng, | ||||
|             rightLatLng | ||||
|         ) | ||||
|         if (placeBindings != null) { | ||||
|             for ((item1, label, location, clas) in placeBindings) { | ||||
|                 if (item1 != null && label != null && clas != null) { | ||||
|                     val input = location.value | ||||
|                     val pattern = Pattern.compile( | ||||
|                         "Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)" | ||||
|                     ) | ||||
|                     val matcher = pattern.matcher(input) | ||||
| 
 | ||||
|                     if (matcher.find()) { | ||||
|                         val longStr = matcher.group(1) | ||||
|                         val latStr = matcher.group(2) | ||||
|                         val itemUrl = item1.value | ||||
|                         val itemName = label.value.replace("&", "&") | ||||
|                         val itemLatitude = latStr | ||||
|                         val itemLongitude = longStr | ||||
|                         val itemClass = clas.value | ||||
| 
 | ||||
|                         val formattedItemName = | ||||
|                             if (!itemClass.isEmpty()) | ||||
|                                 "$itemName ($itemClass)" | ||||
|                             else | ||||
|                                 itemName | ||||
| 
 | ||||
|                         val kmlEntry = (""" | ||||
|         <Placemark> | ||||
|             <name>$formattedItemName</name> | ||||
|             <description>$itemUrl</description> | ||||
|             <Point> | ||||
|                 <coordinates>$itemLongitude,$itemLatitude</coordinates> | ||||
|             </Point> | ||||
|         </Placemark>""") | ||||
|                         kmlString = kmlString + kmlEntry | ||||
|                     } else { | ||||
|                         Timber.e("No match found") | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         kmlString = """$kmlString | ||||
|     </Document> | ||||
| </kml> | ||||
| """ | ||||
|         return kmlString | ||||
|     } | ||||
| 
 | ||||
|     @Throws(Exception::class) | ||||
|     fun getPlacesAsGPX(leftLatLng: LatLng, rightLatLng: LatLng): String? { | ||||
|         var gpxString = ("""<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <gpx | ||||
|  version="1.0" | ||||
|  creator="Wikimedia Commons Android app" | ||||
|  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|  xmlns="http://www.topografix.com/GPX/1/0" | ||||
|  xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd"> | ||||
| <bounds minlat="${"$"}MIN_LATITUDE" minlon="${"$"}MIN_LONGITUDE" maxlat="${"$"}MAX_LATITUDE" maxlon="${"$"}MAX_LONGITUDE"/>""") | ||||
| 
 | ||||
|         val placeBindings = runQuery(leftLatLng, rightLatLng) | ||||
|         if (placeBindings != null) { | ||||
|             for ((item1, label, location, clas) in placeBindings) { | ||||
|                 if (item1 != null && label != null && clas != null) { | ||||
|                     val input = location.value | ||||
|                     val pattern = Pattern.compile( | ||||
|                         "Point\\(([-+]?[0-9]*\\.?[0-9]+) ([-+]?[0-9]*\\.?[0-9]+)\\)" | ||||
|                     ) | ||||
|                     val matcher = pattern.matcher(input) | ||||
| 
 | ||||
|                     if (matcher.find()) { | ||||
|                         val longStr = matcher.group(1) | ||||
|                         val latStr = matcher.group(2) | ||||
|                         val itemUrl = item1.value | ||||
|                         val itemName = label.value.replace("&", "&") | ||||
|                         val itemLatitude = latStr | ||||
|                         val itemLongitude = longStr | ||||
|                         val itemClass = clas.value | ||||
| 
 | ||||
|                         val formattedItemName = if (!itemClass.isEmpty()) | ||||
|                             "$itemName ($itemClass)" | ||||
|                         else | ||||
|                             itemName | ||||
| 
 | ||||
|                         val gpxEntry = | ||||
|                             (""" | ||||
|     <wpt lat="$itemLatitude" lon="$itemLongitude"> | ||||
|         <name>$itemName</name> | ||||
|         <url>$itemUrl</url> | ||||
|     </wpt>""") | ||||
|                         gpxString = gpxString + gpxEntry | ||||
|                     } else { | ||||
|                         Timber.e("No match found") | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         gpxString = "$gpxString\n</gpx>" | ||||
|         return gpxString | ||||
|     } | ||||
| 
 | ||||
|     @Throws(IOException::class) | ||||
|     fun getChildDepictions( | ||||
|         qid: String, startPosition: Int, | ||||
|         limit: Int | ||||
|     ): Single<List<DepictedItem>> = | ||||
|         depictedItemsFrom(sparqlQuery(qid, startPosition, limit, "/queries/subclasses_query.rq")) | ||||
| 
 | ||||
|     @Throws(IOException::class) | ||||
|     fun getParentDepictions( | ||||
|         qid: String, startPosition: Int, | ||||
|         limit: Int | ||||
|     ): Single<List<DepictedItem>> = depictedItemsFrom( | ||||
|         sparqlQuery( | ||||
|             qid, | ||||
|             startPosition, | ||||
|             limit, | ||||
|             "/queries/parentclasses_query.rq" | ||||
|         ) | ||||
|     ) | ||||
| 
 | ||||
|     fun getCampaigns(): Single<CampaignResponseDTO> { | ||||
|         return Single.fromCallable<CampaignResponseDTO?>({ | ||||
|             val request: Request = Request.Builder().url(campaignsUrl).build() | ||||
|             val response: Response = okHttpClient.newCall(request).execute() | ||||
|             if (response.body != null && response.isSuccessful) { | ||||
|                 val json: String = response.body!!.string() | ||||
|                 return@fromCallable gson.fromJson<CampaignResponseDTO>( | ||||
|                     json, | ||||
|                     CampaignResponseDTO::class.java | ||||
|                 ) | ||||
|             } | ||||
|             null | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private fun depictedItemsFrom(request: Request): Single<List<DepictedItem>> { | ||||
|         return depictsClient.toDepictions(Single.fromCallable({ | ||||
|             okHttpClient.newCall(request).execute().body.use { body -> | ||||
|                 return@fromCallable gson.fromJson<SparqlResponse>( | ||||
|                     body!!.string(), | ||||
|                     SparqlResponse::class.java | ||||
|                 ) | ||||
|             } | ||||
|         }).doOnError({ t: Throwable? -> Timber.e(t) })) | ||||
|     } | ||||
| 
 | ||||
|     @Throws(IOException::class) | ||||
|     private fun sparqlQuery( | ||||
|         qid: String, | ||||
|         startPosition: Int, | ||||
|         limit: Int, | ||||
|         fileName: String | ||||
|     ): Request { | ||||
|         val query = FileUtils.readFromResource(fileName) | ||||
|             .replace("\${QID}", qid) | ||||
|             .replace("\${LANG}", "\"" + Locale.getDefault().language + "\"") | ||||
|             .replace("\${LIMIT}", "" + limit) | ||||
|             .replace("\${OFFSET}", "" + startPosition) | ||||
|         val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!! | ||||
|             .newBuilder() | ||||
|             .addQueryParameter("query", query) | ||||
|             .addQueryParameter("format", "json") | ||||
|         return Request.Builder().url(urlBuilder.build()).build() | ||||
|     } | ||||
| 
 | ||||
|     @Throws(IOException::class) | ||||
|     private fun runQuery(currentLatLng: LatLng, nextLatLng: LatLng): List<PlaceBindings>? { | ||||
|         val wikidataQuery = FileUtils.readFromResource("/queries/places_query.rq") | ||||
|         val query = wikidataQuery | ||||
|             .replace("\${LONGITUDE}", String.format(Locale.ROOT, "%.2f", currentLatLng.longitude)) | ||||
|             .replace("\${LATITUDE}", String.format(Locale.ROOT, "%.4f", currentLatLng.latitude)) | ||||
|             .replace("\${NEXT_LONGITUDE}", String.format(Locale.ROOT, "%.4f", nextLatLng.longitude)) | ||||
|             .replace("\${NEXT_LATITUDE}", String.format(Locale.ROOT, "%.4f", nextLatLng.latitude)) | ||||
| 
 | ||||
|         val urlBuilder: HttpUrl.Builder = sparqlQueryUrl.toHttpUrlOrNull()!! | ||||
|             .newBuilder() | ||||
|             .addQueryParameter("query", query) | ||||
|             .addQueryParameter("format", "json") | ||||
| 
 | ||||
|         val request: Request = Request.Builder().url(urlBuilder.build()).build() | ||||
| 
 | ||||
|         val response = okHttpClient.newCall(request).execute() | ||||
|         if (response.body != null && response.isSuccessful) { | ||||
|             val json = response.body!!.string() | ||||
|             val item = gson.fromJson(json, ItemsClass::class.java) | ||||
|             return item.results.bindings | ||||
|         } else { | ||||
|             return null | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -194,7 +194,7 @@ class FileProcessor | |||
|             requireNotNull(imageCoordinates.decimalCoords) | ||||
|             compositeDisposable.add( | ||||
|                 apiCall | ||||
|                     .request(imageCoordinates.decimalCoords) | ||||
|                     .request(imageCoordinates.decimalCoords!!) | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .observeOn(Schedulers.io()) | ||||
|                     .subscribe( | ||||
|  | @ -220,7 +220,7 @@ class FileProcessor | |||
|                 .concatMap { | ||||
|                     Observable.fromCallable { | ||||
|                         okHttpJsonApiClient.getNearbyPlaces( | ||||
|                             imageCoordinates.latLng, | ||||
|                             imageCoordinates.latLng!!, | ||||
|                             Locale.getDefault().language, | ||||
|                             it, | ||||
|                         ) | ||||
|  |  | |||
|  | @ -49,13 +49,13 @@ class CampaignsPresenterTest { | |||
|         campaignsSingle = Single.just(campaignResponseDTO) | ||||
|         campaignsPresenter = CampaignsPresenter(okHttpJsonApiClient, testScheduler, testScheduler) | ||||
|         campaignsPresenter.onAttachView(view) | ||||
|         Mockito.`when`(okHttpJsonApiClient.campaigns).thenReturn(campaignsSingle) | ||||
|         Mockito.`when`(okHttpJsonApiClient.getCampaigns()).thenReturn(campaignsSingle) | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     fun getCampaignsTestNoCampaigns() { | ||||
|         campaignsPresenter.getCampaigns() | ||||
|         verify(okHttpJsonApiClient).campaigns | ||||
|         verify(okHttpJsonApiClient).getCampaigns() | ||||
|         testScheduler.triggerActions() | ||||
|         verify(view).showCampaigns(null) | ||||
|     } | ||||
|  | @ -77,7 +77,7 @@ class CampaignsPresenterTest { | |||
|         Mockito.`when`(campaign.endDate).thenReturn(endDateString) | ||||
|         Mockito.`when`(campaign.startDate).thenReturn(startDateString) | ||||
|         Mockito.`when`(campaignResponseDTO.campaigns).thenReturn(campaigns) | ||||
|         verify(okHttpJsonApiClient).campaigns | ||||
|         verify(okHttpJsonApiClient).getCampaigns() | ||||
|         testScheduler.triggerActions() | ||||
|         verify(view).showCampaigns(campaign) | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Paul Hawke
						Paul Hawke