mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 05:13:53 +01:00
* Add layout items for nearby filter list and filter item (cherry picked from commitb96f8a68ce) * Edit nearby query (cherry picked from commit1f3c8c8deb) * Add filter items to nearby parent fragment xml (cherry picked from commitd0beadd0e0) * Add icon for green marker (cherry picked from commitf65ca0387a) * Add layout util file to organize layout utilities (cherry picked from commit5c57939245) * Add pic parameter to nearby result item (cherry picked from commit86007e4bb6) * Add pic parameter to place type (cherry picked from commit25c358b67f) * Add green marker styling (cherry picked from commit929c92d887) * Inıt search layouts on Nearby parent (cherry picked from commit2ac38a1919) * Style green markers at nearby controller (cherry picked from commit3e08f39f8e) * Edit bookmark daos to include pic to tables (cherry picked from commit48d69edf3b) * TODO foc bookmark dao item but works now (cherry picked from commitf748399720) * Style nearby filter area (cherry picked from commit6267e488b0) * fix style of filter tab (cherry picked from commit5f843bf366) * Add nearby list adapter (cherry picked from commit56334afe03) * Current status, list size is working, visibility working, filter mechanism is ready (cherry picked from commit7d75c9c589) * Filtering works (cherry picked from commit8a13cf7728) * Filter function works (cherry picked from commit78368a2c0c) * Fix style issues (cherry picked from commit2204f70255) * Add divider to recyclerview (cherry picked from commitc8100b55d7) * Hide bottom sheets accordingly (cherry picked from commitc5deba8b0b) * Code cleanup (cherry picked from commitcf8f64f3cb) * 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:
parent
5c6ab3b253
commit
b3c11842f3
35 changed files with 1264 additions and 66 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
60
app/src/main/java/fr/free/nrw/commons/utils/LayoutUtils.java
Normal file
60
app/src/main/java/fr/free/nrw/commons/utils/LayoutUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue