Fixes #1450 Add filter to nearby (#3178)

* Add layout items for nearby filter list and filter item

(cherry picked from commit b96f8a68ce)

* Edit nearby query

(cherry picked from commit 1f3c8c8deb)

* Add filter items to nearby parent fragment xml

(cherry picked from commit d0beadd0e0)

* Add icon for green marker

(cherry picked from commit f65ca0387a)

* Add layout util file to organize layout utilities

(cherry picked from commit 5c57939245)

* Add pic parameter to nearby result item

(cherry picked from commit 86007e4bb6)

* Add pic parameter to place type

(cherry picked from commit 25c358b67f)

* Add green marker styling

(cherry picked from commit 929c92d887)

* Inıt search layouts on Nearby parent

(cherry picked from commit 2ac38a1919)

* Style green markers at nearby controller

(cherry picked from commit 3e08f39f8e)

* Edit bookmark daos to include pic to tables

(cherry picked from commit 48d69edf3b)

* TODO foc bookmark dao item but works now

(cherry picked from commit f748399720)

* Style nearby filter area

(cherry picked from commit 6267e488b0)

* fix style of filter tab

(cherry picked from commit 5f843bf366)

* Add nearby list adapter

(cherry picked from commit 56334afe03)

* Current status, list size is working, visibility working, filter mechanism is ready

(cherry picked from commit 7d75c9c589)

* Filtering works

(cherry picked from commit 8a13cf7728)

* Filter function works

(cherry picked from commit 78368a2c0c)

* Fix style issues

(cherry picked from commit 2204f70255)

* Add divider to recyclerview

(cherry picked from commit c8100b55d7)

* Hide bottom sheets accordingly

(cherry picked from commit c5deba8b0b)

* Code cleanup

(cherry picked from commit cf8f64f3cb)

* Add actions to chips

* Fetch destroyed information from servers

* Add destroyed places icon

* Make chip filter functions work

* Revert irrelevant changes

* Revert accidentally replaced strings

* Remove unneeded lines

* Remove only places expalanation from trings

* Do not filter if nearby places are not loaded

* Add triple checkbox for add none and neutral

* Make tri checkbox work

* Fix travis issue

* Add coments

* fix search this area button locaton

* Set initial place type state and recover it between each populate places
This commit is contained in:
neslihanturan 2019-11-28 11:22:32 +03:00 committed by Josephine Lim
parent 5c6ab3b253
commit b3c11842f3
35 changed files with 1264 additions and 66 deletions

View file

@ -6,6 +6,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.RemoteException;
import androidx.annotation.NonNull;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import java.util.ArrayList;
import java.util.List;
@ -156,8 +157,11 @@ public class BookmarkLocationsDao {
cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)),
location,
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CATEGORY)),
builder.build()
builder.build(),
null,
null
);
// TODO: add pic and destroyed to bookmark location dao
}
private ContentValues toContentValues(Place bookmarkLocation) {
@ -172,6 +176,7 @@ public class BookmarkLocationsDao {
cv.put(BookmarkLocationsDao.Table.COLUMN_COMMONS_LINK, bookmarkLocation.siteLinks.getCommonsLink().toString());
cv.put(BookmarkLocationsDao.Table.COLUMN_LAT, bookmarkLocation.location.getLatitude());
cv.put(BookmarkLocationsDao.Table.COLUMN_LONG, bookmarkLocation.location.getLongitude());
cv.put(BookmarkLocationsDao.Table.COLUMN_PIC, bookmarkLocation.pic);
return cv;
}
@ -189,6 +194,7 @@ public class BookmarkLocationsDao {
static final String COLUMN_WIKIPEDIA_LINK = "location_wikipedia_link";
static final String COLUMN_WIKIDATA_LINK = "location_wikidata_link";
static final String COLUMN_COMMONS_LINK = "location_commons_link";
static final String COLUMN_PIC = "location_pic";
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
public static final String[] ALL_FIELDS = {
@ -202,7 +208,8 @@ public class BookmarkLocationsDao {
COLUMN_IMAGE_URL,
COLUMN_WIKIPEDIA_LINK,
COLUMN_WIKIDATA_LINK,
COLUMN_COMMONS_LINK
COLUMN_COMMONS_LINK,
COLUMN_PIC
};
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
@ -218,7 +225,8 @@ public class BookmarkLocationsDao {
+ COLUMN_IMAGE_URL + " STRING,"
+ COLUMN_WIKIPEDIA_LINK + " STRING,"
+ COLUMN_WIKIDATA_LINK + " STRING,"
+ COLUMN_COMMONS_LINK + " STRING"
+ COLUMN_COMMONS_LINK + " STRING,"
+ COLUMN_PIC + " STRING"
+ ");";
public static void onCreate(SQLiteDatabase db) {

View file

@ -0,0 +1,195 @@
package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.widget.CompoundButton;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatCheckBox;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
/**
* Base on https://stackoverflow.com/a/40939367/3950497 answer.
*/
public class CheckBoxTriStates extends AppCompatCheckBox {
static public final int UNKNOWN = -1;
static public final int UNCHECKED = 0;
static public final int CHECKED = 1;
private int state;
/**
* This is the listener set to the super class which is going to be evoke each
* time the check state has changed.
*/
private final OnCheckedChangeListener privateListener = new CompoundButton.OnCheckedChangeListener() {
// checkbox status is changed from uncheck to checked.
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
switch (state) {
case UNKNOWN:
setState(UNCHECKED);;
break;
case UNCHECKED:
setState(CHECKED);
break;
case CHECKED:
setState(UNKNOWN);
break;
}
}
};
/**
* Holds a reference to the listener set by a client, if any.
*/
private OnCheckedChangeListener clientListener;
/**
* This flag is needed to avoid accidentally changing the current {@link #state} when
* {@link #onRestoreInstanceState(Parcelable)} calls {@link #setChecked(boolean)}
* evoking our {@link #privateListener} and therefore changing the real state.
*/
private boolean restoring;
public CheckBoxTriStates(Context context) {
super(context);
init();
}
public CheckBoxTriStates(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public CheckBoxTriStates(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public int getState() {
return state;
}
public void setState(int state) {
if(!this.restoring && this.state != state) {
this.state = state;
if(this.clientListener != null) {
this.clientListener.onCheckedChanged(this, this.isChecked());
}
if (NearbyController.currentLocation != null) {
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(null, state, false, true);
}
updateBtn();
}
}
@Override
public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) {
// we never truly set the listener to the client implementation, instead we only hold
// a reference to it and evoke it when needed.
if(this.privateListener != listener) {
this.clientListener = listener;
}
// always use our implementation
super.setOnCheckedChangeListener(privateListener);
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.state = state;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
this.restoring = true; // indicates that the ui is restoring its state
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setState(ss.state);
requestLayout();
this.restoring = false;
}
private void init() {
state = UNKNOWN;
updateBtn();
}
public void addAction() {
setOnCheckedChangeListener(this.privateListener);
}
private void updateBtn() {
int btnDrawable = R.drawable.ic_indeterminate_check_box_black_24dp;
switch (state) {
case UNKNOWN:
btnDrawable = R.drawable.ic_indeterminate_check_box_black_24dp;
break;
case UNCHECKED:
btnDrawable = R.drawable.ic_check_box_outline_blank_black_24dp;
break;
case CHECKED:
btnDrawable = R.drawable.ic_check_box_black_24dp;
break;
}
setButtonDrawable(btnDrawable);
}
static class SavedState extends BaseSavedState {
int state;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
state = in.readInt();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeValue(state);
}
@Override
public String toString() {
return "CheckboxTriState.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " state=" + state + "}";
}
@SuppressWarnings("hiding")
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}

View file

@ -42,7 +42,7 @@ public enum Label {
TEMPLE("Q44539", R.drawable.round_icon_church),
UNKNOWN("?", R.drawable.round_icon_unknown);
private static final Map<String, Label> TEXT_TO_DESCRIPTION
public static final Map<String, Label> TEXT_TO_DESCRIPTION
= new HashMap<>(Label.values().length);
static {
@ -54,6 +54,7 @@ public enum Label {
private final String text;
@DrawableRes
private final int icon;
private boolean selected;
Label(String text, @DrawableRes int icon) {
this.text = text;
@ -65,6 +66,18 @@ public enum Label {
this.icon = in.readInt();
}
/**
* Will be used for nearby filter, to determine if place type is selected or not
* @param isSelected true if user selected the place type
*/
public void setSelected(boolean isSelected) {
this.selected = isSelected;
}
public boolean isSelected() {
return selected;
}
public String getText() {
return text;
}

View file

@ -0,0 +1,31 @@
package fr.free.nrw.commons.nearby;
import com.mapbox.mapboxsdk.annotations.Marker;
/**
* This class groups visual map item Marker with the reated data of displayed place and information
* of bookmark
*/
public class MarkerPlaceGroup {
private Marker marker; // Marker item from the map
private boolean isBookmarked; // True if user bookmarked the place
private Place place; // Place of the location displayed by the marker
public MarkerPlaceGroup(Marker marker, boolean isBookmarked, Place place) {
this.marker = marker;
this.isBookmarked = isBookmarked;
this.place = place;
}
public Marker getMarker() {
return marker;
}
public Place getPlace() {
return place;
}
public boolean getIsBookmarked() {
return isBookmarked;
}
}

View file

@ -6,6 +6,7 @@ import android.graphics.Bitmap;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.Marker;
import java.io.IOException;
import java.util.ArrayList;
@ -33,6 +34,10 @@ public class NearbyController {
public static LatLng latestSearchLocation; // Can be current and camera target on search this area button is used
public static double latestSearchRadius = 10.0; // Any last search radius except closest result search
public static List<MarkerPlaceGroup> markerLabelList = new ArrayList<>();
public static Map<Boolean, Marker> markerExistsMap;
public static Map<Boolean, Marker> markerNeedPicMap;
@Inject
public NearbyController(NearbyPlaces nearbyPlaces) {
this.nearbyPlaces = nearbyPlaces;
@ -158,6 +163,8 @@ public class NearbyController {
placeList = placeList.subList(0, Math.min(placeList.size(), MAX_RESULTS));
VectorDrawableCompat vectorDrawable = null;
VectorDrawableCompat vectorDrawableGreen = null;
VectorDrawableCompat vectorDrawableGrey = null;
try {
vectorDrawable = VectorDrawableCompat.create(
context.getResources(), R.drawable.ic_custom_bookmark_marker, context.getTheme()
@ -191,13 +198,18 @@ public class NearbyController {
vectorDrawable = null;
try {
vectorDrawable = VectorDrawableCompat.create(
context.getResources(), R.drawable.ic_custom_map_marker, context.getTheme()
);
context.getResources(), R.drawable.ic_custom_map_marker, context.getTheme());
vectorDrawableGreen = VectorDrawableCompat.create(
context.getResources(), R.drawable.ic_custom_map_marker_green, context.getTheme());
vectorDrawableGrey = VectorDrawableCompat.create(
context.getResources(), R.drawable.ic_custom_map_marker_grey, context.getTheme());
} catch (Resources.NotFoundException e) {
// ignore when running tests.
}
if (vectorDrawable != null) {
Bitmap icon = UiUtils.getBitmap(vectorDrawable);
Bitmap iconGreen = UiUtils.getBitmap(vectorDrawableGreen);
Bitmap iconGrey = UiUtils.getBitmap(vectorDrawableGrey);
for (Place place : placeList) {
String distance = formatDistanceBetween(curLatLng, place.location);
@ -210,8 +222,21 @@ public class NearbyController {
place.location.getLatitude(),
place.location.getLongitude()));
nearbyBaseMarker.place(place);
// Check if string is only spaces or empty, if so place doesn't have any picture
if (!place.pic.trim().isEmpty()) {
if (iconGreen != null) {
nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(iconGreen));
}
} else if (!place.destroyed.trim().isEmpty()) { // Means place is destroyed
if (iconGrey != null) {
nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(iconGrey));
}
} else {
nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(icon));
}
baseMarkerOptions.add(nearbyBaseMarker);
}

View file

@ -0,0 +1,174 @@
package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
public class NearbyFilterSearchRecyclerViewAdapter
extends RecyclerView.Adapter<NearbyFilterSearchRecyclerViewAdapter.RecyclerViewHolder>
implements Filterable {
private final LayoutInflater inflater;
private Context context;
private RecyclerView recyclerView;
private ArrayList<Label> labels;
private ArrayList<Label> displayedLabels;
public ArrayList<Label> selectedLabels = new ArrayList<>();
private int state;
RecyclerView.SmoothScroller smoothScroller;
public NearbyFilterSearchRecyclerViewAdapter(Context context, ArrayList<Label> labels, RecyclerView recyclerView) {
this.context = context;
this.labels = labels;
this.displayedLabels = labels;
this.recyclerView = recyclerView;
smoothScroller = new
LinearSmoothScroller(context) {
@Override protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
}
};
inflater = LayoutInflater.from(context);
}
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
public TextView placeTypeLabel;
public ImageView placeTypeIcon;
public LinearLayout placeTypeLayout;
public RecyclerViewHolder(View view) {
super(view);
placeTypeLabel = view.findViewById(R.id.place_text);
placeTypeIcon = view.findViewById(R.id.place_icon);
placeTypeLayout = view.findViewById(R.id.search_list_item);
}
}
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = inflater.inflate(R.layout.nearby_search_list_item, parent, false);
return new RecyclerViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
Label label = displayedLabels.get(position);
holder.placeTypeIcon.setImageResource(label.getIcon());
holder.placeTypeLabel.setText(label.toString());
holder.placeTypeLayout.setBackgroundColor(label.isSelected() ? ContextCompat.getColor(context, R.color.divider_grey) : Color.WHITE);
holder.placeTypeLayout.setOnClickListener(view -> {
NearbyParentFragmentPresenter.getInstance().setCheckboxUnknown();
if (label.isSelected()) {
selectedLabels.remove(label);
} else {
selectedLabels.add(label);
displayedLabels.remove(label);
displayedLabels.add(selectedLabels.size()-1, label);
notifyDataSetChanged();
smoothScroller.setTargetPosition(0);
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
}
label.setSelected(!label.isSelected());
holder.placeTypeLayout.setBackgroundColor(label.isSelected() ? ContextCompat.getColor(context, R.color.divider_grey) : Color.WHITE);
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(selectedLabels, 0, false, false);
});
}
@Override
public long getItemId(int position) {
return displayedLabels.get(position).hashCode();
}
@Override
public int getItemCount() {
return displayedLabels.size();
}
@Override
public Filter getFilter() {
return new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<Label> filteredArrayList = new ArrayList<>();
if (labels == null) {
labels = new ArrayList<>(displayedLabels);
}
if (constraint == null || constraint.length() == 0) {
// set the Original result to return
results.count = labels.size();
results.values = labels;
} else {
constraint = constraint.toString().toLowerCase();
for (Label label : labels) {
String data = label.toString();
if (data.toLowerCase().startsWith(constraint.toString())) {
filteredArrayList.add(Label.fromText(label.getText()));
}
}
// set the Filtered result to return
results.count = filteredArrayList.size();
results.values = filteredArrayList;
}
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
displayedLabels = (ArrayList<Label>) results.values; // has the filtered values
notifyDataSetChanged(); // notifies the data with new filtered values
}
};
}
public void setRecyclerViewAdapterItemsGreyedOut() {
state = CheckBoxTriStates.UNCHECKED;
for (Label label : labels) {
label.setSelected(false);
selectedLabels.remove(label);
}
notifyDataSetChanged();
}
public void setRecyclerViewAdapterAllSelected() {
state = CheckBoxTriStates.CHECKED;
for (Label label : labels) {
label.setSelected(true);
if (!selectedLabels.contains(label)) {
selectedLabels.add(label);
}
}
notifyDataSetChanged();
}
public void setRecyclerViewAdapterNeutral() {
state = CheckBoxTriStates.UNKNOWN;
}
}

View file

@ -0,0 +1,57 @@
package fr.free.nrw.commons.nearby;
import java.util.ArrayList;
public class NearbyFilterState {
private boolean existsSelected;
private boolean needPhotoSelected;
private int checkBoxTriState;
private ArrayList<Label> selectedLabels;
private static NearbyFilterState nearbyFılterStateInstance;
/**
* Define initial filter values here
*/
private NearbyFilterState() {
existsSelected = false;
needPhotoSelected = true;
checkBoxTriState = -1; // Unknown
selectedLabels = new ArrayList<>(); // Initially empty
}
public static NearbyFilterState getInstance() {
if (nearbyFılterStateInstance == null) {
nearbyFılterStateInstance = new NearbyFilterState();
}
return nearbyFılterStateInstance;
}
public static void setSelectedLabels(ArrayList<Label> selectedLabels) {
getInstance().selectedLabels = selectedLabels;
}
public static void setExistsSelected(boolean existsSelected) {
getInstance().existsSelected = existsSelected;
}
public static void setNeedPhotoSelected(boolean needPhotoSelected) {
getInstance().needPhotoSelected = needPhotoSelected;
}
public boolean isExistsSelected() {
return existsSelected;
}
public boolean isNeedPhotoSelected() {
return needPhotoSelected;
}
public int getCheckBoxTriState() {
return checkBoxTriState;
}
public ArrayList<Label> getSelectedLabels() {
return selectedLabels;
}
}

View file

@ -0,0 +1,21 @@
package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ListView;
public class NearbyfilterSearchListView extends ListView {
public NearbyfilterSearchListView(Context context) {
super(context);
}
public NearbyfilterSearchListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NearbyfilterSearchListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}

View file

@ -23,18 +23,21 @@ public class Place implements Parcelable {
private final String longDescription;
public final LatLng location;
private final String category;
public final String pic;
public final String destroyed;
public String distance;
public final Sitelinks siteLinks;
public Place(String name, Label label, String longDescription, LatLng location, String category, Sitelinks siteLinks) {
this.name = name;
public Place(String name, Label label, String longDescription, LatLng location, String category, Sitelinks siteLinks, String pic, String destroyed) { this.name = name;
this.label = label;
this.longDescription = longDescription;
this.location = location;
this.category = category;
this.siteLinks = siteLinks;
this.pic = (pic == null) ? "":pic;
this.destroyed = (destroyed == null) ? "":destroyed;
}
public Place(Parcel in) {
@ -44,6 +47,10 @@ public class Place implements Parcelable {
this.location = in.readParcelable(LatLng.class.getClassLoader());
this.category = in.readString();
this.siteLinks = in.readParcelable(Sitelinks.class.getClassLoader());
String picString = in.readString();
this.pic = (picString == null) ? "":picString;
String destroyedString = in.readString();
this.destroyed = (destroyedString == null) ? "":destroyedString;
}
public static Place from(NearbyResultItem item) {
@ -62,7 +69,9 @@ public class Place implements Parcelable {
.setWikipediaLink(item.getWikipediaArticle().getValue())
.setCommonsLink(item.getCommonsArticle().getValue())
.setWikidataLink(item.getItem().getValue())
.build());
.build(),
item.getPic().getValue(),
item.getDestroyed().getValue());
}
/**
@ -105,7 +114,7 @@ public class Place implements Parcelable {
/**
* Extracts the entity id from the wikidata link
* @return returns the entity id if wikidata link exists
* @return returns the entity id if wikidata link destroyed
*/
@Nullable
public String getWikiDataEntityId() {
@ -173,6 +182,8 @@ public class Place implements Parcelable {
", category='" + category + '\'' +
", distance='" + distance + '\'' +
", siteLinks='" + siteLinks.toString() + '\'' +
", pic='" + pic + '\'' +
", destroyed='" + destroyed + '\'' +
'}';
}

View file

@ -6,6 +6,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.util.List;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
@ -27,5 +28,9 @@ public interface NearbyMapContract {
void addOnCameraMoveListener(MapboxMap.OnCameraMoveListener onCameraMoveListener);
void centerMapToPlace(Place place, boolean isPortraitMode);
void removeCurrentLocationMarker();
List<NearbyBaseMarker> getBaseMarkerOptions();
void filterMarkersByLabels(List<Label> labelList, boolean displayExists, boolean displayNeeds, boolean filterForPlaceState, boolean filterForAllNoneType);
void filterOutAllMarkers();
void displayAllMarkers();
}
}

View file

@ -8,6 +8,7 @@ import java.util.List;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.Place;
public interface NearbyParentFragmentContract {
@ -26,6 +27,7 @@ public interface NearbyParentFragmentContract {
void animateFABs();
void recenterMap(LatLng curLatLng);
void hideBottomSheet();
void hideBottomDetailsSheet();
void displayBottomSheetWithInfo(Marker marker);
void addOnCameraMoveListener(MapboxMap.OnCameraMoveListener onCameraMoveListener);
void addSearchThisAreaButtonAction();
@ -35,6 +37,11 @@ public interface NearbyParentFragmentContract {
boolean isDetailsBottomSheetVisible();
void setBottomSheetDetailsSmaller();
boolean isSearchThisAreaButtonVisible();
void setRecyclerViewAdapterAllSelected();
void setRecyclerViewAdapterItemsGreyedOut();
void setCheckBoxAction();
void setCheckBoxState(int state);
void setFilterState();
}
interface NearbyListView {
@ -49,6 +56,9 @@ public interface NearbyParentFragmentContract {
void setActionListeners(JsonKvStore applicationKvStore);
void backButtonClicked();
MapboxMap.OnCameraMoveListener onCameraMove(MapboxMap mapboxMap);
void filterByMarkerType(List<Label> selectedLabels, int state, boolean filterForPlaceState, boolean filterForAllNoneType);
void searchViewGainedFocus();
void setCheckboxUnknown();
}
interface ViewsAreReadyCallback {

View file

@ -28,6 +28,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.utils.MapFragmentUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.inject.Inject;
@ -36,6 +37,8 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.MarkerPlaceGroup;
import fr.free.nrw.commons.nearby.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyMarker;
@ -75,6 +78,7 @@ public class NearbyMapFragment extends CommonsDaggerSupportFragment
private MapView map;
private Marker currentLocationMarker;
private Polygon currentLocationPolygon;
private List<NearbyBaseMarker> customBaseMarkerOptions;
private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005;
private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004;
@ -273,7 +277,7 @@ public class NearbyMapFragment extends CommonsDaggerSupportFragment
, Marker selectedMarker
, NearbyParentFragmentPresenter nearbyParentFragmentPresenter) {
Timber.d("Updates map markers");
List<NearbyBaseMarker> customBaseMarkerOptions = NearbyController
customBaseMarkerOptions = NearbyController
.loadAttractionsFromLocationToBaseMarkerOptions(latLng, // Curlatlang will be used to calculate distances
placeList,
getActivity(),
@ -336,6 +340,101 @@ public class NearbyMapFragment extends CommonsDaggerSupportFragment
mapboxMap.removePolygon(currentLocationPolygon);
}
/**
* Filters markers based on selectedLabels and chips
* @param selectedLabels label list that user clicked
* @param displayExists chip for displaying only existing places
* @param displayNeedsPhoto chip for displaying only places need photos
* @param filterForPlaceState true if we filter places for place state
* @param filterForAllNoneType true if we filter places with all none button
*/
@Override
public void filterMarkersByLabels(List<Label> selectedLabels,
boolean displayExists,
boolean displayNeedsPhoto,
boolean filterForPlaceState,
boolean filterForAllNoneType) {
if (selectedLabels.size() == 0 && filterForPlaceState) { // If nothing is selected, display all
greyOutAllMarkers();
for (MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
if (displayExists && displayNeedsPhoto) {
// Exists and needs photo
if (markerPlaceGroup.getPlace().destroyed.trim().isEmpty() && markerPlaceGroup.getPlace().pic.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else if (displayExists && !displayNeedsPhoto) {
// Exists and all included needs and doesn't needs photo
if (markerPlaceGroup.getPlace().destroyed.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else if (!displayExists && displayNeedsPhoto) {
// All and only needs photo
if (markerPlaceGroup.getPlace().pic.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else if (!displayExists && !displayNeedsPhoto) {
// all
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
//updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else {
// First greyed out all markers
greyOutAllMarkers();
for (MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
for (Label label : selectedLabels) {
if (markerPlaceGroup.getPlace().getLabel().toString().equals(label.toString())) {
if (displayExists && displayNeedsPhoto) {
// Exists and needs photo
if (markerPlaceGroup.getPlace().destroyed.trim().isEmpty() && markerPlaceGroup.getPlace().pic.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else if (displayExists && !displayNeedsPhoto) {
// Exists and all included needs and doesn't needs photo
if (markerPlaceGroup.getPlace().destroyed.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else if (!displayExists && displayNeedsPhoto) {
// All and only needs photo
if (markerPlaceGroup.getPlace().pic.trim().isEmpty()) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
} else if (!displayExists && !displayNeedsPhoto) {
// all
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
}
}
}
}
}
/**
* Greys out all markers
*/
@Override
public void filterOutAllMarkers() {
greyOutAllMarkers();
}
/**
* Displays all markers
*/
@Override
public void displayAllMarkers() {
for (MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
}
@Override
public List<NearbyBaseMarker> getBaseMarkerOptions() {
return customBaseMarkerOptions;
}
/**
* Adds markers to map
* @param baseMarkerList is markers will be added
@ -346,7 +445,22 @@ public class NearbyMapFragment extends CommonsDaggerSupportFragment
public void addNearbyMarkersToMapBoxMap(@Nullable List<NearbyBaseMarker> baseMarkerList
, Marker selectedMarker
, NearbyParentFragmentPresenter nearbyParentFragmentPresenter) {
mapboxMap.addMarkers(baseMarkerList);
List<Marker> markers = mapboxMap.addMarkers(baseMarkerList);
NearbyController.markerExistsMap = new HashMap<Boolean, Marker>();
NearbyController.markerNeedPicMap = new HashMap<Boolean, Marker>();
NearbyController.markerLabelList.clear();
for (int i = 0; i < baseMarkerList.size(); i++) {
NearbyBaseMarker nearbyBaseMarker = baseMarkerList.get(i);
NearbyController.markerLabelList.add(
new MarkerPlaceGroup(markers.get(i), bookmarkLocationDao.findBookmarkLocation(baseMarkerList.get(i).getPlace()), nearbyBaseMarker.getPlace()));
//TODO: fix bookmark location
NearbyController.markerExistsMap.put((baseMarkerList.get(i).getPlace().hasWikidataLink()), markers.get(i));
NearbyController.markerNeedPicMap.put(((baseMarkerList.get(i).getPlace().pic == null) ? true : false), markers.get(i));
}
map.getMapAsync(mapboxMap -> {
mapboxMap.addMarkers(baseMarkerList);
setMapMarkerActions(selectedMarker, nearbyParentFragmentPresenter);
@ -403,6 +517,14 @@ public class NearbyMapFragment extends CommonsDaggerSupportFragment
vectorDrawable = VectorDrawableCompat.create(
getContext().getResources(), R.drawable.ic_custom_bookmark_marker, getContext().getTheme()
);
} else if (!place.pic.trim().isEmpty()) {
vectorDrawable = VectorDrawableCompat.create( // Means place has picture
getContext().getResources(), R.drawable.ic_custom_map_marker_green, getContext().getTheme()
);
} else if (!place.destroyed.trim().isEmpty()) { // Means place is destroyed
vectorDrawable = VectorDrawableCompat.create( // Means place has picture
getContext().getResources(), R.drawable.ic_custom_map_marker_grey, getContext().getTheme()
);
} else {
vectorDrawable = VectorDrawableCompat.create(
getContext().getResources(), R.drawable.ic_custom_map_marker, getContext().getTheme()
@ -431,6 +553,22 @@ public class NearbyMapFragment extends CommonsDaggerSupportFragment
}
}
/**
* Greys out all markers except current location marker
*/
public void greyOutAllMarkers() {
VectorDrawableCompat vectorDrawable;
vectorDrawable = VectorDrawableCompat.create(
getContext().getResources(), R.drawable.ic_custom_greyed_out_marker, getContext().getTheme());
Bitmap icon = UiUtils.getBitmap(vectorDrawable);
for (Marker marker : mapboxMap.getMarkers()) {
if (currentLocationMarker.getTitle() != marker.getTitle()) {
marker.setIcon(IconFactory.getInstance(getContext()).fromBitmap(icon));
}
}
addCurrentLocationMarker(NearbyController.currentLocation);
}
/**
* Centers the map in nearby fragment to a given place
* @param place is new center of the map

View file

@ -8,6 +8,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@ -19,6 +20,7 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;
@ -26,10 +28,17 @@ import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxSearchView;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@ -39,6 +48,9 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
import com.mapbox.mapboxsdk.maps.Style;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
@ -54,12 +66,16 @@ import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.nearby.CheckBoxTriStates;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyFilterSearchRecyclerViewAdapter;
import fr.free.nrw.commons.nearby.NearbyFilterState;
import fr.free.nrw.commons.nearby.NearbyMarker;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
import fr.free.nrw.commons.utils.FragmentUtils;
import fr.free.nrw.commons.utils.LayoutUtils;
import fr.free.nrw.commons.utils.NearbyFABUtils;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.PermissionUtils;
@ -73,6 +89,7 @@ import timber.log.Timber;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION;
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.wikidata.WikidataConstants.PLACE_OBJECT;
@ -106,6 +123,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@BindView(R.id.container_sheet) FrameLayout frameLayout;
@BindView(R.id.loading_nearby_list) ConstraintLayout loadingNearbyLayout;
@BindView(R.id.choice_chip_exists) Chip chipExists;
@BindView(R.id.choice_chip_needs_photo) Chip chipNeedsPhoto;
@BindView(R.id.choice_chip_group) ChipGroup choiceChipGroup;
@BindView(R.id.search_view) SearchView searchView;
@BindView(R.id.search_list_view) RecyclerView recyclerView;
@BindView(R.id.nearby_filter_list) View nearbyFilterList;
@BindView(R.id.checkbox_tri_states) CheckBoxTriStates checkBoxTriStates;
@Inject LocationServiceManager locationManager;
@Inject NearbyController nearbyController;
@Inject @Named("default_preferences") JsonKvStore applicationKvStore;
@ -113,6 +138,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Inject ContributionController controller;
@Inject WikidataEditListener wikidataEditListener;
private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter;
private BottomSheetBehavior bottomSheetListBehavior;
private BottomSheetBehavior bottomSheetDetailsBehavior;
private Animation rotate_backward;
@ -178,6 +205,98 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
bottomSheetDetails.setVisibility(View.VISIBLE);
}
public void initNearbyFilter() {
nearbyFilterList.setVisibility(View.GONE);
searchView.setOnQueryTextFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
nearbyParentFragmentPresenter.searchViewGainedFocus();
nearbyFilterList.setVisibility(View.VISIBLE);
} else {
nearbyFilterList.setVisibility(View.GONE);
}
});
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new DividerItemDecoration(getContext(),
DividerItemDecoration.VERTICAL));
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
nearbyFilterSearchRecyclerViewAdapter = new NearbyFilterSearchRecyclerViewAdapter(getContext(),new ArrayList<>(TEXT_TO_DESCRIPTION.values()), recyclerView);
nearbyFilterList.getLayoutParams().width = (int) LayoutUtils.getScreenWidth(getActivity(), 0.75);
recyclerView.setAdapter(nearbyFilterSearchRecyclerViewAdapter);
LayoutUtils.setLayoutHeightAllignedToWidth(1, nearbyFilterList);
compositeDisposable.add(RxSearchView.queryTextChanges(searchView)
.takeUntil(RxView.detaches(searchView))
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe( query -> {
((NearbyFilterSearchRecyclerViewAdapter) recyclerView.getAdapter()).getFilter().filter(query.toString());
}));
initFilterChips();
}
@Override
public void setCheckBoxAction() {
checkBoxTriStates.addAction();
checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN);
}
@Override
public void setCheckBoxState(int state) {
checkBoxTriStates.setState(state);
}
@Override
public void setFilterState() {
Log.d("deneme5","setfilterState");
chipNeedsPhoto.setChecked(NearbyFilterState.getInstance().isNeedPhotoSelected());
chipExists.setChecked(NearbyFilterState.getInstance().isExistsSelected());
if (NearbyController.currentLocation != null) {
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
}
}
private void initFilterChips() {
chipNeedsPhoto.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (NearbyController.currentLocation != null) {
checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN);
if (isChecked) {
NearbyFilterState.getInstance().setNeedPhotoSelected(true);
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
} else {
NearbyFilterState.getInstance().setNeedPhotoSelected(false);
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
}
} else {
chipNeedsPhoto.setChecked(!isChecked);
}
});
chipExists.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (NearbyController.currentLocation != null) {
checkBoxTriStates.setState(CheckBoxTriStates.UNKNOWN);
if (isChecked) {
NearbyFilterState.getInstance().setExistsSelected(true);
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
} else {
NearbyFilterState.getInstance().setExistsSelected(false);
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(nearbyFilterSearchRecyclerViewAdapter.selectedLabels, checkBoxTriStates.getState(), true, false);
}
} else {
chipExists.setChecked(!isChecked);
}
});
}
/**
* Defines how bottom sheets will act on click
*/
@ -385,6 +504,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
nearbyParentFragmentPresenter.nearbyFragmentsAreReady();
initViews();
nearbyParentFragmentPresenter.setActionListeners(applicationKvStore);
initNearbyFilter();
}
@Override
@ -483,6 +603,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
// showErrorMessage(getString(R.string.error_fetching_nearby_places));
setProgressBarVisibility(false);
nearbyParentFragmentPresenter.lockUnlockNearby(false);
setFilterState();
}));
}
@ -509,6 +630,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
*/
private void updateMapMarkers(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
nearbyParentFragmentPresenter.updateMapMarkers(nearbyPlacesInfo, selectedMarker);
setFilterState();
}
/**
@ -518,6 +640,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
*/
private void updateMapMarkersForCustomLocation(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
nearbyParentFragmentPresenter.updateMapMarkersForCustomLocation(nearbyPlacesInfo, selectedMarker);
setFilterState();
}
@ -563,6 +686,20 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
}
@Override
public void setRecyclerViewAdapterAllSelected() {
if (nearbyFilterSearchRecyclerViewAdapter != null && NearbyController.currentLocation != null) {
nearbyFilterSearchRecyclerViewAdapter.setRecyclerViewAdapterAllSelected();
}
}
@Override
public void setRecyclerViewAdapterItemsGreyedOut() {
if (nearbyFilterSearchRecyclerViewAdapter != null && NearbyController.currentLocation != null) {
nearbyFilterSearchRecyclerViewAdapter.setRecyclerViewAdapterItemsGreyedOut();
}
}
@Override
public void setProgressBarVisibility(boolean isVisible) {
if (isVisible) {
@ -729,6 +866,11 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
@Override
public void hideBottomDetailsSheet() {
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
@Override
public void displayBottomSheetWithInfo(Marker marker) {
this.selectedMarker = marker;

View file

@ -12,6 +12,8 @@ public class NearbyResultItem {
@SerializedName("class") private final ResultTuple className;
@SerializedName("classLabel") private final ResultTuple classLabel;
@SerializedName("commonsCategory") private final ResultTuple commonsCategory;
@SerializedName("pic") private final ResultTuple pic;
@SerializedName("destroyed") private final ResultTuple destroyed;
public NearbyResultItem(ResultTuple item,
ResultTuple wikipediaArticle,
@ -20,7 +22,9 @@ public class NearbyResultItem {
ResultTuple label,
ResultTuple icon, ResultTuple className,
ResultTuple classLabel,
ResultTuple commonsCategory) {
ResultTuple commonsCategory,
ResultTuple pic,
ResultTuple destroyed) {
this.item = item;
this.wikipediaArticle = wikipediaArticle;
this.commonsArticle = commonsArticle;
@ -30,6 +34,8 @@ public class NearbyResultItem {
this.className = className;
this.classLabel = classLabel;
this.commonsCategory = commonsCategory;
this.pic = pic;
this.destroyed = destroyed;
}
public ResultTuple getItem() {
@ -67,4 +73,10 @@ public class NearbyResultItem {
public ResultTuple getCommonsCategory() {
return commonsCategory == null ? new ResultTuple():commonsCategory;
}
public ResultTuple getPic() {
return pic == null ? new ResultTuple():pic;
}
public ResultTuple getDestroyed() {
return destroyed == null ? new ResultTuple(): destroyed;
}
}

View file

@ -5,11 +5,16 @@ import android.view.View;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.util.List;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.location.LocationUpdateListener;
import fr.free.nrw.commons.nearby.CheckBoxTriStates;
import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyFilterState;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.contract.NearbyMapContract;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
@ -22,6 +27,9 @@ import static fr.free.nrw.commons.location.LocationServiceManager.LocationChange
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.SEARCH_CUSTOM_AREA;
import static fr.free.nrw.commons.nearby.CheckBoxTriStates.CHECKED;
import static fr.free.nrw.commons.nearby.CheckBoxTriStates.UNCHECKED;
import static fr.free.nrw.commons.nearby.CheckBoxTriStates.UNKNOWN;
public class NearbyParentFragmentPresenter
implements NearbyParentFragmentContract.UserActions,
@ -135,6 +143,7 @@ public class NearbyParentFragmentPresenter
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED, null);
this.nearbyParentFragmentView.addSearchThisAreaButtonAction();
this.nearbyMapFragmentView.addOnCameraMoveListener(onCameraMove(getMapboxMap()));
nearbyParentFragmentView.setCheckBoxAction();
mapInitialized = true;
}
@ -345,6 +354,45 @@ public class NearbyParentFragmentPresenter
};
}
@Override
public void filterByMarkerType(List<Label> selectedLabels, int state, boolean filterForPlaceState, boolean filterForAllNoneType) {
if (filterForAllNoneType) { // Means we will set labels based on states
switch (state) {
case UNKNOWN:
// Do nothing
break;
case UNCHECKED:
nearbyMapFragmentView.filterOutAllMarkers();
nearbyParentFragmentView.setRecyclerViewAdapterItemsGreyedOut();
break;
case CHECKED:
nearbyMapFragmentView.displayAllMarkers();
nearbyParentFragmentView.setRecyclerViewAdapterAllSelected();
break;
}
} else {
nearbyMapFragmentView.filterMarkersByLabels(selectedLabels,
NearbyFilterState.getInstance().isExistsSelected(),
NearbyFilterState.getInstance().isNeedPhotoSelected(),
filterForPlaceState, filterForAllNoneType);
}
}
@Override
public void setCheckboxUnknown() {
nearbyParentFragmentView.setCheckBoxState(CheckBoxTriStates.UNKNOWN);
}
@Override
public void searchViewGainedFocus() {
if(nearbyParentFragmentView.isListBottomSheetExpanded()) {
// Back should first hide the bottom sheet if it is expanded
nearbyParentFragmentView.hideBottomSheet();
} else if (nearbyParentFragmentView.isDetailsBottomSheetVisible()) {
nearbyParentFragmentView.hideBottomDetailsSheet();
}
}
public View.OnClickListener onSearchThisAreaClicked() {
return v -> {
// Lock map operations during search this area operation

View file

@ -0,0 +1,60 @@
package fr.free.nrw.commons.utils;
import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
public class LayoutUtils {
/**
* Can be used for keeping aspect radios suggested by material guidelines. See:
* https://material.io/design/layout/spacing-methods.html#containers-aspect-ratios
* In some cases we don't know exact width, for such cases this method measures
* width and sets height by multiplying the width with height.
* @param rate Aspect ratios, ie 1 for 1:1. (width * rate = height)
* @param view view to change height
*/
public static void setLayoutHeightAllignedToWidth(double rate, View view) {
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = (int) (view.getWidth() * rate);
view.setLayoutParams(layoutParams);
}
});
}
/**
* Can be used for keeping aspect radios suggested by material guidelines. See:
* https://material.io/design/layout/spacing-methods.html#containers-aspect-ratios
* In some cases we don't know exact height, for such cases this method measures
* height and sets width by multiplying the width with height.
* @param rate Aspect ratios, ie 1 for 1:1. (height * rate = width)
* @param view view to change width
*/
public static void setLayoutWidthAllignedToHeight(double rate, View view) {
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.width = (int) (view.getHeight() * rate);
view.setLayoutParams(layoutParams);
}
});
}
public static double getScreenWidth(Context context, double rate) {
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.widthPixels * rate;
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/current_marker_stroke" android:state_checked="true" />
<item android:color="@color/cardview_light_background" />
</selector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/button_blue"
android:pathData="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/button_blue"
android:pathData="M19,5v14H5V5h14m0,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

View file

@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="28dp"
android:viewportWidth="24.0"
android:viewportHeight="28.0">
<path
android:fillAlpha="0.1"
android:fillColor="#48000000"
android:pathData="M6.072,22.223a6.031,3.672 0,1 0,12.062 0a6.031,3.672 0,1 0,-12.062 0z"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#4D000000"
android:pathData="M11.575,11.62C10.689,11.462 9.902,10.759 9.625,9.878 9.553,9.65 9.535,9.499 9.538,9.14c0.004,-0.397 0.019,-0.492 0.13,-0.787 0.236,-0.631 0.646,-1.099 1.212,-1.382 0.386,-0.193 0.709,-0.272 1.116,-0.272 0.676,0 1.263,0.247 1.744,0.734 0.355,0.359 0.541,0.682 0.657,1.136 0.327,1.278 -0.442,2.611 -1.723,2.987 -0.282,0.083 -0.817,0.114 -1.099,0.063z"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#7CE4E0D9"
android:pathData="M11.617,21.707C10.518,20.424 9.338,18.864 8.395,17.449 6.524,14.641 5.455,12.305 5.102,10.255 5.014,9.744 5.006,8.628 5.088,8.137 5.348,6.561 6.043,5.221 7.158,4.148 9.148,2.231 12.016,1.668 14.593,2.688c2.043,0.809 3.607,2.581 4.162,4.719 0.174,0.67 0.204,0.933 0.203,1.761 -0.001,0.81 -0.035,1.098 -0.22,1.857 -0.614,2.524 -2.571,5.977 -5.383,9.501 -0.645,0.809 -1.321,1.61 -1.358,1.61 -0.008,0 -0.179,-0.193 -0.381,-0.428zM12.617,11.603c0.783,-0.188 1.457,-0.795 1.738,-1.564 0.516,-1.415 -0.317,-2.962 -1.783,-3.312 -0.216,-0.052 -0.317,-0.059 -0.661,-0.047 -0.354,0.012 -0.441,0.025 -0.682,0.104 -0.673,0.221 -1.205,0.695 -1.506,1.344 -0.176,0.38 -0.218,0.584 -0.217,1.054 0.001,0.324 0.014,0.452 0.064,0.635 0.266,0.97 1.077,1.689 2.079,1.844 0.243,0.038 0.68,0.012 0.968,-0.057z"
android:strokeAlpha="1"
android:strokeColor="#54003B59"
android:strokeWidth="1" />
</vector>

View file

@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="28dp"
android:viewportWidth="24.0"
android:viewportHeight="28.0">
<path
android:fillAlpha="0.1"
android:fillColor="#000000"
android:pathData="M6.072,22.223a6.031,3.672 0,1 0,12.062 0a6.031,3.672 0,1 0,-12.062 0z"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#000000"
android:pathData="M11.575,11.62C10.689,11.462 9.902,10.759 9.625,9.878 9.553,9.65 9.535,9.499 9.538,9.14c0.004,-0.397 0.019,-0.492 0.13,-0.787 0.236,-0.631 0.646,-1.099 1.212,-1.382 0.386,-0.193 0.709,-0.272 1.116,-0.272 0.676,0 1.263,0.247 1.744,0.734 0.355,0.359 0.541,0.682 0.657,1.136 0.327,1.278 -0.442,2.611 -1.723,2.987 -0.282,0.083 -0.817,0.114 -1.099,0.063z"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#4CAF50"
android:pathData="M11.617,21.707C10.518,20.424 9.338,18.864 8.395,17.449 6.524,14.641 5.455,12.305 5.102,10.255 5.014,9.744 5.006,8.628 5.088,8.137 5.348,6.561 6.043,5.221 7.158,4.148 9.148,2.231 12.016,1.668 14.593,2.688c2.043,0.809 3.607,2.581 4.162,4.719 0.174,0.67 0.204,0.933 0.203,1.761 -0.001,0.81 -0.035,1.098 -0.22,1.857 -0.614,2.524 -2.571,5.977 -5.383,9.501 -0.645,0.809 -1.321,1.61 -1.358,1.61 -0.008,0 -0.179,-0.193 -0.381,-0.428zM12.617,11.603c0.783,-0.188 1.457,-0.795 1.738,-1.564 0.516,-1.415 -0.317,-2.962 -1.783,-3.312 -0.216,-0.052 -0.317,-0.059 -0.661,-0.047 -0.354,0.012 -0.441,0.025 -0.682,0.104 -0.673,0.221 -1.205,0.695 -1.506,1.344 -0.176,0.38 -0.218,0.584 -0.217,1.054 0.001,0.324 0.014,0.452 0.064,0.635 0.266,0.97 1.077,1.689 2.079,1.844 0.243,0.038 0.68,0.012 0.968,-0.057z"
android:strokeAlpha="1"
android:strokeColor="#003b59"
android:strokeWidth="1" />
</vector>

View file

@ -0,0 +1,23 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="28dp"
android:viewportWidth="24.0"
android:viewportHeight="28.0">
<path
android:fillAlpha="0.1"
android:fillColor="#000000"
android:pathData="M6.072,22.223a6.031,3.672 0,1 0,12.062 0a6.031,3.672 0,1 0,-12.062 0z"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#000000"
android:pathData="M11.575,11.62C10.689,11.462 9.902,10.759 9.625,9.878 9.553,9.65 9.535,9.499 9.538,9.14c0.004,-0.397 0.019,-0.492 0.13,-0.787 0.236,-0.631 0.646,-1.099 1.212,-1.382 0.386,-0.193 0.709,-0.272 1.116,-0.272 0.676,0 1.263,0.247 1.744,0.734 0.355,0.359 0.541,0.682 0.657,1.136 0.327,1.278 -0.442,2.611 -1.723,2.987 -0.282,0.083 -0.817,0.114 -1.099,0.063z"
android:strokeWidth="1" />
<path
android:fillAlpha="1"
android:fillColor="#454547"
android:pathData="M11.617,21.707C10.518,20.424 9.338,18.864 8.395,17.449 6.524,14.641 5.455,12.305 5.102,10.255 5.014,9.744 5.006,8.628 5.088,8.137 5.348,6.561 6.043,5.221 7.158,4.148 9.148,2.231 12.016,1.668 14.593,2.688c2.043,0.809 3.607,2.581 4.162,4.719 0.174,0.67 0.204,0.933 0.203,1.761 -0.001,0.81 -0.035,1.098 -0.22,1.857 -0.614,2.524 -2.571,5.977 -5.383,9.501 -0.645,0.809 -1.321,1.61 -1.358,1.61 -0.008,0 -0.179,-0.193 -0.381,-0.428zM12.617,11.603c0.783,-0.188 1.457,-0.795 1.738,-1.564 0.516,-1.415 -0.317,-2.962 -1.783,-3.312 -0.216,-0.052 -0.317,-0.059 -0.661,-0.047 -0.354,0.012 -0.441,0.025 -0.682,0.104 -0.673,0.221 -1.205,0.695 -1.506,1.344 -0.176,0.38 -0.218,0.584 -0.217,1.054 0.001,0.324 0.014,0.452 0.064,0.635 0.266,0.97 1.077,1.689 2.079,1.844 0.243,0.038 0.68,0.012 0.968,-0.057z"
android:strokeAlpha="1"
android:strokeColor="#003b59"
android:strokeWidth="1" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/button_blue"
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM17,13L7,13v-2h10v2z"/>
</vector>

View file

@ -0,0 +1,42 @@
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingStart="15dp"
android:text="@string/place_state"
android:textColor="@color/white"/>
<com.google.android.material.chip.ChipGroup
android:id="@+id/choice_chip_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:theme="@style/Theme.MaterialComponents.Light"
app:singleSelection="false">
<com.google.android.material.chip.Chip
android:id="@+id/choice_chip_exists"
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_exists"/>
<com.google.android.material.chip.Chip
android:id="@+id/choice_chip_needs_photo"
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_needs_photo"/>
</com.google.android.material.chip.ChipGroup>
</LinearLayout>

View file

@ -0,0 +1,24 @@
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/search_view_layout"
android:paddingVertical="15dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingStart="15dp"
android:text="@string/place_type"
android:textColor="@color/white"/>
<SearchView
android:id="@+id/search_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:tint="@color/white"
android:queryHint="@string/nearby_search_hint"
android:searchIcon="@drawable/ic_search_white_24dp">
</SearchView>
</LinearLayout>

View file

@ -8,18 +8,47 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/status_bar_blue">
android:background="@color/status_bar_blue"
android:id="@+id/map_layout">
<LinearLayout
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:focusableInTouchMode="true"/>
<include
layout="@layout/nearby_filter_all_items"
android:id="@+id/nearby_filter"/>
<include layout="@layout/nearby_filter_list"
android:id="@+id/nearby_filter_list"
android:layout_below="@id/nearby_filter"
android:layout_height="160dp"
android:layout_width="160dp"
android:layout_alignParentEnd="true"/>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
android:layout_below="@id/nearby_filter"/>
</LinearLayout>
<Button
android:id="@+id/search_this_area_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:background="@color/white"
android:padding="8dp"
android:singleLine="true"
android:text="@string/search_this_area"
android:textColor="@color/status_bar_blue"
android:visibility="gone"
app:elevation="6dp"
android:layout_below="@id/nearby_filter"/>
<View
android:id="@+id/transparentView"
@ -37,9 +66,9 @@
android:id="@+id/fab_recenter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/toolbar"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@id/nearby_filter"
android:clickable="true"
android:visibility="visible"
app:backgroundTint="@color/main_background_light"
@ -49,7 +78,6 @@
app:srcCompat="@drawable/ic_my_location_black_24dp"
app:useCompatPadding="true" />
</RelativeLayout>
<include layout="@layout/bottom_sheet_nearby" />
@ -58,21 +86,6 @@
android:id="@+id/bottom_sheet_details"
layout="@layout/bottom_sheet_details" />
<Button
android:id="@+id/search_this_area_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:layout_margin="8dp"
android:background="@color/white"
android:padding="8dp"
android:singleLine="true"
android:text="@string/search_this_area"
android:textColor="@color/status_bar_blue"
android:visibility="gone"
app:elevation="6dp" />
<ProgressBar
android:id="@+id/map_progress_bar"
android:layout_width="match_parent"

View file

@ -0,0 +1,22 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<include
android:id="@+id/chip_view"
layout="@layout/filter_chip_view"
android:background="@color/deleteRed"
/>
<include android:id="@+id/search_view_layout" layout="@layout/filter_search_view_layout" />
<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

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nearby_filter_list_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:elevation="8dp"
android:background="@color/white">
<fr.free.nrw.commons.nearby.CheckBoxTriStates
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/checkbox_tri_states"
android:layout_margin="10dp"
android:text="All"
/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/search_list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
/>
</LinearLayout>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_list_item"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="25dp"
>
<ImageView
android:id="@+id/place_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ImageView>
<TextView
android:id="@+id/place_text"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</TextView>
</LinearLayout>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* Cblair91
-->
<resources>
<string name="crash_dialog_title">Commons has crashed</string>
<string name="crash_dialog_text">Oops. Something went wrong!</string>
<string name="crash_dialog_comment_prompt">Tell us what you were doing, then share it via email to us. Will help us fix it!</string>
<string name="crash_dialog_ok_toast">Thank you!</string>
</resources>

View file

@ -380,7 +380,7 @@
<string name="next">ದುಂಬುದ</string>
<string name="previous">ಪಿರವುದ</string>
<string name="submit">ಒಪ್ಪಿಸಾಲೆ</string>
<string name="upload_title_duplicate">ಕಡತಪುದರ್ %1$s ದ ಒಂಜಿ ಕಡತ ಉಂಡು. ಈರ್ ಖಂಡಿತಾ ದುಂಬು ಪೋವರೆ ಬಯಕುವರಾ?\nA file with the file name exists. Are you sure you want to proceed?</string>
<string name="upload_title_duplicate">ಕಡತಪುದರ್ %1$s ದ ಒಂಜಿ ಕಡತ ಉಂಡು. ಈರ್ ಖಂಡಿತಾ ದುಂಬು ಪೋವರೆ ಬಯಕುವರಾ?\nA file with the file name destroyed. Are you sure you want to proceed?</string>
<string name="map_application_missing">ಹೊಂದುನ ನಕಾಶೆ ಪರಿಕರನ ಇರೆನ ಸಲಕರಣೆಡ್ ತಿಕ್ಕುಜಿ. ಈ ಗುಣಧರ್ಮನ್ ಬಳಸರೆ ದಯಮಲ್ತ್ ಒಂಜಿ ನಕಾಶೆ ಪರಿಕರನ ತಾಪನೆ ಮಲ್ಪುಲೆ.</string>
<plurals name="upload_count_title">
<item quantity="other">ಒಂಜಿ=%1$d ಮಿತೇರಾಯಿನ</item>

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Authors:
* Ерней
-->
<resources>
<string name="crash_dialog_ok_toast">Рәхмәт!</string>
</resources>

View file

@ -172,7 +172,7 @@
<string name="title_activity_nearby">Nearby Places</string>
<string name="no_nearby">No nearby places found</string>
<string name="warning">Warning</string>
<string name="upload_image_duplicate">This file already exists on Commons. Are you sure you want to proceed?</string>
<string name="upload_image_duplicate">This file already destroyed on Commons. Are you sure you want to proceed?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="media_detail_title">Title</string>
@ -410,7 +410,7 @@
<string name="next">Next</string>
<string name="previous">Previous</string>
<string name="submit">Submit</string>
<string name="upload_title_duplicate" formatted="true">A file with the file name %1$s exists. Are you sure you want to proceed?</string>
<string name="upload_title_duplicate" formatted="true">A file with the file name %1$s destroyed. Are you sure you want to proceed?</string>
<string name="map_application_missing">No compatible map application could be found on your device. Please install a map application to use this feature.</string>
<plurals name="upload_count_title">
<item quantity="one">%1$d Upload</item>
@ -582,5 +582,10 @@ Upload your first media by tapping on the add button.</string>
<string name="text_copy">Text copied to clipboard</string>
<string name="notification_mark_read">Notification marked as read</string>
<string name="some_error">There was some error!</string>
<string name="place_state">Place state:</string>
<string name="place_state_exists">Exists</string>
<string name="place_state_needs_photo">Needs Photo</string>
<string name="place_type">Place type:</string>
<string name="nearby_search_hint">Bridge, museum, hotel etc.</string>
<string name="you_must_reset_your_passsword">Something went wrong with login, you must reset your password !!</string>
</resources>

View file

@ -8,6 +8,8 @@ SELECT
?wikipediaArticle
?commonsArticle
(SAMPLE(?commonsCategory) as ?commonsCategory)
(SAMPLE(?pic) as ?pic)
(SAMPLE(?destroyed) as ?destroyed)
WHERE {
# Around given location...
SERVICE wikibase:around {
@ -16,9 +18,6 @@ SELECT
bd:serviceParam wikibase:radius "${RAD}" . # Radius in kilometers.
}
# ... and without an image.
MINUS {?item wdt:P18 []}
# 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}
@ -26,6 +25,12 @@ SELECT
# Get Commons category (P373)
OPTIONAL { ?item wdt:P373 ?commonsCategory. }
# Get (P18)
OPTIONAL { ?item wdt:P18 ?pic. }
# Get (P576)
OPTIONAL { ?item wdt:P576 ?destroyed. }
# 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.

View file

@ -61,7 +61,7 @@ class BookMarkLocationDaoTest {
examplePlaceBookmark = Place("placeName", exampleLabel, "placeDescription"
, exampleLocation, "placeCategory", builder.build())
, exampleLocation, "placeCategory", builder.build(),"picName","placeDestroyed")
testObject = BookmarkLocationsDao { client }
}
@ -146,7 +146,7 @@ class BookMarkLocationDaoTest {
assertTrue(testObject.updateBookmarkLocation(examplePlaceBookmark))
verify(client).insert(eq(BASE_URI), captor.capture())
captor.firstValue.let { cv ->
assertEquals(10, cv.size())
assertEquals(11, cv.size())
assertEquals(examplePlaceBookmark.name, cv.getAsString(COLUMN_NAME))
assertEquals(examplePlaceBookmark.longDescription, cv.getAsString(COLUMN_DESCRIPTION))
assertEquals(examplePlaceBookmark.label.text, cv.getAsString(COLUMN_LABEL_TEXT))
@ -156,6 +156,7 @@ class BookMarkLocationDaoTest {
assertEquals(examplePlaceBookmark.siteLinks.wikipediaLink.toString(), cv.getAsString(COLUMN_WIKIPEDIA_LINK))
assertEquals(examplePlaceBookmark.siteLinks.wikidataLink.toString(), cv.getAsString(COLUMN_WIKIDATA_LINK))
assertEquals(examplePlaceBookmark.siteLinks.commonsLink.toString(), cv.getAsString(COLUMN_COMMONS_LINK))
assertEquals(examplePlaceBookmark.pic.toString(), cv.getAsString(COLUMN_PIC))
}
}