Fixes #545 - Add support for campaigns (#4423)

* Integrate WLM
- Show monuments in maps along with nearby

* BugFix in Monuments
1. Single preference for monuments and campaigns
2. Expand collapse chips container in nearby
3. Typo fix in Monuments card in Nearby
4. If a nearby place is a monument as well - do not show them separately, show it as a monument instead
5. Bug fix, monument radius, use the same one as that of nearby

* More bug fixes
1. Possible NPE in nearby
2. Added column location_address in BookmarkLocationDao
3. Bug Fix - Display Date in WLM card
4. WLM card on click takes to nearby

* Use lowercase country code in WLM uploads

* Bug-Fix, WLM Campaign Icon

* 1. Updated monuments query to use any of the following properties for monuments - [P1435, P2186, P1459, P1460, P1216, P709, P718, P5694] 2. Append WikiData QID to descriptions template

* Updated WLM Banner String, Handle NPE in contributions callback

* Added nearby-monuments query log lines

* Handle WLM Query exception : - if an exception is thrown in WLM query, continue showing the nearby items if that succeeds

* Fix BookmarkLocationDaoTest

* Added Column Address in BookmarkLocationDaoTest

* Use fallback description as usual nearby pins even for WLM pins, instead of relying on P6375

* Test fix in BookmarkLocationDao

* Updated template for WLM, removed redundant feilds

* Fixed WLM template

* Removed categories from WLM template

* Fixed BookmarkControllerTest

* Fixed BookmarkLocationFragmentUnitTest

* fix ModelFunctions

* Fixed BookmarksDaoLocationTest

* Fixed WLM template
This commit is contained in:
Ashish 2021-08-18 13:57:26 +05:30 committed by GitHub
parent 67f5b6c271
commit 6588a6fd0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 2906 additions and 185 deletions

View file

@ -17,6 +17,8 @@ import androidx.browser.customtabs.CustomTabColorSchemeParams;
import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsIntent;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import java.util.Date;
import org.wikipedia.dataclient.WikiSite; import org.wikipedia.dataclient.WikiSite;
import org.wikipedia.page.PageTitle; import org.wikipedia.page.PageTitle;
@ -29,6 +31,7 @@ import fr.free.nrw.commons.utils.ViewUtil;
import timber.log.Timber; import timber.log.Timber;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static fr.free.nrw.commons.campaigns.CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE;
public class Utils { public class Utils {
@ -210,4 +213,35 @@ public class Utils {
textView.setText(content); textView.setText(content);
} }
/**
* For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt
* @param date
* @return
*/
public static boolean isMonumentsEnabled(final Date date, final JsonKvStore store){
if(date.getDay()>=1 && date.getMonth()>=9 && date.getDay()<=31 && date.getMonth()<=10 ){
return true;
}
return store.getBoolean(CAMPAIGNS_DEFAULT_PREFERENCE) || true ;
}
/**
* Util function to get the start date of wlm monument
* For this release we are hardcoding it to be 1st September
* @return
*/
public static String getWLMStartDate() {
return "1 Sep";
}
/***
* Util function to get the end date of wlm monument
* For this release we are hardcoding it to be 31st October
* @return
*/
public static String getWLMEndDate() {
return "31 Oct";
}
} }

View file

@ -147,25 +147,25 @@ public class BookmarkLocationsDao {
} }
@NonNull @NonNull
Place fromCursor(Cursor cursor) { Place fromCursor(final Cursor cursor) {
LatLng location = new LatLng(cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LAT)), final LatLng location = new LatLng(cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LAT)),
cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LONG)), 1F); cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LONG)), 1F);
Sitelinks.Builder builder = new Sitelinks.Builder(); final Sitelinks.Builder builder = new Sitelinks.Builder();
builder.setWikipediaLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIPEDIA_LINK))); builder.setWikipediaLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIPEDIA_LINK)));
builder.setWikidataLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIDATA_LINK))); builder.setWikidataLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_WIKIDATA_LINK)));
builder.setCommonsLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_COMMONS_LINK))); builder.setCommonsLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_COMMONS_LINK)));
return new Place( return new Place(
cursor.getString(cursor.getColumnIndex(Table.COLUMN_LANGUAGE)), cursor.getString(cursor.getColumnIndex(Table.COLUMN_LANGUAGE)),
cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)), cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)),
Label.fromText((cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT)))), Label.fromText((cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT)))),
cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)), cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)),
location, location,
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CATEGORY)), cursor.getString(cursor.getColumnIndex(Table.COLUMN_CATEGORY)),
builder.build(), builder.build(),
cursor.getString(cursor.getColumnIndex(Table.COLUMN_PIC)), cursor.getString(cursor.getColumnIndex(Table.COLUMN_PIC)),
Boolean.parseBoolean(cursor.getString(cursor.getColumnIndex(Table.COLUMN_EXISTS))) Boolean.parseBoolean(cursor.getString(cursor.getColumnIndex(Table.COLUMN_EXISTS)))
); );
} }
@ -220,7 +220,7 @@ public class BookmarkLocationsDao {
COLUMN_WIKIDATA_LINK, COLUMN_WIKIDATA_LINK,
COLUMN_COMMONS_LINK, COLUMN_COMMONS_LINK,
COLUMN_PIC, COLUMN_PIC,
COLUMN_EXISTS COLUMN_EXISTS,
}; };
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME; static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
@ -251,7 +251,7 @@ public class BookmarkLocationsDao {
onCreate(db); onCreate(db);
} }
public static void onUpdate(SQLiteDatabase db, int from, int to) { public static void onUpdate(final SQLiteDatabase db, int from, final int to) {
Timber.d("bookmarksLocations db is updated from:"+from+", to:"+to); Timber.d("bookmarksLocations db is updated from:"+from+", to:"+to);
if (from == to) { if (from == to) {
return; return;

View file

@ -7,4 +7,5 @@ data class Campaign(var title: String? = null,
var description: String? = null, var description: String? = null,
var startDate: String? = null, var startDate: String? = null,
var endDate: String? = null, var endDate: String? = null,
var link: String? = null) var link: String? = null,
var isWLMCampaign: Boolean = false)

View file

@ -4,11 +4,13 @@ import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import fr.free.nrw.commons.theme.BaseActivity;
import org.wikipedia.util.DateUtil; import org.wikipedia.util.DateUtil;
import java.text.ParseException; import java.text.ParseException;
@ -27,9 +29,14 @@ import fr.free.nrw.commons.utils.ViewUtil;
* A view which represents a single campaign * A view which represents a single campaign
*/ */
public class CampaignView extends SwipableCardView { public class CampaignView extends SwipableCardView {
Campaign campaign = null; Campaign campaign;
private ViewHolder viewHolder; private ViewHolder viewHolder;
public static final String CAMPAIGNS_DEFAULT_PREFERENCE = "displayCampaignsCardView";
public static final String WLM_CARD_PREFERENCE = "displayWLMCardView";
private String campaignPreference = CAMPAIGNS_DEFAULT_PREFERENCE;
public CampaignView(@NonNull Context context) { public CampaignView(@NonNull Context context) {
super(context); super(context);
init(); init();
@ -45,37 +52,46 @@ public class CampaignView extends SwipableCardView {
init(); init();
} }
public void setCampaign(Campaign campaign) { public void setCampaign(final Campaign campaign) {
this.campaign = campaign; this.campaign = campaign;
if (campaign != null) { if (campaign != null) {
this.setVisibility(View.VISIBLE); if (campaign.isWLMCampaign()) {
campaignPreference = WLM_CARD_PREFERENCE;
}
setVisibility(View.VISIBLE);
viewHolder.init(); viewHolder.init();
} else { } else {
this.setVisibility(View.GONE); this.setVisibility(View.GONE);
} }
} }
@Override public boolean onSwipe(View view) { @Override public boolean onSwipe(final View view) {
view.setVisibility(View.GONE); view.setVisibility(View.GONE);
((MainActivity) getContext()).defaultKvStore ((BaseActivity) getContext()).defaultKvStore
.putBoolean("displayCampaignsCardView", false); .putBoolean(campaignPreference, false);
ViewUtil.showLongToast(getContext(), ViewUtil.showLongToast(getContext(),
getResources().getString(R.string.nearby_campaign_dismiss_message)); getResources().getString(R.string.nearby_campaign_dismiss_message));
return true; return true;
} }
private void init() { private void init() {
View rootView = inflate(getContext(), R.layout.layout_campagin, this); final View rootView = inflate(getContext(), R.layout.layout_campagin, this);
viewHolder = new ViewHolder(rootView); viewHolder = new ViewHolder(rootView);
setOnClickListener(view -> { setOnClickListener(view -> {
if (campaign != null) { if (campaign != null) {
Utils.handleWebUrl(getContext(), Uri.parse(campaign.getLink())); if (campaign.isWLMCampaign()) {
((MainActivity)(getContext())).showNearby();
} else {
Utils.handleWebUrl(getContext(), Uri.parse(campaign.getLink()));
}
} }
}); });
} }
public class ViewHolder { public class ViewHolder {
@BindView(R.id.iv_campaign)
ImageView ivCampaign;
@BindView(R.id.tv_title) TextView tvTitle; @BindView(R.id.tv_title) TextView tvTitle;
@BindView(R.id.tv_description) TextView tvDescription; @BindView(R.id.tv_description) TextView tvDescription;
@BindView(R.id.tv_dates) TextView tvDates; @BindView(R.id.tv_dates) TextView tvDates;
@ -86,14 +102,26 @@ public class CampaignView extends SwipableCardView {
public void init() { public void init() {
if (campaign != null) { if (campaign != null) {
ivCampaign.setImageDrawable(
getResources().getDrawable(R.drawable.ic_campaign));
tvTitle.setText(campaign.getTitle()); tvTitle.setText(campaign.getTitle());
tvDescription.setText(campaign.getDescription()); tvDescription.setText(campaign.getDescription());
try { try {
Date startDate = CommonsDateUtil.getIso8601DateFormatShort().parse(campaign.getStartDate()); if (campaign.isWLMCampaign()) {
Date endDate = CommonsDateUtil.getIso8601DateFormatShort().parse(campaign.getEndDate()); tvDates.setText(
tvDates.setText(String.format("%1s - %2s", DateUtil.getExtraShortDateString(startDate), String.format("%1s - %2s", campaign.getStartDate(),
DateUtil.getExtraShortDateString(endDate))); campaign.getEndDate()));
} catch (ParseException e) { } else {
final Date startDate = CommonsDateUtil.getIso8601DateFormatShort()
.parse(campaign.getStartDate());
final Date endDate = CommonsDateUtil.getIso8601DateFormatShort()
.parse(campaign.getEndDate());
tvDates.setText(
String.format("%1s - %2s", startDate,
endDate));
}
} catch (final ParseException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }

View file

@ -2,14 +2,13 @@ package fr.free.nrw.commons.contributions;
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED; import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED; import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED;
import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@ -27,21 +26,21 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener; import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.notification.Notification; import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationController; import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.theme.BaseActivity;
import java.text.ParseException;
import java.util.Date;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import androidx.work.WorkInfo;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.campaigns.Campaign; import fr.free.nrw.commons.campaigns.Campaign;
import fr.free.nrw.commons.campaigns.CampaignView; import fr.free.nrw.commons.campaigns.CampaignView;
import fr.free.nrw.commons.campaigns.CampaignsPresenter; import fr.free.nrw.commons.campaigns.CampaignsPresenter;
@ -59,10 +58,7 @@ import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.nearby.NearbyController; import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyNotificationCardView; import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.upload.worker.UploadWorker; import fr.free.nrw.commons.upload.worker.UploadWorker;
import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.DialogUtil;
@ -73,9 +69,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import timber.log.Timber; import timber.log.Timber;
public class ContributionsFragment public class ContributionsFragment
@ -118,6 +111,8 @@ public class ContributionsFragment
public TextView notificationCount; public TextView notificationCount;
private Campaign wlmCampaign;
@NonNull @NonNull
public static ContributionsFragment newInstance() { public static ContributionsFragment newInstance() {
ContributionsFragment fragment = new ContributionsFragment(); ContributionsFragment fragment = new ContributionsFragment();
@ -137,6 +132,7 @@ public class ContributionsFragment
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_contributions, container, false); View view = inflater.inflate(R.layout.fragment_contributions, container, false);
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
initWLMCampaign();
presenter.onAttachView(this); presenter.onAttachView(this);
contributionsPresenter.onAttachView(this); contributionsPresenter.onAttachView(this);
campaignView.setVisibility(View.GONE); campaignView.setVisibility(View.GONE);
@ -177,6 +173,15 @@ public class ContributionsFragment
return view; return view;
} }
/**
* Initialise the campaign object for WML
*/
private void initWLMCampaign() {
wlmCampaign = new Campaign(getString(R.string.wlm_campaign_title),
getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(),
Utils.getWLMEndDate().toString(), WLM_URL, true);
}
@Override @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.contribution_activity_notification_menu, menu); inflater.inflate(R.menu.contribution_activity_notification_menu, menu);
@ -518,13 +523,17 @@ public class ContributionsFragment
} }
/** /**
* ask the presenter to fetch the campaigns only if user has not manually disabled it * As the home screen has limited space, we have choosen to show either campaigns or WLM card.
* The WLM Card gets the priority over monuments, so if the WLM is going on we show that instead
* of campaigns on the campaigns card
*/ */
private void fetchCampaigns() { private void fetchCampaigns() {
if (store.getBoolean("displayCampaignsCardView", true)) { if (Utils.isMonumentsEnabled(new Date(), store)) {
campaignView.setCampaign(wlmCampaign);
campaignView.setVisibility(View.VISIBLE);
} else if (store.getBoolean(CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE, true)) {
presenter.getCampaigns(); presenter.getCampaigns();
} } else {
else{
campaignView.setVisibility(View.GONE); campaignView.setVisibility(View.GONE);
} }
} }

View file

@ -90,7 +90,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private ContributionsListAdapter adapter; private ContributionsListAdapter adapter;
private Callback callback; @Nullable private Callback callback;
private final int SPAN_COUNT_LANDSCAPE = 3; private final int SPAN_COUNT_LANDSCAPE = 3;
private final int SPAN_COUNT_PORTRAIT = 1; private final int SPAN_COUNT_PORTRAIT = 1;
@ -150,7 +150,9 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
contributionsListPresenter.contributionList.observe(this.getViewLifecycleOwner(), list -> { contributionsListPresenter.contributionList.observe(this.getViewLifecycleOwner(), list -> {
contributionsSize = list.size(); contributionsSize = list.size();
adapter.submitList(list); adapter.submitList(list);
callback.notifyDataSetChanged(); if (callback != null) {
callback.notifyDataSetChanged();
}
}); });
rvContributionsList.setAdapter(adapter); rvContributionsList.setAdapter(adapter);
adapter.registerAdapterDataObserver(new AdapterDataObserver() { adapter.registerAdapterDataObserver(new AdapterDataObserver() {
@ -172,7 +174,9 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
@Override @Override
public void onItemRangeChanged(final int positionStart, final int itemCount) { public void onItemRangeChanged(final int positionStart, final int itemCount) {
super.onItemRangeChanged(positionStart, itemCount); super.onItemRangeChanged(positionStart, itemCount);
callback.viewPagerNotifyDataSetChanged(); if (callback != null) {
callback.viewPagerNotifyDataSetChanged();
}
} }
}); });

View file

@ -373,6 +373,13 @@ public class MainActivity extends BaseActivity
super.onDestroy(); super.onDestroy();
} }
/**
* Public method to show nearby from the reference of this.
*/
public void showNearby() {
tabLayout.setSelectedItemId(NavTab.NEARBY.code());
}
public enum ActiveFragment { public enum ActiveFragment {
CONTRIBUTIONS, CONTRIBUTIONS,
NEARBY, NEARBY,

View file

@ -13,7 +13,7 @@ import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao;
public class DBOpenHelper extends SQLiteOpenHelper { public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "commons.db"; private static final String DATABASE_NAME = "commons.db";
private static final int DATABASE_VERSION = 15; private static final int DATABASE_VERSION = 17;
public static final String CONTRIBUTIONS_TABLE = "contributions"; public static final String CONTRIBUTIONS_TABLE = "contributions";
private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s"; private final String DROP_TABLE_STATEMENT="DROP TABLE IF EXISTS %s";

View file

@ -265,7 +265,9 @@ public class OkHttpJsonApiClient {
} }
public Observable<List<Place>> getNearbyPlaces(LatLng cur, String language, double radius) public Observable<List<Place>> getNearbyPlaces(LatLng cur, String language, double radius)
throws IOException { throws Exception {
Timber.d("Fetching nearby items at radius %s", radius);
String wikidataQuery = FileUtils.readFromResource("/queries/nearby_query.rq"); String wikidataQuery = FileUtils.readFromResource("/queries/nearby_query.rq");
String query = wikidataQuery String query = wikidataQuery
.replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius)) .replace("${RAD}", String.format(Locale.ROOT, "%.2f", radius))
@ -302,6 +304,67 @@ public class OkHttpJsonApiClient {
}); });
} }
/**
* Wikidata query to fetch monuments
*
* @param cur : The current location coordinates
* @param language : The language
* @param radius : The radius around the current location within which we expect the results
* @return
* @throws IOException
*/
public Observable<List<Place>> getNearbyMonuments(LatLng cur, String language, final double radius){
Timber.d("Fetching monuments at radius %s", radius);
final String wikidataQuery;
try {
wikidataQuery = FileUtils.readFromResource("/queries/monuments_query.rq");
if (TextUtils.isEmpty(language)) {
language = "en";
}
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);
HttpUrl.Builder urlBuilder = HttpUrl
.parse(sparqlQueryUrl)
.newBuilder()
.addQueryParameter("query", query)
.addQueryParameter("format", "json");
Request request = new Request.Builder()
.url(urlBuilder.build())
.build();
Timber.d("Monuments URL: %s", request.url().toString());
return Observable.fromCallable(() -> {
final Response response = okHttpClient.newCall(request).execute();
if (response != null && response.body() != null && response.isSuccessful()) {
final String json = response.body().string();
if (json == null) {
return new ArrayList<>();
}
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 place = Place.from(item);
place.setMonument(true);
places.add(place);
}
return places;
}
return new ArrayList<>();
});
} catch (final IOException e) {
e.printStackTrace();
return Observable.error(e);
}
}
/** /**
* Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example: * Get the QIDs of all Wikidata items that are subclasses of the given Wikidata item. Example:
* bridge -> suspended bridge, aqueduct, etc * bridge -> suspended bridge, aqueduct, etc

View file

@ -8,14 +8,17 @@ import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLng;
import java.util.Objects;
public class NearbyBaseMarker extends BaseMarkerOptions<NearbyMarker, NearbyBaseMarker> { public class NearbyBaseMarker extends BaseMarkerOptions<NearbyMarker, NearbyBaseMarker> {
public static final Parcelable.Creator<NearbyBaseMarker> CREATOR = new Parcelable.Creator<NearbyBaseMarker>() { public static final Parcelable.Creator<NearbyBaseMarker> CREATOR = new Parcelable.Creator<NearbyBaseMarker>() {
@Override
public NearbyBaseMarker createFromParcel(Parcel in) { public NearbyBaseMarker createFromParcel(Parcel in) {
return new NearbyBaseMarker(in); return new NearbyBaseMarker(in);
} }
@Override
public NearbyBaseMarker[] newArray(int size) { public NearbyBaseMarker[] newArray(int size) {
return new NearbyBaseMarker[size]; return new NearbyBaseMarker[size];
} }
@ -70,4 +73,21 @@ public class NearbyBaseMarker extends BaseMarkerOptions<NearbyMarker, NearbyBase
dest.writeString(title); dest.writeString(title);
dest.writeParcelable(place, 0); dest.writeParcelable(place, 0);
} }
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final NearbyBaseMarker that = (NearbyBaseMarker) o;
return Objects.equals(place.location, that.place.location);
}
@Override
public int hashCode() {
return Objects.hash(place);
}
} }

View file

@ -10,6 +10,7 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import com.mapbox.mapboxsdk.annotations.IconFactory; import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.Marker;
import io.reactivex.Observable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -127,6 +128,11 @@ public class NearbyController {
} }
} }
public Observable<List<Place>> queryWikiDataForMonuments(
final LatLng latLng, final String language) {
return nearbyPlaces.queryWikiDataForMonuments(latLng, language);
}
/** /**
* Loads attractions from location for list view, we need to return Place data type. * Loads attractions from location for list view, we need to return Place data type.
* *
@ -168,6 +174,7 @@ public class NearbyController {
VectorDrawableCompat vectorDrawable = null; VectorDrawableCompat vectorDrawable = null;
VectorDrawableCompat vectorDrawableGreen = null; VectorDrawableCompat vectorDrawableGreen = null;
VectorDrawableCompat vectorDrawableGrey = null; VectorDrawableCompat vectorDrawableGrey = null;
VectorDrawableCompat vectorDrawableMonuments = null;
vectorDrawable = null; vectorDrawable = null;
try { try {
vectorDrawable = VectorDrawableCompat.create( vectorDrawable = VectorDrawableCompat.create(
@ -176,6 +183,9 @@ public class NearbyController {
context.getResources(), R.drawable.ic_custom_map_marker_green, context.getTheme()); context.getResources(), R.drawable.ic_custom_map_marker_green, context.getTheme());
vectorDrawableGrey = VectorDrawableCompat.create( vectorDrawableGrey = VectorDrawableCompat.create(
context.getResources(), R.drawable.ic_custom_map_marker_grey, context.getTheme()); context.getResources(), R.drawable.ic_custom_map_marker_grey, context.getTheme());
vectorDrawableMonuments = VectorDrawableCompat
.create(context.getResources(), R.drawable.ic_custom_map_marker_monuments,
context.getTheme());
} catch (Resources.NotFoundException e) { } catch (Resources.NotFoundException e) {
// ignore when running tests. // ignore when running tests.
} }
@ -183,34 +193,39 @@ public class NearbyController {
Bitmap icon = UiUtils.getBitmap(vectorDrawable); Bitmap icon = UiUtils.getBitmap(vectorDrawable);
Bitmap iconGreen = UiUtils.getBitmap(vectorDrawableGreen); Bitmap iconGreen = UiUtils.getBitmap(vectorDrawableGreen);
Bitmap iconGrey = UiUtils.getBitmap(vectorDrawableGrey); Bitmap iconGrey = UiUtils.getBitmap(vectorDrawableGrey);
Bitmap iconMonuments = UiUtils.getBitmap(vectorDrawableMonuments);
for (Place place : placeList) { for (Place place : placeList) {
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
String distance = formatDistanceBetween(curLatLng, place.location); String distance = formatDistanceBetween(curLatLng, place.location);
place.setDistance(distance); place.setDistance(distance);
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
nearbyBaseMarker.title(place.name); nearbyBaseMarker.title(place.name);
nearbyBaseMarker.position( nearbyBaseMarker.position(
new com.mapbox.mapboxsdk.geometry.LatLng( new com.mapbox.mapboxsdk.geometry.LatLng(
place.location.getLatitude(), place.location.getLatitude(),
place.location.getLongitude())); place.location.getLongitude()));
nearbyBaseMarker.place(place); nearbyBaseMarker.place(place);
// Check if string is only spaces or empty, if so place doesn't have any picture // Check if string is only spaces or empty, if so place doesn't have any picture
if (!place.pic.trim().isEmpty()) {
if (place.isMonument()) {
nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(iconMonuments));
}
else if (!place.pic.trim().isEmpty()) {
if (iconGreen != null) { if (iconGreen != null) {
nearbyBaseMarker.icon(IconFactory.getInstance(context) nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(iconGreen)); .fromBitmap(iconGreen));
} }
} else if (!place.exists) { // Means that the topic of the Wikidata item does not exist in the real world anymore, for instance it is a past event, or a place that was destroyed } else if (!place.exists) { // Means that the topic of the Wikidata item does not exist in the real world anymore, for instance it is a past event, or a place that was destroyed
if (iconGrey != null) { if (iconGrey != null) {
nearbyBaseMarker.icon(IconFactory.getInstance(context) nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(iconGrey)); .fromBitmap(iconGrey));
} }
} else { } else {
nearbyBaseMarker.icon(IconFactory.getInstance(context) nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(icon)); .fromBitmap(icon));
} }
baseMarkerOptions.add(nearbyBaseMarker); baseMarkerOptions.add(nearbyBaseMarker);
} }
} }

View file

@ -5,6 +5,7 @@ import java.util.ArrayList;
public class NearbyFilterState { public class NearbyFilterState {
private boolean existsSelected; private boolean existsSelected;
private boolean needPhotoSelected; private boolean needPhotoSelected;
private boolean wlmSelected;
private int checkBoxTriState; private int checkBoxTriState;
private ArrayList<Label> selectedLabels; private ArrayList<Label> selectedLabels;
@ -16,6 +17,7 @@ public class NearbyFilterState {
private NearbyFilterState() { private NearbyFilterState() {
existsSelected = false; existsSelected = false;
needPhotoSelected = true; needPhotoSelected = true;
wlmSelected = true;
checkBoxTriState = -1; // Unknown checkBoxTriState = -1; // Unknown
selectedLabels = new ArrayList<>(); // Initially empty selectedLabels = new ArrayList<>(); // Initially empty
} }
@ -39,6 +41,14 @@ public class NearbyFilterState {
getInstance().needPhotoSelected = needPhotoSelected; getInstance().needPhotoSelected = needPhotoSelected;
} }
public static void setWlmSelected(final boolean wlmSelected) {
getInstance().wlmSelected = wlmSelected;
}
public boolean isWlmSelected() {
return wlmSelected;
}
public boolean isExistsSelected() { public boolean isExistsSelected() {
return existsSelected; return existsSelected;
} }

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.nearby; package fr.free.nrw.commons.nearby;
import io.reactivex.Observable;
import java.io.IOException; import java.io.IOException;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.util.Collections; import java.util.Collections;
@ -65,9 +66,9 @@ public class NearbyPlaces {
while (radius <= maxRadius) { while (radius <= maxRadius) {
try { try {
places = getFromWikidataQuery(curLatLng, lang, radius); places = getFromWikidataQuery(curLatLng, lang, radius);
} catch (InterruptedIOException e) { } catch (final Exception e) {
Timber.e(e, "exception in fetching nearby places"); Timber.e(e, "Exception in fetching nearby places");
return places; break;
} }
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) {
@ -91,7 +92,13 @@ public class NearbyPlaces {
* @return list of places obtained * @return list of places obtained
* @throws IOException if query fails * @throws IOException if query fails
*/ */
public List<Place> getFromWikidataQuery(LatLng cur, String lang, double radius) throws IOException { public List<Place> getFromWikidataQuery(LatLng cur, String lang, double radius) throws Exception {
return okHttpJsonApiClient.getNearbyPlaces(cur, lang, radius).blockingSingle(); return okHttpJsonApiClient.getNearbyPlaces(cur, lang, radius).blockingSingle();
} }
public Observable<List<Place>> queryWikiDataForMonuments(
LatLng latLng, String language) {
return okHttpJsonApiClient
.getNearbyMonuments(latLng, language, radius);
}
} }

View file

@ -4,8 +4,10 @@ import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import fr.free.nrw.commons.nearby.NearbyController.NearbyPlacesInfo;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng;
@ -31,6 +33,7 @@ public class Place implements Parcelable {
public String distance; public String distance;
public final Sitelinks siteLinks; public final Sitelinks siteLinks;
private boolean isMonument;
public Place(String language,String name, Label label, String longDescription, LatLng location, String category, Sitelinks siteLinks, String pic, Boolean exists) { public Place(String language,String name, Label label, String longDescription, LatLng location, String category, Sitelinks siteLinks, String pic, Boolean exists) {
@ -56,6 +59,7 @@ public class Place implements Parcelable {
this.pic = (picString == null) ? "":picString; this.pic = (picString == null) ? "":picString;
String existString = in.readString(); String existString = in.readString();
this.exists = Boolean.parseBoolean(existString); this.exists = Boolean.parseBoolean(existString);
this.isMonument = in.readInt() == 1;
} }
public static Place from(NearbyResultItem item) { public static Place from(NearbyResultItem item) {
String itemClass = item.getClassName().getValue(); String itemClass = item.getClassName().getValue();
@ -80,20 +84,20 @@ public class Place implements Parcelable {
? " (" + description + ")" : "") ? " (" + description + ")" : "")
: description); : description);
return new Place( return new Place(
item.getLabel().getLanguage(), item.getLabel().getLanguage(),
item.getLabel().getValue(), item.getLabel().getValue(),
Label.fromText(classEntityId), // list Label.fromText(classEntityId), // list
description, // description and label of Wikidata item description, // description and label of Wikidata item
PlaceUtils.latLngFromPointString(item.getLocation().getValue()), PlaceUtils.latLngFromPointString(item.getLocation().getValue()),
item.getCommonsCategory().getValue(), item.getCommonsCategory().getValue(),
new Sitelinks.Builder() new Sitelinks.Builder()
.setWikipediaLink(item.getWikipediaArticle().getValue()) .setWikipediaLink(item.getWikipediaArticle().getValue())
.setCommonsLink(item.getCommonsArticle().getValue()) .setCommonsLink(item.getCommonsArticle().getValue())
.setWikidataLink(item.getItem().getValue()) .setWikidataLink(item.getItem().getValue())
.build(), .build(),
item.getPic().getValue(), item.getPic().getValue(),
// Checking if the place exists or not // Checking if the place exists or not
(item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == "")); (item.getDestroyed().getValue() == "") && (item.getEndTime().getValue() == ""));
} }
/** /**
@ -126,7 +130,9 @@ public class Place implements Parcelable {
* Gets the long description of the place * Gets the long description of the place
* @return long description * @return long description
*/ */
public String getLongDescription() { return longDescription; } public String getLongDescription() {
return longDescription;
}
/** /**
* Gets the Commons category of the place * Gets the Commons category of the place
@ -181,6 +187,22 @@ public class Place implements Parcelable {
return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getCommonsLink())); return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getCommonsLink()));
} }
/**
* Sets that this place in nearby is a WikiData monument
* @param monument
*/
public void setMonument(final boolean monument) {
isMonument = monument;
}
/**
* Returns if this place is a WikiData monument
* @return
*/
public boolean isMonument() {
return isMonument;
}
/** /**
* Check if we already have the exact same Place * Check if we already have the exact same Place
* @param o Place being tested * @param o Place being tested
@ -233,6 +255,7 @@ public class Place implements Parcelable {
dest.writeParcelable(siteLinks, 0); dest.writeParcelable(siteLinks, 0);
dest.writeString(pic); dest.writeString(pic);
dest.writeString(exists.toString()); dest.writeString(exists.toString());
dest.writeInt(isMonument ? 1 : 0);
} }
public static final Creator<Place> CREATOR = new Creator<Place>() { public static final Creator<Place> CREATOR = new Creator<Place>() {

View file

@ -3,7 +3,6 @@ package fr.free.nrw.commons.nearby.contract;
import android.content.Context; import android.content.Context;
import com.mapbox.mapboxsdk.annotations.Marker; import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.util.List; import java.util.List;
@ -13,7 +12,6 @@ import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.nearby.Label; import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.NearbyBaseMarker; import fr.free.nrw.commons.nearby.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
public interface NearbyParentFragmentContract { public interface NearbyParentFragmentContract {
@ -60,7 +58,7 @@ public interface NearbyParentFragmentContract {
void displayAllMarkers(); void displayAllMarkers();
void filterMarkersByLabels(List<Label> selectedLabels, boolean existsSelected, boolean needPhotoSelected, boolean filterForPlaceState, boolean filterForAllNoneType); void filterMarkersByLabels(List<Label> selectedLabels, boolean existsSelected, boolean needPhotoSelected, boolean wlmSelected, boolean filterForPlaceState, boolean filterForAllNoneType);
LatLng getCameraTarget(); LatLng getCameraTarget();

View file

@ -3,7 +3,6 @@ package fr.free.nrw.commons.nearby.fragments;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
import static fr.free.nrw.commons.nearby.Label.TEXT_TO_DESCRIPTION;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
@ -16,8 +15,8 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.VectorDrawable;
import android.location.LocationManager; import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.text.Html; import android.text.Html;
@ -45,8 +44,8 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.AppCompatTextView; import androidx.appcompat.widget.AppCompatTextView;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -54,6 +53,7 @@ import androidx.recyclerview.widget.RecyclerView;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.chip.Chip; import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup; import com.google.android.material.chip.ChipGroup;
@ -83,26 +83,24 @@ import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.ContributionController; import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.contributions.ContributionsFragment;
import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment; import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.location.LocationUpdateListener;
import fr.free.nrw.commons.navtab.NavTab;
import fr.free.nrw.commons.nearby.CheckBoxTriStates; import fr.free.nrw.commons.nearby.CheckBoxTriStates;
import fr.free.nrw.commons.nearby.Label; import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.MarkerPlaceGroup; import fr.free.nrw.commons.nearby.MarkerPlaceGroup;
import fr.free.nrw.commons.nearby.NearbyBaseMarker; import fr.free.nrw.commons.nearby.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.NearbyController; import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyController.NearbyPlacesInfo;
import fr.free.nrw.commons.nearby.NearbyFilterSearchRecyclerViewAdapter; import fr.free.nrw.commons.nearby.NearbyFilterSearchRecyclerViewAdapter;
import fr.free.nrw.commons.nearby.NearbyFilterState; import fr.free.nrw.commons.nearby.NearbyFilterState;
import fr.free.nrw.commons.nearby.NearbyMarker; import fr.free.nrw.commons.nearby.NearbyMarker;
import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter; import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.utils.ExecutorUtils; import fr.free.nrw.commons.utils.ExecutorUtils;
import fr.free.nrw.commons.utils.LayoutUtils; import fr.free.nrw.commons.utils.LayoutUtils;
@ -116,14 +114,18 @@ import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.wikidata.WikidataEditListener; import fr.free.nrw.commons.wikidata.WikidataEditListener;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import kotlin.Unit; import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import timber.log.Timber; import timber.log.Timber;
@ -155,6 +157,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@BindView(R.id.search_this_area_button) Button searchThisAreaButton; @BindView(R.id.search_this_area_button) Button searchThisAreaButton;
@BindView(R.id.map_progress_bar) ProgressBar progressBar; @BindView(R.id.map_progress_bar) ProgressBar progressBar;
@BindView(R.id.choice_chip_exists) Chip chipExists; @BindView(R.id.choice_chip_exists) Chip chipExists;
@BindView(R.id.choice_chip_wlm) Chip chipWlm;
@BindView(R.id.choice_chip_needs_photo) Chip chipNeedsPhoto; @BindView(R.id.choice_chip_needs_photo) Chip chipNeedsPhoto;
@BindView(R.id.choice_chip_group) ChipGroup choiceChipGroup; @BindView(R.id.choice_chip_group) ChipGroup choiceChipGroup;
@BindView(R.id.search_view) SearchView searchView; @BindView(R.id.search_view) SearchView searchView;
@ -168,6 +171,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@BindView(R.id.no_results_message) TextView noResultsView; @BindView(R.id.no_results_message) TextView noResultsView;
@BindView(R.id.tv_attribution) @BindView(R.id.tv_attribution)
AppCompatTextView tvAttribution; AppCompatTextView tvAttribution;
@BindView(R.id.rl_container_wlm_month_message)
RelativeLayout rlContainerWLMMonthMessage;
@BindView(R.id.tv_learn_more)
AppCompatTextView tvLearnMore;
@BindView(R.id.iv_toggle_chips)
AppCompatImageView ivToggleChips;
@BindView(R.id.chip_view)
View llContainerChips;
@Inject LocationServiceManager locationManager; @Inject LocationServiceManager locationManager;
@Inject NearbyController nearbyController; @Inject NearbyController nearbyController;
@ -229,7 +240,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
/** /**
* Holds all the markers * Holds all the markers
*/ */
private List<NearbyBaseMarker> allMarkers; private List<NearbyBaseMarker> allMarkers =new ArrayList<>();
/**
* WLM URL
*/
public static final String WLM_URL = "https://www.wikilovesmonuments.org/";
@NonNull @NonNull
public static NearbyParentFragment newInstance() { public static NearbyParentFragment newInstance() {
@ -268,6 +284,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
isDarkTheme = systemThemeUtils.isDeviceInNightMode(); isDarkTheme = systemThemeUtils.isDeviceInNightMode();
if (Utils.isMonumentsEnabled(new Date(), applicationKvStore)) {
rlContainerWLMMonthMessage.setVisibility(View.VISIBLE);
} else {
rlContainerWLMMonthMessage.setVisibility(View.GONE);
}
cameraMoveListener= () -> presenter.onCameraMove(mapBox.getCameraPosition().target); cameraMoveListener= () -> presenter.onCameraMove(mapBox.getCameraPosition().target);
addCheckBoxCallback(); addCheckBoxCallback();
presenter.attachView(this); presenter.attachView(this);
@ -491,6 +513,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
setBottomSheetCallbacks(); setBottomSheetCallbacks();
decideButtonVisibilities(); decideButtonVisibilities();
addActionToTitle(); addActionToTitle();
if(!Utils.isMonumentsEnabled(new Date(), applicationKvStore)){
chipWlm.setVisibility(View.GONE);
}
} }
/** /**
@ -598,6 +623,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
Log.d("deneme5","setfilterState"); Log.d("deneme5","setfilterState");
chipNeedsPhoto.setChecked(NearbyFilterState.getInstance().isNeedPhotoSelected()); chipNeedsPhoto.setChecked(NearbyFilterState.getInstance().isNeedPhotoSelected());
chipExists.setChecked(NearbyFilterState.getInstance().isExistsSelected()); chipExists.setChecked(NearbyFilterState.getInstance().isExistsSelected());
chipWlm.setChecked(NearbyFilterState.getInstance().isWlmSelected());
if (NearbyController.currentLocation != null) { if (NearbyController.currentLocation != null) {
presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false); presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
} }
@ -608,13 +634,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
chipNeedsPhoto.setOnCheckedChangeListener((buttonView, isChecked) -> { chipNeedsPhoto.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (NearbyController.currentLocation != null) { if (NearbyController.currentLocation != null) {
checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN); checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN);
if (isChecked) { NearbyFilterState.setNeedPhotoSelected(isChecked);
NearbyFilterState.setNeedPhotoSelected(true); presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
} else {
NearbyFilterState.setNeedPhotoSelected(false);
presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
}
} else { } else {
chipNeedsPhoto.setChecked(!isChecked); chipNeedsPhoto.setChecked(!isChecked);
} }
@ -624,17 +645,23 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
chipExists.setOnCheckedChangeListener((buttonView, isChecked) -> { chipExists.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (NearbyController.currentLocation != null) { if (NearbyController.currentLocation != null) {
checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN); checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN);
if (isChecked) { NearbyFilterState.setExistsSelected(isChecked);
NearbyFilterState.setExistsSelected(true); presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
} else {
NearbyFilterState.setExistsSelected(false);
presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
}
} else { } else {
chipExists.setChecked(!isChecked); chipExists.setChecked(!isChecked);
} }
}); });
chipWlm.setOnCheckedChangeListener((buttonView, isChecked) -> {
if(NearbyController.currentLocation!=null){
checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN);
NearbyFilterState.setWlmSelected(isChecked);
presenter.filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
}else{
chipWlm.setChecked(!isChecked);
}
});
} }
/** /**
@ -852,40 +879,105 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
private void populatePlacesForCurrentLocation(final fr.free.nrw.commons.location.LatLng curlatLng, private void populatePlacesForCurrentLocation(final fr.free.nrw.commons.location.LatLng curlatLng,
final fr.free.nrw.commons.location.LatLng searchLatLng) { final fr.free.nrw.commons.location.LatLng searchLatLng){
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curlatLng, searchLatLng, false, true)) final Observable<NearbyPlacesInfo> nearbyPlacesInfoObservable = Observable
.subscribeOn(Schedulers.io()) .fromCallable(() -> nearbyController
.observeOn(AndroidSchedulers.mainThread()) .loadAttractionsFromLocation(curlatLng, searchLatLng, false, true));
.subscribe(nearbyPlacesInfo -> {
updateMapMarkers(nearbyPlacesInfo, true); Observable<List<Place>> observableWikidataMonuments = Observable.empty();
lastFocusLocation=searchLatLng; if(Utils.isMonumentsEnabled(new Date(), applicationKvStore)){
}, observableWikidataMonuments =
throwable -> { nearbyController
Timber.d(throwable); .queryWikiDataForMonuments(searchLatLng, Locale.getDefault().getLanguage());
showErrorMessage(getString(R.string.error_fetching_nearby_places)+throwable.getLocalizedMessage());
setProgressBarVisibility(false); }
presenter.lockUnlockNearby(false);
setFilterState(); compositeDisposable.add(Observable.zip(nearbyPlacesInfoObservable
})); , observableWikidataMonuments.onErrorReturn(throwable -> {
showErrorMessage(getString(R.string.error_fetching_nearby_monuments) + throwable
.getLocalizedMessage());
return new ArrayList<>();
}),
(nearbyPlacesInfo, monuments) -> {
final List<Place> places = mergeNearbyPlacesAndMonuments(nearbyPlacesInfo.placeList,
monuments);
nearbyPlacesInfo.placeList.clear();
nearbyPlacesInfo.placeList.addAll(places);
return nearbyPlacesInfo;
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(nearbyPlacesInfo -> {
updateMapMarkers(nearbyPlacesInfo, true);
lastFocusLocation=searchLatLng;
},
throwable -> {
Timber.d(throwable);
showErrorMessage(getString(R.string.error_fetching_nearby_places)+throwable.getLocalizedMessage());
setProgressBarVisibility(false);
presenter.lockUnlockNearby(false);
setFilterState();
}));
} }
private void populatePlacesForAnotherLocation(final fr.free.nrw.commons.location.LatLng curlatLng, private void populatePlacesForAnotherLocation(final fr.free.nrw.commons.location.LatLng curlatLng,
final fr.free.nrw.commons.location.LatLng searchLatLng) { final fr.free.nrw.commons.location.LatLng searchLatLng){
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curlatLng, searchLatLng, false, false)) final Observable<NearbyPlacesInfo> nearbyPlacesInfoObservable = Observable
.subscribeOn(Schedulers.io()) .fromCallable(() -> nearbyController
.observeOn(AndroidSchedulers.mainThread()) .loadAttractionsFromLocation(curlatLng, searchLatLng, false, false));
.subscribe(nearbyPlacesInfo -> {
updateMapMarkers(nearbyPlacesInfo, false); Observable<List<Place>> observableWikidataMonuments = Observable.empty();
lastFocusLocation=searchLatLng; if (Utils.isMonumentsEnabled(new Date(), applicationKvStore)) {
}, observableWikidataMonuments = nearbyController
throwable -> { .queryWikiDataForMonuments(searchLatLng, Locale.getDefault().getLanguage());
Timber.d(throwable);
showErrorMessage(getString(R.string.error_fetching_nearby_places)+throwable.getLocalizedMessage()); }
setProgressBarVisibility(false);
presenter.lockUnlockNearby(false); compositeDisposable.add(Observable.zip(nearbyPlacesInfoObservable
})); , observableWikidataMonuments.onErrorReturn(throwable -> {
showErrorMessage(getString(R.string.error_fetching_nearby_monuments) + throwable
.getLocalizedMessage());
return new ArrayList<>();
}),
(nearbyPlacesInfo, monuments) -> {
final List<Place> places = mergeNearbyPlacesAndMonuments(nearbyPlacesInfo.placeList,
monuments);
nearbyPlacesInfo.placeList.clear();
nearbyPlacesInfo.placeList.addAll(places);
return nearbyPlacesInfo;
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(nearbyPlacesInfo -> {
updateMapMarkers(nearbyPlacesInfo, false);
lastFocusLocation=searchLatLng;
},
throwable -> {
Timber.e(throwable);
showErrorMessage(getString(R.string.error_fetching_nearby_places)+throwable.getLocalizedMessage());
setProgressBarVisibility(false);
presenter.lockUnlockNearby(false);
setFilterState();
}));
}
/**
* If a nearby place happens to be a monument as well, don't make the Pin's overlap, instead
* show it as a monument
*
* @param nearbyPlaces
* @param monuments
* @return
*/
private List<Place> mergeNearbyPlacesAndMonuments(List<Place> nearbyPlaces, List<Place> monuments){
List<Place> allPlaces= new ArrayList<>();
allPlaces.addAll(monuments);
for (Place place : nearbyPlaces){
if(!allPlaces.contains(place)){
allPlaces.add(place);
}
}
return allPlaces;
} }
/** /**
@ -1178,7 +1270,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private void removeCurrentLocationMarker() { private void removeCurrentLocationMarker() {
if (currentLocationMarker != null && mapBox!=null) { if (currentLocationMarker != null && mapBox!=null) {
mapBox.removeMarker(currentLocationMarker); mapBox.removeMarker(currentLocationMarker);
mapBox.removePolygon(currentLocationPolygon); if (currentLocationPolygon != null) {
mapBox.removePolygon(currentLocationPolygon);
}
} }
} }
@ -1244,6 +1338,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
public void filterMarkersByLabels(final List<Label> selectedLabels, public void filterMarkersByLabels(final List<Label> selectedLabels,
final boolean displayExists, final boolean displayExists,
final boolean displayNeedsPhoto, final boolean displayNeedsPhoto,
final boolean displayWlm,
final boolean filterForPlaceState, final boolean filterForPlaceState,
final boolean filterForAllNoneType) { final boolean filterForAllNoneType) {
@ -1263,23 +1358,36 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
continue; continue;
} }
if (displayExists && displayNeedsPhoto) { if (!displayWlm && place.isMonument()) {
continue;
}
boolean shouldUpdateMarker=false;
if (displayWlm && place.isMonument()) {
shouldUpdateMarker=true;
}
else if (displayExists && displayNeedsPhoto) {
// Exists and needs photo // Exists and needs photo
if (place.exists && place.pic.trim().isEmpty()) { if (place.exists && place.pic.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation); shouldUpdateMarker=true;
} }
} else if (displayExists && !displayNeedsPhoto) { } else if (displayExists && !displayNeedsPhoto) {
// Exists and all included needs and doesn't needs photo // Exists and all included needs and doesn't needs photo
if (place.exists) { if (place.exists) {
updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation); shouldUpdateMarker=true;
} }
} else if (!displayExists && displayNeedsPhoto) { } else if (!displayExists && displayNeedsPhoto) {
// All and only needs photo // All and only needs photo
if (place.pic.trim().isEmpty()) { if (place.pic.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation); shouldUpdateMarker=true;
} }
} else if (!displayExists && !displayNeedsPhoto) { } else if (!displayExists && !displayNeedsPhoto) {
// all // all
shouldUpdateMarker=true;
}
if(shouldUpdateMarker){
updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation); updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation);
} }
} }
@ -1337,7 +1445,10 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
private @DrawableRes int getIconFor(Place place, Boolean isBookmarked) { private @DrawableRes int getIconFor(Place place, Boolean isBookmarked) {
if (!place.pic.trim().isEmpty()) { if(place.isMonument()){
return R.drawable.ic_custom_map_marker_monuments;
}
else if (!place.pic.trim().isEmpty()) {
return (isBookmarked ? return (isBookmarked ?
R.drawable.ic_custom_map_marker_green_bookmarked : R.drawable.ic_custom_map_marker_green_bookmarked :
R.drawable.ic_custom_map_marker_green); R.drawable.ic_custom_map_marker_green);
@ -1397,7 +1508,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
@Override @Override
public void recenterMap(final fr.free.nrw.commons.location.LatLng curLatLng) { public void recenterMap(fr.free.nrw.commons.location.LatLng curLatLng) {
if (isPermissionDenied || curLatLng == null) { if (isPermissionDenied || curLatLng == null) {
recenterToUserLocation = true; recenterToUserLocation = true;
checkPermissionsAndPerformAction(); checkPermissionsAndPerformAction();
@ -1667,4 +1778,21 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
rlBottomSheetLayoutParams.height = getActivity().getWindowManager().getDefaultDisplay().getHeight() / 16 * 9; rlBottomSheetLayoutParams.height = getActivity().getWindowManager().getDefaultDisplay().getHeight() / 16 * 9;
rlBottomSheet.setLayoutParams(rlBottomSheetLayoutParams); rlBottomSheet.setLayoutParams(rlBottomSheetLayoutParams);
} }
@OnClick(R.id.tv_learn_more)
public void onLearnMoreClicked() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(WLM_URL));
startActivity(intent);
}
@OnClick(R.id.iv_toggle_chips)
public void onToggleChipsClicked() {
if (llContainerChips.getVisibility() == View.VISIBLE) {
llContainerChips.setVisibility(View.GONE);
} else {
llContainerChips.setVisibility(View.VISIBLE);
}
ivToggleChips.setRotation(ivToggleChips.getRotation() + 180);
}
} }

View file

@ -7,6 +7,7 @@ class NearbyResultItem(private val item: ResultTuple?,
private val commonsArticle: ResultTuple?, private val commonsArticle: ResultTuple?,
private val location: ResultTuple?, private val location: ResultTuple?,
private val label: ResultTuple?, private val label: ResultTuple?,
@field:SerializedName("streetAddress") private val address: ResultTuple?,
private val icon: ResultTuple?, @field:SerializedName("class") private val className: ResultTuple?, private val icon: ResultTuple?, @field:SerializedName("class") private val className: ResultTuple?,
@field:SerializedName("classLabel") private val classLabel: ResultTuple?, @field:SerializedName("classLabel") private val classLabel: ResultTuple?,
@field:SerializedName("commonsCategory") private val commonsCategory: ResultTuple?, @field:SerializedName("commonsCategory") private val commonsCategory: ResultTuple?,
@ -66,4 +67,8 @@ class NearbyResultItem(private val item: ResultTuple?,
fun getEndTime(): ResultTuple { fun getEndTime(): ResultTuple {
return endTime ?: ResultTuple() return endTime ?: ResultTuple()
} }
fun getAddress(): String {
return address?.value?:""
}
} }

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.nearby.presenter; package fr.free.nrw.commons.nearby.presenter;
import android.util.Log;
import android.view.View; import android.view.View;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
@ -297,6 +296,7 @@ public class NearbyParentFragmentPresenter
nearbyParentFragmentView.filterMarkersByLabels(selectedLabels, nearbyParentFragmentView.filterMarkersByLabels(selectedLabels,
NearbyFilterState.getInstance().isExistsSelected(), NearbyFilterState.getInstance().isExistsSelected(),
NearbyFilterState.getInstance().isNeedPhotoSelected(), NearbyFilterState.getInstance().isNeedPhotoSelected(),
NearbyFilterState.getInstance().isWlmSelected(),
filterForPlaceState, false); filterForPlaceState, false);
nearbyParentFragmentView.setRecyclerViewAdapterAllSelected(); nearbyParentFragmentView.setRecyclerViewAdapterAllSelected();
break; break;
@ -305,6 +305,7 @@ public class NearbyParentFragmentPresenter
nearbyParentFragmentView.filterMarkersByLabels(selectedLabels, nearbyParentFragmentView.filterMarkersByLabels(selectedLabels,
NearbyFilterState.getInstance().isExistsSelected(), NearbyFilterState.getInstance().isExistsSelected(),
NearbyFilterState.getInstance().isNeedPhotoSelected(), NearbyFilterState.getInstance().isNeedPhotoSelected(),
NearbyFilterState.getInstance().isWlmSelected(),
filterForPlaceState, false); filterForPlaceState, false);
} }
} }

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.repository; package fr.free.nrw.commons.repository;
import androidx.annotation.Nullable;
import fr.free.nrw.commons.category.CategoriesModel; import fr.free.nrw.commons.category.CategoriesModel;
import fr.free.nrw.commons.category.CategoryItem; import fr.free.nrw.commons.category.CategoryItem;
import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.contributions.Contribution;
@ -26,6 +27,7 @@ import java.util.Locale;
import java.util.Set; import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import timber.log.Timber;
/** /**
* The repository class for UploadActivity * The repository class for UploadActivity
@ -280,6 +282,7 @@ public class UploadRepository {
* @param decLongitude * @param decLongitude
* @return * @return
*/ */
@Nullable
public Place checkNearbyPlaces(double decLatitude, double decLongitude) { public Place checkNearbyPlaces(double decLatitude, double decLongitude) {
try { try {
List<Place> fromWikidataQuery = nearbyPlaces.getFromWikidataQuery(new LatLng( List<Place> fromWikidataQuery = nearbyPlaces.getFromWikidataQuery(new LatLng(
@ -287,7 +290,8 @@ public class UploadRepository {
Locale.getDefault().getLanguage(), Locale.getDefault().getLanguage(),
NEARBY_RADIUS_IN_KILO_METERS); NEARBY_RADIUS_IN_KILO_METERS);
return fromWikidataQuery.size() > 0 ? fromWikidataQuery.get(0) : null; return fromWikidataQuery.size() > 0 ? fromWikidataQuery.get(0) : null;
} catch (IOException e) { }catch (final Exception e) {
Timber.e("Error fetching nearby places: %s", e.getMessage());
return null; return null;
} }
} }

View file

@ -8,11 +8,13 @@ import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference; import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroupAdapter; import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
@ -25,6 +27,7 @@ import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.maps.TelemetryDefinition; import com.mapbox.mapboxsdk.maps.TelemetryDefinition;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.campaigns.CampaignView;
import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.ApplicationlessInjection; import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
@ -104,7 +107,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
findPreference("displayNearbyCardView").setEnabled(false); findPreference("displayNearbyCardView").setEnabled(false);
findPreference("descriptionDefaultLanguagePref").setEnabled(false); findPreference("descriptionDefaultLanguagePref").setEnabled(false);
findPreference("displayLocationPermissionForCardView").setEnabled(false); findPreference("displayLocationPermissionForCardView").setEnabled(false);
findPreference("displayCampaignsCardView").setEnabled(false); findPreference(CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE).setEnabled(false);
} }
findPreference("telemetryOptOut").setOnPreferenceChangeListener( findPreference("telemetryOptOut").setOnPreferenceChangeListener(
@ -127,7 +130,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
} }
@Override @Override
protected Adapter onCreateAdapter(PreferenceScreen preferenceScreen) { protected Adapter onCreateAdapter(final PreferenceScreen preferenceScreen) {
return new PreferenceGroupAdapter(preferenceScreen) { return new PreferenceGroupAdapter(preferenceScreen) {
@Override @Override
public void onBindViewHolder(PreferenceViewHolder holder, int position) { public void onBindViewHolder(PreferenceViewHolder holder, int position) {

View file

@ -13,6 +13,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import timber.log.Timber;
class PageContentsCreator { class PageContentsCreator {
@ -29,13 +30,15 @@ class PageContentsCreator {
this.context = context; this.context = context;
} }
public String createFrom(Contribution contribution) { public String createFrom(Contribution contribution, String countryCode) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
final Media media = contribution.getMedia(); final Media media = contribution.getMedia();
buffer buffer
.append("== {{int:filedesc}} ==\n") .append("== {{int:filedesc}} ==\n")
.append("{{Information\n") .append("{{Information\n")
.append("|description=").append(media.getFallbackDescription()).append("\n") .append("|description=").append(media.getFallbackDescription())
.append("{{ on Wikidata|").append(contribution.getWikidataPlace().getId()).append("}}")
.append("\n")
.append("|source=").append("{{own}}\n") .append("|source=").append("{{own}}\n")
.append("|author=[[User:").append(media.getAuthor()).append("|") .append("|author=[[User:").append(media.getAuthor()).append("|")
.append(media.getAuthor()).append("]]\n"); .append(media.getAuthor()).append("]]\n");
@ -48,6 +51,12 @@ class PageContentsCreator {
buffer.append("}}").append("\n"); buffer.append("}}").append("\n");
if (contribution.getWikidataPlace()!=null && contribution.getWikidataPlace().isMonumentUpload()) {
buffer.append("{{Wiki Loves Monuments 2021|1= ")
.append(countryCode)
.append("}}").append("\n");
}
//Only add Location template (e.g. {{Location|37.51136|-77.602615}} ) if coords is not null //Only add Location template (e.g. {{Location|37.51136|-77.602615}} ) if coords is not null
final String decimalCoords = contribution.getDecimalCoords(); final String decimalCoords = contribution.getDecimalCoords();
if (decimalCoords != null) { if (decimalCoords != null) {
@ -66,6 +75,7 @@ class PageContentsCreator {
} else { } else {
buffer.append("{{subst:unc}}"); buffer.append("{{subst:unc}}");
} }
Timber.d("Template: %s", buffer.toString());
return buffer.toString(); return buffer.toString();
} }

View file

@ -261,6 +261,11 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
return fragments.size(); return fragments.size();
} }
@Override
public boolean isWLMUpload() {
return place!=null && place.isMonument();
}
@Override @Override
public void showMessage(int messageResourceId) { public void showMessage(int messageResourceId) {
ViewUtil.showLongToast(this, messageResourceId); ViewUtil.showLongToast(this, messageResourceId);
@ -372,6 +377,11 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
public int getTotalNumberOfSteps() { public int getTotalNumberOfSteps() {
return fragments.size(); return fragments.size();
} }
@Override
public boolean isWLMUpload() {
return place!=null && place.isMonument();
}
}); });
fragments.add(uploadMediaDetailFragment); fragments.add(uploadMediaDetailFragment);
} }

View file

@ -37,5 +37,6 @@ public class UploadBaseFragment extends CommonsDaggerSupportFragment {
int getTotalNumberOfSteps(); int getTotalNumberOfSteps();
boolean isWLMUpload();
} }
} }

View file

@ -16,9 +16,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -212,11 +210,11 @@ public class UploadClient {
public Observable<UploadResult> uploadFileFromStash( public Observable<UploadResult> uploadFileFromStash(
final Contribution contribution, final Contribution contribution,
final String uniqueFileName, final String uniqueFileName,
final String fileKey) { final String fileKey, @Nullable String countryCode) {
try { try {
return uploadInterface return uploadInterface
.uploadFileFromStash(csrfTokenClient.getTokenBlocking(), .uploadFileFromStash(csrfTokenClient.getTokenBlocking(),
pageContentsCreator.createFrom(contribution), pageContentsCreator.createFrom(contribution, countryCode),
CommonsApplication.DEFAULT_EDIT_SUMMARY, CommonsApplication.DEFAULT_EDIT_SUMMARY,
uniqueFileName, uniqueFileName,
fileKey).map(uploadResponse -> { fileKey).map(uploadResponse -> {

View file

@ -1,6 +1,7 @@
package fr.free.nrw.commons.upload package fr.free.nrw.commons.upload
import android.os.Parcelable import android.os.Parcelable
import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
@ -9,14 +10,18 @@ data class WikidataPlace(
override val id: String, override val id: String,
override val name: String, override val name: String,
val imageValue: String?, val imageValue: String?,
val wikipediaArticle: String? val wikipediaArticle: String?,
val location: LatLng? = null,
val isMonumentUpload : Boolean =false
) : ) :
WikidataItem, Parcelable { WikidataItem, Parcelable {
constructor(place: Place) : this( constructor(place: Place) : this(
place.wikiDataEntityId!!, place.wikiDataEntityId!!,
place.name, place.name,
place.pic.takeIf { it.isNotBlank() }, place.pic.takeIf { it.isNotBlank() },
place.siteLinks.wikipediaLink?.toString() ?: "" place.siteLinks.wikipediaLink?.toString() ?: "",
place.location,
isMonumentUpload=place.isMonument
) )
companion object { companion object {

View file

@ -16,6 +16,7 @@ import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -48,6 +49,8 @@ public class MediaLicenseFragment extends UploadBaseFragment implements MediaLic
TextView tvShareLicenseSummary; TextView tvShareLicenseSummary;
@BindView(R.id.tooltip) @BindView(R.id.tooltip)
ImageView tooltip; ImageView tooltip;
@BindView(R.id.ll_info_monument_upload)
LinearLayout llInfoMonumentUpload;
@Inject @Inject
MediaLicenseContract.UserActionListener presenter; MediaLicenseContract.UserActionListener presenter;
@ -87,6 +90,16 @@ public class MediaLicenseFragment extends UploadBaseFragment implements MediaLic
initPresenter(); initPresenter();
initLicenseSpinner(); initLicenseSpinner();
presenter.getLicenses(); presenter.getLicenses();
/**
* Show the wlm info message if the upload is a WLM upload
*/
if(callback.isWLMUpload()){
//TODO : Update the info message logo
llInfoMonumentUpload.setVisibility(View.VISIBLE);
}else{
llInfoMonumentUpload.setVisibility(View.GONE);
}
} }
/** /**

View file

@ -3,12 +3,12 @@ package fr.free.nrw.commons.upload.worker
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.location.Geocoder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.work.CoroutineWorker import androidx.work.CoroutineWorker
import androidx.work.Data import androidx.work.Data
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.google.gson.Gson
import com.mapbox.mapboxsdk.plugins.localization.BuildConfig import com.mapbox.mapboxsdk.plugins.localization.BuildConfig
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication
@ -19,6 +19,7 @@ import fr.free.nrw.commons.contributions.ChunkInfo
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
import fr.free.nrw.commons.contributions.ContributionDao import fr.free.nrw.commons.contributions.ContributionDao
import fr.free.nrw.commons.di.ApplicationlessInjection import fr.free.nrw.commons.di.ApplicationlessInjection
import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.upload.StashUploadState import fr.free.nrw.commons.upload.StashUploadState
import fr.free.nrw.commons.upload.UploadClient import fr.free.nrw.commons.upload.UploadClient
@ -30,6 +31,7 @@ import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import timber.log.Timber import timber.log.Timber
import java.io.IOException
import java.util.* import java.util.*
import java.util.regex.Pattern import java.util.regex.Pattern
import javax.inject.Inject import javax.inject.Inject
@ -275,8 +277,16 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
try { try {
//Upload the file from stash //Upload the file from stash
var countryCode: String? =null
with(contribution.wikidataPlace?.location){
if(contribution.wikidataPlace?.isMonumentUpload!!) {
countryCode =
reverseGeoCode(contribution.wikidataPlace?.location!!)?.toLowerCase()
}
}
val uploadResult = uploadClient.uploadFileFromStash( val uploadResult = uploadClient.uploadFileFromStash(
contribution, uniqueFileName, stashUploadResult.fileKey contribution, uniqueFileName, stashUploadResult.fileKey, countryCode
).blockingSingle() ).blockingSingle()
if (uploadResult.isSuccessful()) { if (uploadResult.isSuccessful()) {
@ -339,6 +349,26 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
} }
} }
private fun reverseGeoCode(latLng: LatLng): String? {
val geocoder = Geocoder(
CommonsApplication.getInstance().applicationContext, Locale
.getDefault()
)
try {
val addresses =
geocoder.getFromLocation(latLng.latitude, latLng.longitude, 1)
for (address in addresses) {
if (address != null && address.locale.isO3Country != null) {
return address.locale.country
}
}
} catch (e: IOException) {
Timber.e(e)
}
return null
}
private fun clearChunks(contribution: Contribution) { private fun clearChunks(contribution: Contribution) {
contribution.chunkInfo=null contribution.chunkInfo=null
contributionDao.saveSynchronous(contribution) contributionDao.saveSynchronous(contribution)

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -37,6 +37,14 @@
app:chipBackgroundColor="@color/bg_chip_state" app:chipBackgroundColor="@color/bg_chip_state"
android:text="@string/place_state_needs_photo"/> android:text="@string/place_state_needs_photo"/>
<com.google.android.material.chip.Chip
android:id="@+id/choice_chip_wlm"
style="@style/Widget.MaterialComponents.Chip.Filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipBackgroundColor="@color/bg_chip_state"
android:text="@string/place_state_wlm"/>
</com.google.android.material.chip.ChipGroup> </com.google.android.material.chip.ChipGroup>
</LinearLayout> </LinearLayout>

View file

@ -3,9 +3,9 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginBottom="@dimen/activity_margin_horizontal" android:layout_marginBottom="@dimen/activity_margin_horizontal"
android:padding="@dimen/standard_gap" android:padding="@dimen/standard_gap">
>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -67,8 +67,29 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:padding="10dp"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_info_monument_upload"
android:visibility="gone"
tools:visibility="visible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="96dp"
android:layout_height="96dp"
app:srcCompat="@drawable/ic_custom_map_marker_monuments"/>
<androidx.appcompat.widget.AppCompatTextView
android:layout_marginLeft="10dp"
android:layout_width="match_parent"
android:text="@string/wlm_upload_info"
android:layout_height="wrap_content"/>
</LinearLayout>
<fr.free.nrw.commons.ui.widget.HtmlTextView <fr.free.nrw.commons.ui.widget.HtmlTextView
android:id="@+id/tv_media_upload_policy" android:id="@+id/tv_media_upload_policy"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout" android:id="@+id/coordinator_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -15,6 +16,35 @@
layout="@layout/nearby_filter_all_items" layout="@layout/nearby_filter_all_items"
android:id="@+id/nearby_filter"/> android:id="@+id/nearby_filter"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:gravity="center"
android:id="@+id/rl_container_wlm_month_message"
android:background="@color/white"
tools:visibility="visible"
android:visibility="gone"
android:layout_below="@id/nearby_filter">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/tv_learn_more"
android:textStyle="bold"
android:textColor="@color/secondaryTextColor"
android:text="@string/wlm_month_message"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_learn_more"
android:textColor="@color/status_bar_blue"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_alignParentEnd="true"
android:text="@string/learn_more"
android:layout_height="wrap_content">
</androidx.appcompat.widget.AppCompatTextView>
</RelativeLayout>
<include layout="@layout/nearby_filter_list" <include layout="@layout/nearby_filter_list"
android:id="@+id/nearby_filter_list" android:id="@+id/nearby_filter_list"
android:layout_below="@id/nearby_filter" android:layout_below="@id/nearby_filter"
@ -28,7 +58,7 @@
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@id/nearby_filter"> android:layout_below="@id/rl_container_wlm_month_message">
<com.mapbox.mapboxsdk.maps.MapView <com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/map_view" android:id="@+id/map_view"
@ -69,7 +99,8 @@
android:textColor="@color/status_bar_blue" android:textColor="@color/status_bar_blue"
android:visibility="gone" android:visibility="gone"
app:elevation="@dimen/dimen_6" app:elevation="@dimen/dimen_6"
android:layout_below="@id/nearby_filter"/> android:layout_below="@id/rl_container_wlm_month_message"
/>
<View <View
android:id="@+id/transparentView" android:id="@+id/transparentView"
@ -88,7 +119,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_below="@id/nearby_filter" android:layout_below="@id/rl_container_wlm_month_message"
android:clickable="true" android:clickable="true"
android:visibility="visible" android:visibility="visible"
app:backgroundTint="@color/main_background_light" app:backgroundTint="@color/main_background_light"

View file

@ -1,22 +1,35 @@
<LinearLayout android:layout_width="match_parent" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/status_bar_blue">
<include
android:layout_toStartOf="@+id/iv_toggle_chips"
android:id="@+id/chip_view"
layout="@layout/filter_chip_view"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent" android:layout_width="match_parent"
android:orientation="vertical" android:background="@color/deleteRed" />
xmlns:android="http://schemas.android.com/apk/res/android">
<include <include
android:id="@+id/chip_view" android:layout_toStartOf="@+id/iv_toggle_chips"
layout="@layout/filter_chip_view" android:id="@+id/search_view_layout"
android:background="@color/deleteRed" layout="@layout/filter_search_view_layout"
/> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/chip_view" />
<include android:id="@+id/search_view_layout" layout="@layout/filter_search_view_layout" /> <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_toggle_chips"
android:layout_width="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_expand_less_black_24dp"
android:tint="@color/white"
android:layout_height="wrap_content"
android:padding="12dp"
android:layout_gravity="center" />
<RelativeLayout </RelativeLayout>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/search_view_layout"
android:background="@color/status_bar_blue">
</RelativeLayout>
</LinearLayout>

View file

@ -323,6 +323,7 @@
<string name="share_app_title">Share App</string> <string name="share_app_title">Share App</string>
<string name="error_fetching_nearby_places">Error fetching nearby places.</string> <string name="error_fetching_nearby_places">Error fetching nearby places.</string>
<string name="error_fetching_nearby_monuments">Error fetching nearby monuments.</string>
<string name="no_recent_searches">No recent searches</string> <string name="no_recent_searches">No recent searches</string>
<string name="delete_recent_searches_dialog">Are you sure you want to clear your search history?</string> <string name="delete_recent_searches_dialog">Are you sure you want to clear your search history?</string>
<string name="delete_search_dialog">Do you want to delete this search?</string> <string name="delete_search_dialog">Do you want to delete this search?</string>
@ -638,5 +639,12 @@ Upload your first media by tapping on the add button.</string>
The shadow of the image view of the location picker</string> The shadow of the image view of the location picker</string>
<string name="image_location">Image Location</string> <string name="image_location">Image Location</string>
<string name="check_whether_location_is_correct">Check whether location is correct</string> <string name="check_whether_location_is_correct">Check whether location is correct</string>
<string name="place_state_wlm">WLM</string>
<string name="wlm_upload_info">You are contributing to Wiki Loves Monuments Campaign. Related templates will be added accordingly.</string>
<string name="display_monuments">Display monuments</string>
<string name="wlm_month_message">It\'s Wiki Loves Monuments month!</string>
<string name="learn_more">LEARN MORE</string>
<string name="wlm_campaign_title">Wiki Loves Monuments</string>
<string name="wlm_campaign_description">Wiki Loves Monuments is an international photo contest for monuments organised by Wikimedia</string>
</resources> </resources>

View file

@ -70,7 +70,6 @@
app:singleLineTitle="false" app:singleLineTitle="false"
android:summary="@string/display_campaigns_explanation" android:summary="@string/display_campaigns_explanation"
android:title="@string/display_campaigns" /> android:title="@string/display_campaigns" />
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <PreferenceCategory

View file

@ -0,0 +1,60 @@
SELECT
(SAMPLE(?location) as ?location)
?item
(SAMPLE(COALESCE(?itemLabelPreferredLanguage, ?itemLabelAnyLanguage)) as ?label)
(SAMPLE(COALESCE(?itemDescriptionPreferredLanguage, ?itemDescriptionAnyLanguage, "?")) as ?description)
(SAMPLE(?classId) as ?class)
(SAMPLE(COALESCE(?classLabelPreferredLanguage, ?classLabelAnyLanguage, "?")) as ?classLabel)
(SAMPLE(COALESCE(?icon0, ?icon1)) as ?icon)
?wikipediaArticle
?commonsArticle
(SAMPLE(?commonsCategory) as ?commonsCategory)
(SAMPLE(?pic) as ?pic)
WHERE {
# Around given location...
SERVICE wikibase:around {
?item wdt:P625 ?location.
bd:serviceParam wikibase:center "Point(${LONG} ${LAT})"^^geo:wktLiteral.
bd:serviceParam wikibase:radius "${RAD}" . # Radius in kilometers.
}
{ ?item p:P1435 ?monument } UNION { ?item p:P2186 ?monument } UNION { ?item p:P1459 ?monument } UNION { ?item p:P1460 ?monument } UNION { ?item p:P1216 ?monument } UNION { ?item p:P709 ?monument } UNION { ?item p:P718 ?monument } UNION { ?item p:P5694 ?monument }
# Get Commons category (P373)
OPTIONAL { ?item wdt:P373 ?commonsCategory. }
# Get (P18)
OPTIONAL { ?item wdt:P18 ?pic. }
# Get the label in the preferred language of the user, or any other language if no label is available in that language.
OPTIONAL {?item rdfs:label ?itemLabelPreferredLanguage. FILTER (lang(?itemLabelPreferredLanguage) = "${LANG}")}
OPTIONAL {?item rdfs:label ?itemLabelAnyLanguage}
# Get the description in the preferred language of the user, or any other language if no description is available in that language.
OPTIONAL {?item schema:description ?itemDescriptionPreferredLanguage. FILTER (lang(?itemDescriptionPreferredLanguage) = "${LANG}")}
OPTIONAL {?item schema:description ?itemDescriptionAnyLanguage }
# Get the class label in the preferred language of the user, or any other language if no label is available in that language.
OPTIONAL {
?item p:P31/ps:P31 ?classId.
OPTIONAL {?classId rdfs:label ?classLabelPreferredLanguage. FILTER (lang(?classLabelPreferredLanguage) = "")}
OPTIONAL {?classId rdfs:label ?classLabelAnyLanguage}
OPTIONAL {
?wikipediaArticle schema:about ?item ;
schema:isPartOf <https://.wikipedia.org/> .
}
OPTIONAL {
?wikipediaArticle schema:about ?item ;
schema:isPartOf <https://en.wikipedia.org/> .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
}
OPTIONAL {
?commonsArticle schema:about ?item ;
schema:isPartOf <https://commons.wikimedia.org/> .
SERVICE wikibase:label { bd:serviceParam wikibase:language "en" }
}
}
}
GROUP BY ?item ?wikipediaArticle ?commonsArticle

View file

@ -1,9 +1,6 @@
package fr.free.nrw.commons.bookmarks.locations package fr.free.nrw.commons.bookmarks.locations
import android.net.Uri
import com.nhaarman.mockitokotlin2.whenever import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.bookmarks.Bookmark
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import org.junit.Assert import org.junit.Assert
import org.junit.Before import org.junit.Before
@ -33,8 +30,23 @@ class BookmarkLocationControllerTest {
private val mockBookmarkList: List<Place> private val mockBookmarkList: List<Place>
private get() { private get() {
val list = ArrayList<Place>() val list = ArrayList<Place>()
list.add(Place("en","a place",null,"a description",null,"a cat",null,null,true)) list.add(
list.add(Place("en","another place",null,"another description",null,"another cat",null,null,true)) Place(
"en", "a place", null, "a description", null, "a cat", null, null, true)
)
list.add(
Place(
"en",
"another place",
null,
"another description",
null,
"another cat",
null,
null,
true
)
)
return list return list
} }

View file

@ -77,7 +77,18 @@ class BookmarkLocationFragmentUnitTests {
private val mockBookmarkList: List<Place> private val mockBookmarkList: List<Place>
private get() { private get() {
val list = ArrayList<Place>() val list = ArrayList<Place>()
list.add(Place("en","a place",null,"a description",null,"a cat",null,null,true)) list.add(
Place(
"en",
"a place",
null,
"a description",
null,
"a cat",
null,
null,
true)
)
return list return list
} }

View file

@ -265,7 +265,14 @@ class NearbyParentFragmentPresenterTest {
fun testFilterByMarkerTypeMultiSelectCHECKED() { fun testFilterByMarkerTypeMultiSelectCHECKED() {
val state = CheckBoxTriStates.CHECKED val state = CheckBoxTriStates.CHECKED
nearbyPresenter.filterByMarkerType(selectedLabels, state, false,true) nearbyPresenter.filterByMarkerType(selectedLabels, state, false,true)
verify(nearbyParentFragmentView).filterMarkersByLabels(ArgumentMatchers.anyList(),ArgumentMatchers.anyBoolean(),ArgumentMatchers.anyBoolean(),ArgumentMatchers.anyBoolean(),ArgumentMatchers.anyBoolean()); verify(nearbyParentFragmentView).filterMarkersByLabels(
ArgumentMatchers.anyList(),
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.anyBoolean(),
ArgumentMatchers.anyBoolean()
);
verify(nearbyParentFragmentView).setRecyclerViewAdapterAllSelected() verify(nearbyParentFragmentView).setRecyclerViewAdapterAllSelected()
verifyNoMoreInteractions(nearbyParentFragmentView) verifyNoMoreInteractions(nearbyParentFragmentView)
} }
@ -276,7 +283,14 @@ class NearbyParentFragmentPresenterTest {
@Test @Test
fun testFilterByMarkerTypeSingleSelect() { fun testFilterByMarkerTypeSingleSelect() {
nearbyPresenter.filterByMarkerType(selectedLabels, 0, true,false) nearbyPresenter.filterByMarkerType(selectedLabels, 0, true,false)
verify(nearbyParentFragmentView).filterMarkersByLabels(any(), any(), any(), any(), any()); verify(nearbyParentFragmentView).filterMarkersByLabels(
any(),
any(),
any(),
any(),
any(),
any()
);
verifyNoMoreInteractions(nearbyParentFragmentView) verifyNoMoreInteractions(nearbyParentFragmentView)
} }