Fix conflicts

This commit is contained in:
neslihanturan 2019-08-16 13:47:15 +03:00
commit e6f36336f8
39 changed files with 2687 additions and 219 deletions

View file

@ -48,8 +48,21 @@ public class CategoriesModel{
*/
public Comparator<CategoryItem> sortBySimilarity(final String filter) {
Comparator<String> stringSimilarityComparator = StringSortingUtils.sortBySimilarity(filter);
return (firstItem, secondItem) -> stringSimilarityComparator
.compare(firstItem.getName(), secondItem.getName());
return (firstItem, secondItem) -> {
//if the category is selected, it should get precedence
if (null != firstItem && firstItem.isSelected()) {
if (null != secondItem && secondItem.isSelected()) {
return stringSimilarityComparator
.compare(firstItem.getName(), secondItem.getName());
}
return -1;
}
if (null != secondItem && secondItem.isSelected()) {
return 1;
}
return stringSimilarityComparator
.compare(firstItem.getName(), secondItem.getName());
};
}
/**
@ -255,4 +268,38 @@ public class CategoriesModel{
this.categoriesCache.clear();
this.selectedCategories.clear();
}
/**
* Search for categories
*/
public Observable<CategoryItem> searchCategories(String query, List<String> imageTitleList) {
if (TextUtils.isEmpty(query)) {
return gpsCategories()
.concatWith(titleCategories(imageTitleList))
.concatWith(recentCategories());
}
return mwApi
.searchCategories(query, SEARCH_CATS_LIMIT)
.map(s -> new CategoryItem(s, false));
}
/**
* Returns default categories
*/
public Observable<CategoryItem> getDefaultCategories(List<String> titleList) {
Observable<CategoryItem> directCategories = directCategories();
if (hasDirectCategories()) {
Timber.d("Image has direct Categories");
return directCategories
.concatWith(gpsCategories())
.concatWith(titleCategories(titleList))
.concatWith(recentCategories());
} else {
Timber.d("Image has no direct Categories");
return gpsCategories()
.concatWith(titleCategories(titleList))
.concatWith(recentCategories());
}
}
}

View file

@ -0,0 +1,723 @@
package fr.free.nrw.commons.nearby;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
import com.google.gson.Gson;
import java.util.List;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
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.utils.FragmentUtils;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.PermissionUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.wikidata.WikidataEditListener;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION;
import static fr.free.nrw.commons.contributions.MainActivity.NEARBY_TAB_POSITION;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED;
public class NearbyFragment extends CommonsDaggerSupportFragment
implements LocationUpdateListener,
WikidataEditListener.WikidataP18EditListener {
@BindView(R.id.progressBar)
ProgressBar progressBar;
@BindView(R.id.bottom_sheet)
LinearLayout bottomSheet;
@BindView(R.id.bottom_sheet_details)
LinearLayout bottomSheetDetails;
@BindView(R.id.transparentView)
View transparentView;
@BindView(R.id.container_sheet)
FrameLayout frameLayout;
@BindView(R.id.loading_nearby_list)
ConstraintLayout loading_nearby_layout;
@Inject
LocationServiceManager locationManager;
@Inject
NearbyController nearbyController;
@Inject
WikidataEditListener wikidataEditListener;
@Inject Gson gson;
public NearbyMapFragment nearbyMapFragment;
private NearbyListFragment nearbyListFragment;
private static final String TAG_RETAINED_MAP_FRAGMENT = NearbyMapFragment.class.getSimpleName();
private static final String TAG_RETAINED_LIST_FRAGMENT = NearbyListFragment.class.getSimpleName();
private Bundle bundle;
private BottomSheetBehavior bottomSheetBehavior; // Behavior for list bottom sheet
private BottomSheetBehavior bottomSheetBehaviorForDetails; // Behavior for details bottom sheet
private LatLng curLatLng;
private boolean lockNearbyView; //Determines if the nearby places needs to be refreshed
public View view;
private Snackbar snackbar;
private LatLng lastKnownLocation;
private LatLng customLatLng;
private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
private BroadcastReceiver broadcastReceiver;
private boolean onOrientationChanged = false;
private boolean populateForCurrentLocation = false;
private boolean isNetworkErrorOccured = false;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_nearby, container, false);
ButterKnife.bind(this, view);
/*// Resume the fragment if exist
resumeFragment();*/
bundle = new Bundle();
initBottomSheetBehaviour();
this.view = view;
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
onOrientationChanged = true;
}
}
/**
* Hide or expand bottom sheet according to states of all sheets
*/
public void listOptionMenuItemClicked() {
if(bottomSheetBehavior.getState()==BottomSheetBehavior.STATE_COLLAPSED || bottomSheetBehavior.getState()==BottomSheetBehavior.STATE_HIDDEN){
bottomSheetBehaviorForDetails.setState(BottomSheetBehavior.STATE_HIDDEN);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}else if(bottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED){
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
/**
* Resume fragments if they exists
*/
private void resumeFragment() {
// Find the retained fragment on activity restarts
nearbyMapFragment = getMapFragment();
nearbyListFragment = getListFragment();
addNetworkBroadcastReceiver();
}
/**
* Returns the map fragment added to child fragment manager previously, if exists.
*/
private NearbyMapFragment getMapFragment() {
return (NearbyMapFragment) getChildFragmentManager().findFragmentByTag(TAG_RETAINED_MAP_FRAGMENT);
}
private void removeMapFragment() {
if (nearbyMapFragment != null) {
FragmentManager fm = getFragmentManager();
fm.beginTransaction().remove(nearbyMapFragment).commit();
nearbyMapFragment = null;
}
}
/**
* Returns the list fragment added to child fragment manager previously, if exists.
*/
private NearbyListFragment getListFragment() {
return (NearbyListFragment) getChildFragmentManager().findFragmentByTag(TAG_RETAINED_LIST_FRAGMENT);
}
private void removeListFragment() {
if (nearbyListFragment != null) {
FragmentManager fm = getFragmentManager();
fm.beginTransaction().remove(nearbyListFragment).commit();
nearbyListFragment = null;
}
}
/**
* Initialize bottom sheet behaviour (sheet for map list.) Set height 9/16 of all window.
* Add callback for bottom sheet changes, so that we can sync it with bottom sheet for details
* (sheet for nearby details)
*/
private void initBottomSheetBehaviour() {
transparentView.setAlpha(0);
bottomSheet.getLayoutParams().height = getActivity().getWindowManager()
.getDefaultDisplay().getHeight() / 16 * 9;
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(View bottomSheet, int unusedNewState) {
prepareViewsForSheetPosition();
}
@Override
public void onSlide(View bottomSheet, float slideOffset) {
}
});
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
bottomSheetBehaviorForDetails = BottomSheetBehavior.from(bottomSheetDetails);
bottomSheetBehaviorForDetails.setState(BottomSheetBehavior.STATE_HIDDEN);
}
/**
* Sets camera position, zoom level according to sheet positions
*/
private void prepareViewsForSheetPosition() {
// TODO
}
@Override
public void onLocationChangedSignificantly(LatLng latLng) {
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
}
@Override
public void onLocationChangedSlightly(LatLng latLng) {
refreshView(LOCATION_SLIGHTLY_CHANGED);
}
@Override
public void onLocationChangedMedium(LatLng latLng) {
// For nearby map actions, there are no differences between 500 meter location change (aka medium change) and slight change
refreshView(LOCATION_SLIGHTLY_CHANGED);
}
@Override
public void onWikidataEditSuccessful() {
// Do not refresh nearby map if we are checking other areas with search this area button
if (nearbyMapFragment != null && !nearbyMapFragment.searchThisAreaModeOn) {
refreshView(MAP_UPDATED);
}
}
/**
* This method should be the single point to load/refresh nearby places
*
* @param locationChangeType defines if location changed significantly or slightly
*/
private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) {
Timber.d("Refreshing nearby places");
if (lockNearbyView) {
return;
}
if (!NetworkUtils.isInternetConnectionEstablished(getActivity())) {
hideProgressBar();
return;
}
registerLocationUpdates();
LatLng lastLocation = locationManager.getLastLocation();
if (curLatLng != null && curLatLng.equals(lastLocation)
&& !locationChangeType.equals(MAP_UPDATED)) { //refresh view only if location has changed
// Two exceptional cases to refresh nearby map manually.
if (!onOrientationChanged) {
return;
}
}
curLatLng = lastLocation;
if (locationChangeType.equals(PERMISSION_JUST_GRANTED)) {
curLatLng = lastKnownLocation;
}
if (curLatLng == null) {
Timber.d("Skipping update of nearby places as location is unavailable");
return;
}
/*
onOrientation changed is true whenever activities orientation changes. After orientation
change we want to refresh map significantly, doesn't matter if location changed significantly
or not. Thus, we included onOrientationChanged boolean to if clause
*/
if (locationChangeType.equals(LOCATION_SIGNIFICANTLY_CHANGED)
|| locationChangeType.equals(PERMISSION_JUST_GRANTED)
|| locationChangeType.equals(MAP_UPDATED)
|| onOrientationChanged) {
progressBar.setVisibility(View.VISIBLE);
//TODO: This hack inserts curLatLng before populatePlaces is called (see #1440). Ideally a proper fix should be found
String gsonCurLatLng = gson.toJson(curLatLng);
bundle.clear();
bundle.putString("CurLatLng", gsonCurLatLng);
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curLatLng, curLatLng, false, true))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::populatePlaces,
throwable -> {
Timber.d(throwable);
showErrorMessage(getString(R.string.error_fetching_nearby_places));
progressBar.setVisibility(View.GONE);
}));
} else if (locationChangeType
.equals(LOCATION_SLIGHTLY_CHANGED) && nearbyMapFragment != null) {
String gsonCurLatLng = gson.toJson(curLatLng);
bundle.putString("CurLatLng", gsonCurLatLng);
updateMapFragment(false,true, null, null);
}
if (nearbyMapFragment != null && nearbyMapFragment.searchThisAreaButton != null) {
nearbyMapFragment.searchThisAreaButton.setVisibility(View.GONE);
}
}
/**
* This method should be used with "Search this are button". This method will search nearby
* points around any custom location (target location when user clicked on search this area)
* button. It populates places for custom location.
* @param customLatLng Custom area which we will search around
*/
void refreshViewForCustomLocation(LatLng customLatLng, boolean refreshForCurrentLocation) {
if (customLatLng == null) {
// If null, return
return;
}
populateForCurrentLocation = refreshForCurrentLocation;
this.customLatLng = customLatLng;
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curLatLng, customLatLng, false, populateForCurrentLocation))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::populatePlacesFromCustomLocation,
throwable -> {
Timber.d(throwable);
showErrorMessage(getString(R.string.error_fetching_nearby_places));
}));
if (nearbyMapFragment != null) {
nearbyMapFragment.searchThisAreaButton.setVisibility(View.GONE);
}
}
/**
* Populates places for custom location, should be used for finding nearby places around a
* location where you are not at.
* @param nearbyPlacesInfo This variable has place list information and distances.
*/
private void populatePlacesFromCustomLocation(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
if (nearbyMapFragment != null) {
nearbyMapFragment.searchThisAreaButtonProgressBar.setVisibility(View.GONE);
}
if (nearbyMapFragment != null && curLatLng != null) {
if (!populateForCurrentLocation) {
nearbyMapFragment.updateMapSignificantlyForCustomLocation(customLatLng, nearbyPlacesInfo.placeList);
} else {
updateMapFragment(true,true, customLatLng, nearbyPlacesInfo);
}
updateListFragmentForCustomLocation(nearbyPlacesInfo.placeList);
}
}
/**
* Turns nearby place lists and boundary coordinates into gson and update map and list fragments
* accordingly
* @param nearbyPlacesInfo a variable holds both nearby place list and boundary coordinates
*/
private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
Timber.d("Populating nearby places");
List<Place> placeList = nearbyPlacesInfo.placeList;
LatLng[] boundaryCoordinates = nearbyPlacesInfo.boundaryCoordinates;
String gsonPlaceList = gson.toJson(placeList);
String gsonCurLatLng = gson.toJson(curLatLng);
String gsonBoundaryCoordinates = gson.toJson(boundaryCoordinates);
if (placeList.size() == 0) {
ViewUtil.showShortSnackbar(view.findViewById(R.id.container), R.string.no_nearby);
}
bundle.putString("PlaceList", gsonPlaceList);
bundle.putString("BoundaryCoord", gsonBoundaryCoordinates);
// First time to init fragments
if (nearbyMapFragment == null) {
Timber.d("Init map fragment for the first time");
lockNearbyView(true);
setMapFragment();
setListFragment();
hideProgressBar();
lockNearbyView(false);
} else {
// There are fragments, just update the map and list
Timber.d("Map fragment already exists, just update the map and list");
updateMapFragment(false,false, null, null);
updateListFragment();
}
}
/**
* Lock nearby view updates while updating map or list. Because we don't want new update calls
* when we already updating for old location update.
* @param lock true if we should lock nearby map
*/
private void lockNearbyView(boolean lock) {
if (lock) {
lockNearbyView = true;
locationManager.unregisterLocationManager();
locationManager.removeLocationListener(this);
} else {
lockNearbyView = false;
registerLocationUpdates();
locationManager.addLocationListener(this);
}
}
/**
* Updates map fragment,
* For slight update: camera follows users location
* For significant update: nearby markers are removed and new markers added again
* Slight updates stop if user is checking another area of map
*
* @param updateViaButton search this area button is clicked
* @param isSlightUpdate Means no need to update markers, just follow user location with camera
* @param customLatLng Will be used for updates for other locations than users current location.
* Ie. when we use search this area feature
* @param nearbyPlacesInfo Includes nearby places list and boundary coordinates
*/
private void updateMapFragment(boolean updateViaButton, boolean isSlightUpdate, @Nullable LatLng customLatLng, @Nullable NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
/*
Significant update means updating nearby place markers. Slightly update means only
updating current location marker and camera target.
We update our map Significantly on each 1000 meter change, but we can't never know
the frequency of nearby places. Thus we check if we are close to the boundaries of
our nearby markers, we update our map Significantly.
*/
NearbyMapFragment nearbyMapFragment = getMapFragment();
if (nearbyMapFragment != null && !nearbyMapFragment.isCurrentLocationMarkerVisible() && !onOrientationChanged) {
Timber.d("Do not update the map, user is not seeing current location marker" +
" means they are checking around and moving on map");
return;
}
if (nearbyMapFragment != null && curLatLng != null) {
hideProgressBar(); // In case it is visible (this happens, not an impossible case)
/*
* If we are close to nearby places boundaries, we need a significant update to
* get new nearby places. Check order is south, north, west, east
* */
if (nearbyMapFragment.boundaryCoordinates != null
&& !nearbyMapFragment.checkingAround
&& !nearbyMapFragment.searchThisAreaModeOn
&& !onOrientationChanged
&& (curLatLng.getLatitude() < nearbyMapFragment.boundaryCoordinates[0].getLatitude()
|| curLatLng.getLatitude() > nearbyMapFragment.boundaryCoordinates[1].getLatitude()
|| curLatLng.getLongitude() < nearbyMapFragment.boundaryCoordinates[2].getLongitude()
|| curLatLng.getLongitude() > nearbyMapFragment.boundaryCoordinates[3].getLongitude())) {
// populate places
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curLatLng, curLatLng, false, updateViaButton))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::populatePlaces,
throwable -> {
Timber.d(throwable);
showErrorMessage(getString(R.string.error_fetching_nearby_places));
progressBar.setVisibility(View.GONE);
}));
nearbyMapFragment.setBundleForUpdates(bundle);
nearbyMapFragment.updateMapSignificantlyForCurrentLocation();
updateListFragment();
return;
}
if (updateViaButton) {
nearbyMapFragment.updateMapSignificantlyForCustomLocation(customLatLng, nearbyPlacesInfo.placeList);
return;
}
/*
If this is the map update just after orientation change, then it is not a slight update
anymore. We want to significantly update map after each orientation change
*/
if (onOrientationChanged) {
isSlightUpdate = false;
onOrientationChanged = false;
}
if (isSlightUpdate) {
nearbyMapFragment.setBundleForUpdates(bundle);
nearbyMapFragment.updateMapSlightly();
} else {
nearbyMapFragment.setBundleForUpdates(bundle);
nearbyMapFragment.updateMapSignificantlyForCurrentLocation();
updateListFragment();
}
} else {
lockNearbyView(true);
setMapFragment();
setListFragment();
hideProgressBar();
lockNearbyView(false);
}
}
/**
* Updates already existing list fragment with bundle includes nearby places and boundary
* coordinates
*/
private void updateListFragment() {
nearbyListFragment.setBundleForUpdates(bundle);
nearbyListFragment.updateNearbyListSignificantly();
}
/**
* Updates nearby list for custom location, will be used with search this area method. When you
* want to search for a place where you are not at.
* @param placeList List of places around your manually chosen target location from map.
*/
private void updateListFragmentForCustomLocation(List<Place> placeList) {
nearbyListFragment.updateNearbyListSignificantlyForCustomLocation(placeList);
}
/**
* Calls fragment for map view.
*/
private void setMapFragment() {
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
nearbyMapFragment = new NearbyMapFragment();
nearbyMapFragment.setArguments(bundle);
fragmentTransaction.replace(R.id.container, nearbyMapFragment, TAG_RETAINED_MAP_FRAGMENT);
fragmentTransaction.commitAllowingStateLoss();
}
/**
* Calls fragment for list view.
*/
private void setListFragment() {
loading_nearby_layout.setVisibility(View.GONE);
frameLayout.setVisibility(View.VISIBLE);
FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
nearbyListFragment = new NearbyListFragment();
nearbyListFragment.setArguments(bundle);
fragmentTransaction.replace(R.id.container_sheet, nearbyListFragment, TAG_RETAINED_LIST_FRAGMENT);
initBottomSheetBehaviour();
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
fragmentTransaction.commitAllowingStateLoss();
}
/**
* Hides progress bar
*/
private void hideProgressBar() {
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
/**
* This method first checks if the location permissions has been granted and then register the location manager for updates.
*/
private void registerLocationUpdates() {
locationManager.registerLocationManager();
}
private void showErrorMessage(String message) {
ViewUtil.showLongToast(getActivity(), message);
}
/**
* Adds network broadcast receiver to recognize connection established
*/
private void addNetworkBroadcastReceiver() {
if (!FragmentUtils.isFragmentUIActive(this)) {
return;
}
if (broadcastReceiver != null) {
return;
}
IntentFilter intentFilter = new IntentFilter(NETWORK_INTENT_ACTION);
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (getActivity() != null) {
if (NetworkUtils.isInternetConnectionEstablished(getActivity())) {
if (isNetworkErrorOccured) {
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
isNetworkErrorOccured = false;
}
if (snackbar != null) {
snackbar.dismiss();
snackbar = null;
}
} else {
if (snackbar == null) {
snackbar = Snackbar.make(view, R.string.no_internet, Snackbar.LENGTH_INDEFINITE);
if (nearbyMapFragment != null && nearbyMapFragment.searchThisAreaButton != null) {
nearbyMapFragment.searchThisAreaButton.setVisibility(View.GONE);
}
}
isNetworkErrorOccured = true;
snackbar.show();
}
}
}
};
getActivity().registerReceiver(broadcastReceiver, intentFilter);
}
@Override
public void onResume() {
super.onResume();
// Resume the fragment if exist
if (((MainActivity) getActivity()).viewPager.getCurrentItem() == NEARBY_TAB_POSITION) {
checkPermissionsAndPerformAction(this::resumeFragment);
} else {
resumeFragment();
}
}
/**
* Perform nearby operations on nearby tab selected
* @param onOrientationChanged pass orientation changed info to fragment
*/
public void onTabSelected(boolean onOrientationChanged) {
Timber.d("On nearby tab selected");
this.onOrientationChanged = onOrientationChanged;
checkPermissionsAndPerformAction(this::performNearbyOperations);
}
private void checkPermissionsAndPerformAction(Runnable runnable) {
PermissionUtils.checkPermissionsAndPerformAction(getActivity(),
Manifest.permission.ACCESS_FINE_LOCATION,
runnable,
() -> ((MainActivity) getActivity()).viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION),
R.string.location_permission_title,
R.string.location_permission_rationale_nearby);
}
/**
* Calls nearby operations in required order.
*/
private void performNearbyOperations() {
locationManager.addLocationListener(this);
registerLocationUpdates();
lockNearbyView = false;
addNetworkBroadcastReceiver();
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
wikidataEditListener.setAuthenticationStateListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
wikidataEditListener.setAuthenticationStateListener(null);
}
@Override
public void onDetach() {
super.onDetach();
snackbar = null;
broadcastReceiver = null;
wikidataEditListener.setAuthenticationStateListener(null);
}
@Override
public void onPause() {
super.onPause();
// this means that this activity will not be recreated now, user is leaving it
// or the activity is otherwise finishing
if(getActivity().isFinishing()) {
// we will not need this fragment anymore, this may also be a good place to signal
// to the retained fragment object to perform its own cleanup.
//removeMapFragment();
removeListFragment();
}
if (broadcastReceiver != null) {
getActivity().unregisterReceiver(broadcastReceiver);
broadcastReceiver = null;
}
if (locationManager != null) {
locationManager.removeLocationListener(this);
locationManager.unregisterLocationManager();
}
}
/**
* Centers the map in nearby fragment to a given place
* @param place is new center of the map
*/
public void centerMapToPlace(Place place) {
if (nearbyMapFragment != null) {
nearbyMapFragment.centerMapToPlace(place);
}
}
public boolean isBottomSheetExpanded() { return bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED;
}
}

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,8 @@ import fr.free.nrw.commons.utils.SwipableCardView;
import fr.free.nrw.commons.utils.ViewUtil;
import timber.log.Timber;
import static fr.free.nrw.commons.contributions.MainActivity.NEARBY_TAB_POSITION;
/**
* Custom card view for nearby notification card view on main screen, above contributions list
*/
@ -66,7 +68,6 @@ public class NearbyNotificationCardView extends SwipableCardView {
progressBar = rootView.findViewById(R.id.progressBar);
setActionListeners();
}
@Override
@ -81,8 +82,16 @@ public class NearbyNotificationCardView extends SwipableCardView {
}
private void setActionListeners() {
this.setOnClickListener(view -> ((MainActivity)getContext()).viewPager.setCurrentItem(1));
private void setActionListeners(Place place) {
this.setOnClickListener(view -> {
MainActivity m = (MainActivity) getContext();
// Change to nearby tab
m.viewPager.setCurrentItem(NEARBY_TAB_POSITION);
// Center the map to the place
((NearbyFragment) m.contributionsActivityPagerAdapter.getItem(NEARBY_TAB_POSITION)).centerMapToPlace(place);
});
}
@Override public boolean onSwipe(View view) {
@ -120,6 +129,7 @@ public class NearbyNotificationCardView extends SwipableCardView {
contentLayout.setVisibility(VISIBLE);
// Make progress bar invisible once data is ready
progressBar.setVisibility(GONE);
setActionListeners(place);
// And content views visible since they are ready
notificationTitle.setVisibility(VISIBLE);
notificationDistance.setVisibility(VISIBLE);

View file

@ -116,9 +116,12 @@ public class PlaceRenderer extends Renderer<Place> {
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(lastPosition, buttonLayout.getHeight());
}
}
if (onBookmarkClick == null) {
((NearbyFragment) fragment.getParentFragment()).centerMapToPlace(place);
}
};
view.setOnClickListener(listener);
view.requestFocus();
view.setOnFocusChangeListener((view1, hasFocus) -> {
if (!hasFocus && buttonLayout.isShown()) {

View file

@ -225,6 +225,7 @@ public class NotificationActivity extends NavigationBaseActivity {
public void notificationClicked(Notification notification) {
Timber.d("Notification clicked %s", notification.link);
handleUrl(notification.link);
removeNotification(notification);
}
@Override

View file

@ -9,7 +9,6 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.daimajia.swipe.SwipeLayout;
import com.google.android.material.animation.ArgbEvaluatorCompat;
import com.pedrogomez.renderers.Renderer;
@ -32,12 +31,8 @@ public class NotificationRenderer extends Renderer<Notification> {
TextView time;
@BindView(R.id.icon)
ImageView icon;
@BindView(R.id.swipeLayout)
SwipeLayout swipeLayout;
@BindView(R.id.bottom)
LinearLayout bottomLayout;
@BindView(R.id.notification_view)
RelativeLayout notificationView;
/*@BindView(R.id.bottom)
LinearLayout bottomLayout;*/
private NotificationClicked listener;
private boolean isarchivedvisible = false;
@ -53,13 +48,6 @@ public class NotificationRenderer extends Renderer<Notification> {
listener.notificationClicked(getContent());
}
@OnClick(R.id.bottom)
void onBottomLayoutClicked(){
Notification notification = getContent();
Timber.d("NotificationID: %s", notification.notificationId);
listener.markNotificationAsRead(notification);
}
@Override
protected void setUpView(View rootView) {
@ -74,21 +62,6 @@ public class NotificationRenderer extends Renderer<Notification> {
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
ButterKnife.bind(this, inflatedView);
if (isarchivedvisible) {
swipeLayout.setSwipeEnabled(false);
}else {
swipeLayout.setSwipeEnabled(true);
}
swipeLayout.addDrag(SwipeLayout.DragEdge.Top, bottomLayout);
swipeLayout.addRevealListener(R.id.bottom_wrapper_child1, (child, edge, fraction, distance) -> {
View star = child.findViewById(R.id.star);
float d = child.getHeight() / 2 - star.getHeight() / 2;
star.setTranslationY(d * fraction);
star.setScaleX(fraction + 0.6f);
star.setScaleY(fraction + 0.6f);
int c = ArgbEvaluatorCompat.getInstance().evaluate(fraction, Color.parseColor("#dddddd"), Color.parseColor("#90960a0a"));
child.setBackgroundColor(c);
});
return inflatedView;
}

View file

@ -11,10 +11,8 @@ import fr.free.nrw.commons.upload.UploadModel;
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
import io.reactivex.Observable;
import io.reactivex.Single;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -167,7 +165,7 @@ public class UploadRemoteDataSource {
}
/**
* ask the UplaodModel for the image quality of the UploadItem
* ask the UploadModel for the image quality of the UploadItem
*
* @param uploadItem
* @param shouldValidateTitle
@ -176,4 +174,21 @@ public class UploadRemoteDataSource {
public Single<Integer> getImageQuality(UploadItem uploadItem, boolean shouldValidateTitle) {
return uploadModel.getImageQuality(uploadItem, shouldValidateTitle);
}
/**
* Ask the CategoriesModel to search categories
* @param query
* @param imageTitleList
* @return
*/
public Observable<CategoryItem> searchCategories(String query, List<String> imageTitleList) {
return categoriesModel.searchCategories(query, imageTitleList);
}
/**
* Ask the CategoriesModel for default categories
*/
public Observable<CategoryItem> getDefaultCategories(List<String> imageTitleList) {
return categoriesModel.getDefaultCategories(imageTitleList);
}
}

View file

@ -8,10 +8,8 @@ import fr.free.nrw.commons.upload.SimilarImageInterface;
import fr.free.nrw.commons.upload.UploadModel.UploadItem;
import io.reactivex.Observable;
import io.reactivex.Single;
import java.util.Comparator;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -262,4 +260,18 @@ public class UploadRepository {
public void setSelectedLicense(String licenseName) {
localDataSource.setSelectedLicense(licenseName);
}
/**
* Ask the RemoteDataSource to search for categories
*/
public Observable<CategoryItem> searchCategories(String query, List<String> imageTitleList) {
return remoteDataSource.searchCategories(query, imageTitleList);
}
/**
* Ask the RemoteDataSource to get default categories
*/
public Observable<CategoryItem> getDefaultCategories(List<String> imageTitleList) {
return remoteDataSource.getDefaultCategories(imageTitleList);
}
}

View file

@ -1,15 +1,12 @@
package fr.free.nrw.commons.settings;
import android.Manifest;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.MultiSelectListPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.SwitchPreference;
import android.text.Editable;
import android.text.TextWatcher;
@ -31,6 +28,7 @@ import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.logging.CommonsLogSender;
import fr.free.nrw.commons.ui.LongTitlePreferences.LongTitleMultiSelectListPreference;
import fr.free.nrw.commons.utils.PermissionUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.upload.Language;
@ -70,8 +68,9 @@ public class SettingsFragment extends PreferenceFragment {
return true;
});
MultiSelectListPreference multiSelectListPref = (MultiSelectListPreference) findPreference("manageExifTags");
LongTitleMultiSelectListPreference multiSelectListPref = (LongTitleMultiSelectListPreference) findPreference("manageExifTags");
if (multiSelectListPref != null) {
defaultKvStore.putJson(Prefs.MANAGED_EXIF_TAGS, multiSelectListPref.getValues());
multiSelectListPref.setOnPreferenceChangeListener((preference, newValue) -> {
defaultKvStore.putJson(Prefs.MANAGED_EXIF_TAGS, newValue);
return true;

View file

@ -147,7 +147,6 @@ public class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapte
spinnerDescriptionLanguages.getContext(),
R.layout.row_item_languages_spinner, selectedLanguages,
savedLanguageValue);
languagesAdapter.notifyDataSetChanged();
spinnerDescriptionLanguages.setAdapter(languagesAdapter);
spinnerDescriptionLanguages.setOnItemSelectedListener(new OnItemSelectedListener() {
@ -162,6 +161,7 @@ public class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapte
selectedLanguages.put(adapterView, languageCode);
((SpinnerLanguagesAdapter) adapterView
.getAdapter()).selectedLangCode = languageCode;
spinnerDescriptionLanguages.setSelection(position);
Timber.d("Description language code is: "+languageCode);
}
@ -171,7 +171,7 @@ public class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapte
});
if (description.getSelectedLanguageIndex() == -1) {
if (savedLanguageValue != null) {
if (!TextUtils.isEmpty(savedLanguageValue)) {
// If user has chosen a default language from settings activity savedLanguageValue is not null
spinnerDescriptionLanguages.setSelection(languagesAdapter.getIndexOfLanguageCode(savedLanguageValue));
} else {
@ -180,7 +180,7 @@ public class DescriptionsAdapter extends RecyclerView.Adapter<DescriptionsAdapte
.getIndexOfUserDefaultLocale(spinnerDescriptionLanguages.getContext());
spinnerDescriptionLanguages.setSelection(defaultLocaleIndex, true);
} else {
spinnerDescriptionLanguages.setSelection(0);
spinnerDescriptionLanguages.setSelection(0,true);
}
}

View file

@ -105,13 +105,13 @@ public class FileProcessor implements Callback {
*/
private Set<String> getExifTagsToRedact(Context context) {
Type setType = new TypeToken<Set<String>>() {}.getType();
Set<String> prefManageEXIFTags = defaultKvStore.getJson(Prefs.MANAGED_EXIF_TAGS, setType);
Set<String> selectedExifTags = defaultKvStore.getJson(Prefs.MANAGED_EXIF_TAGS, setType);
Set<String> redactTags = new HashSet<>(Arrays.asList(
context.getResources().getStringArray(R.array.pref_exifTag_values)));
Timber.d(redactTags.toString());
if (prefManageEXIFTags != null) redactTags.removeAll(prefManageEXIFTags);
if (selectedExifTags != null) redactTags.removeAll(selectedExifTags);
else redactTags.clear();
return redactTags;
}

View file

@ -139,14 +139,7 @@ public class SpinnerLanguagesAdapter extends ArrayAdapter {
String languageCode = LangCodeUtils.fixLanguageCode(languageCodesList.get(position));
final String languageName = StringUtils.capitalize(languageNamesList.get(position));
if(savedLanguageValue.equals("")){
savedLanguageValue = Locale.getDefault().getLanguage();
}
if (!isDropDownView) {
if( !dropDownClicked){
languageCode = LangCodeUtils.fixLanguageCode(savedLanguageValue);
}
view.setVisibility(View.GONE);
if (languageCode.length() > 2)
tvLanguage.setText(languageCode.substring(0, 2));

View file

@ -80,6 +80,9 @@ public class CategoriesPresenter implements CategoriesContract.UserActionListene
.observeOn(ioScheduler)
.concatWith(
repository.searchAll(query, imageTitleList)
.mergeWith(repository.searchCategories(query, imageTitleList))
.concatWith(TextUtils.isEmpty(query) ? repository
.getDefaultCategories(imageTitleList) : Observable.empty())
)
.filter(categoryItem -> !repository.containsYear(categoryItem.getName()))
.distinct();

View file

@ -89,7 +89,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
@Override
public void onResume() {
super.onResume();
if (presenter != null && isVisible && (categories == null || categories.isEmpty())) {
if (presenter != null && isVisible) {
presenter.searchForCategories(null);
}
}
@ -193,7 +193,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
super.setUserVisibleHint(isVisibleToUser);
isVisible = isVisibleToUser;
if (presenter != null && isResumed() && (categories == null || categories.isEmpty())) {
if (presenter != null && isResumed()) {
presenter.searchForCategories(null);
}
}