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);
nearbyBaseMarker.icon(IconFactory.getInstance(context)
.fromBitmap(icon));
// 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;
}
}