* Fixes #3303
* Refactor Nearby to alig lifecycle methods

* Pass updated place list to listfragment

* Added default zoom rate to mapbox

* Removed NearbyListFragmet and added the ui login to handle the same in NearbyParentFragment

* More code refactor
* Make BottomSheetList hideable
* onFragmentHide, hide the bottom sheets

* BigFix, Fragmet visibility, register/un-register camera move based on fragments lifecycke

* More code refactor
* Let the ExecutorUtil have non-ui thread
* Add Location Marker on non-ui thread (the non-ui stuffs)

* BugFixes
* Removed configchanges "orientation" from MainActivity in Manifest (That was messing with the fragment lifecycle)
* Some null checks
* Initialise lastknown location in onMapReady

* UI Fixes
* Adjusted UI to support dark and no-dark themes both (in nearby)
* Do not update map on Location Slightly changed

* Fix failing test case, let TestCommonsApplication extend Application instead of CommonsApplication

* start map view when nearby is visible

* start the map when NearbyFragmet is visible

* More bugfixes
* Added DUMMY view for NearbyPresenter's onDetach State
* Added a wrapper frame layout parent for MapView to preven it from drawing above other views

* More bugfixes (Fixes #3287)
* Gray out the un-selected markers from the nearby filter list

* BugFix, search this area should search the nearby places for the current camera position

* More BugFixes
* Handle null primitives with proxy
* Current location marker flow via permission flow

* onCameraMove should have null-check on NearbyController.latestSearchLocation instead of currentLocation

* Search for places around last focus location

* Handle location updates
* If the user is browsing the map, donot update the map with current location
This commit is contained in:
Ashish Kumar 2020-02-04 14:35:29 +05:30 committed by GitHub
parent a6d2523588
commit 05e98307be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 915 additions and 1254 deletions

View file

@ -19,6 +19,7 @@ import com.facebook.imagepipeline.producers.Consumer;
import com.facebook.imagepipeline.producers.FetchState;
import com.facebook.imagepipeline.producers.NetworkFetcher;
import com.facebook.imagepipeline.producers.ProducerContext;
import com.mapbox.mapboxsdk.Mapbox;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
@ -134,6 +135,7 @@ public class CommonsApplication extends Application {
INSTANCE = this;
ACRA.init(this);
Mapbox.getInstance(this, getString(R.string.mapbox_commons_app_token));
ApplicationlessInjection
.getInstance(this)

View file

@ -35,7 +35,6 @@ import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.notification.NotificationController;
@ -78,6 +77,7 @@ public class MainActivity extends NavigationBaseActivity implements FragmentMana
private MenuItem notificationsMenuItem;
private TextView notificationCount;
private NearbyParentFragment nearbyParentFragment;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -188,8 +188,6 @@ public class MainActivity extends NavigationBaseActivity implements FragmentMana
tabLayout.getTabAt(NEARBY_TAB_POSITION).select();
isContributionsFragmentVisible = false;
updateMenuItem();
// Do all permission and GPS related tasks on tab selected, not on create
NearbyParentFragmentPresenter.getInstance().onTabSelected();
break;
default:
tabLayout.getTabAt(CONTRIBUTIONS_TAB_POSITION).select();
@ -265,7 +263,9 @@ public class MainActivity extends NavigationBaseActivity implements FragmentMana
}
} else if (getSupportFragmentManager().findFragmentByTag(nearbyFragmentTag) != null && !isContributionsFragmentVisible) {
// Means that nearby fragment is visible (not contributions fragment)
NearbyParentFragmentPresenter.getInstance().backButtonClicked();
if (null != nearbyParentFragment) {
nearbyParentFragment.backButtonClicked();
}
} else {
super.onBackPressed();
}
@ -380,12 +380,13 @@ public class MainActivity extends NavigationBaseActivity implements FragmentMana
}
case 1:
NearbyParentFragment retainedNearbyFragment = getNearbyFragment(1);
if (retainedNearbyFragment != null) {
return retainedNearbyFragment;
nearbyParentFragment = getNearbyFragment(1);
if (nearbyParentFragment != null) {
return nearbyParentFragment;
} else {
// If we reach here, retainedNearbyFragment is null
return new NearbyParentFragment();
nearbyParentFragment=new NearbyParentFragment();
return nearbyParentFragment;
}
default:
return null;

View file

@ -13,8 +13,6 @@ import fr.free.nrw.commons.explore.images.SearchImageFragment;
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment;
import fr.free.nrw.commons.media.MediaDetailFragment;
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
import fr.free.nrw.commons.nearby.fragments.NearbyListFragment;
import fr.free.nrw.commons.nearby.fragments.NearbyMapFragment;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
import fr.free.nrw.commons.review.ReviewImageFragment;
import fr.free.nrw.commons.settings.SettingsFragment;
@ -40,9 +38,6 @@ public abstract class FragmentBuilderModule {
@ContributesAndroidInjector
abstract MediaDetailPagerFragment bindMediaDetailPagerFragment();
@ContributesAndroidInjector
abstract NearbyListFragment bindNearbyListFragment();
@ContributesAndroidInjector
abstract SettingsFragment bindSettingsFragment();
@ -64,9 +59,6 @@ public abstract class FragmentBuilderModule {
@ContributesAndroidInjector
abstract ContributionsFragment bindContributionsFragment();
@ContributesAndroidInjector
abstract NearbyMapFragment bindNearbyMapFragment();
@ContributesAndroidInjector
abstract NearbyParentFragment bindNearbyParentFragment();

View file

@ -9,6 +9,8 @@ import android.widget.CompoundButton;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatCheckBox;
import java.util.List;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
@ -25,6 +27,16 @@ public class CheckBoxTriStates extends AppCompatCheckBox {
private int state;
private Callback callback;
public interface Callback{
void filterByMarkerType(@Nullable List<Label> selectedLabels, int state, boolean b, boolean b1);
}
public void setCallback(Callback callback) {
this.callback = callback;
}
/**
* This is the listener set to the super class which is going to be evoke each
* time the check state has changed.
@ -87,7 +99,7 @@ public class CheckBoxTriStates extends AppCompatCheckBox {
}
if (NearbyController.currentLocation != null) {
NearbyParentFragmentPresenter.getInstance().filterByMarkerType(null, state, false, true);
callback.filterByMarkerType(null, state, false, true);
}
updateBtn();
}

View file

@ -17,10 +17,6 @@ public class NearbyAdapterFactory {
private Fragment fragment;
private ContributionController controller;
NearbyAdapterFactory(){
}
public NearbyAdapterFactory(Fragment fragment, ContributionController controller) {
this.fragment = fragment;
this.controller = controller;

View file

@ -27,20 +27,24 @@ public class NearbyFilterSearchRecyclerViewAdapter
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;
private Callback callback;
RecyclerView.SmoothScroller smoothScroller;
public void setCallback(Callback callback) {
this.callback = callback;
}
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() {
@ -66,7 +70,7 @@ public class NearbyFilterSearchRecyclerViewAdapter
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = inflater.inflate(R.layout.nearby_search_list_item, parent, false);
View itemView = inflater.inflate(callback.isDarkTheme()?R.layout.nearby_search_list_item_dark:R.layout.nearby_search_list_item, parent, false);
return new RecyclerViewHolder(itemView);
}
@ -76,9 +80,9 @@ public class NearbyFilterSearchRecyclerViewAdapter
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.setBackgroundColor(label.isSelected() ? ContextCompat.getColor(context, R.color.divider_grey) : callback.isDarkTheme()?Color.BLACK:Color.WHITE);
holder.placeTypeLayout.setOnClickListener(view -> {
NearbyParentFragmentPresenter.getInstance().setCheckboxUnknown();
callback.setCheckboxUnknown();
if (label.isSelected()) {
selectedLabels.remove(label);
} else {
@ -86,7 +90,7 @@ public class NearbyFilterSearchRecyclerViewAdapter
}
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);
callback.filterByMarkerType(selectedLabels, 0, false, false);
});
}
@ -161,8 +165,13 @@ public class NearbyFilterSearchRecyclerViewAdapter
notifyDataSetChanged();
}
public void setRecyclerViewAdapterNeutral() {
state = CheckBoxTriStates.UNKNOWN;
public interface Callback{
void setCheckboxUnknown();
void filterByMarkerType(ArrayList<Label> selectedLabels, int i, boolean b, boolean b1);
boolean isDarkTheme();
}
}

View file

@ -34,11 +34,9 @@ import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.nearby.fragments.NearbyMapFragment;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
import timber.log.Timber;
import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.TAG_RETAINED_MAP_FRAGMENT;
import static fr.free.nrw.commons.theme.NavigationBaseActivity.startActivityWithFlags;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
@ -121,7 +119,7 @@ public class PlaceRenderer extends Renderer<Place> {
}
}
if (onBookmarkClick == null) {
((NearbyParentFragment) fragment.getParentFragment()).centerMapToPlace(place);
((NearbyParentFragment) fragment).centerMapToPlace(place);
}
};
view.setOnClickListener(listener);
@ -194,8 +192,7 @@ public class PlaceRenderer extends Renderer<Place> {
onBookmarkClick.onClick();
}
else {
((NearbyMapFragment)(fragment.getParentFragment()).getChildFragmentManager().
findFragmentByTag(TAG_RETAINED_MAP_FRAGMENT)).
((NearbyParentFragment) (fragment.getParentFragment())).
updateMarker(isBookmarked, place, null);
}
}

View file

@ -1,36 +0,0 @@
package fr.free.nrw.commons.nearby.contract;
import com.mapbox.mapboxsdk.annotations.Marker;
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;
/**
* This interface defines specific View and UserActions for map
* part of the nearby.
*/
public interface NearbyMapContract {
interface View{
void updateMapMarkers(LatLng latLng, List<Place> placeList, Marker selectedMarker, NearbyParentFragmentPresenter nearbyParentFragmentPresenter);
void updateMapToTrackPosition(LatLng curLatLng);
void addCurrentLocationMarker(LatLng curLatLng);
void addNearbyMarkersToMapBoxMap(List<NearbyBaseMarker> baseMarkerOptions, Marker marker, NearbyParentFragmentPresenter nearbyParentFragmentPresenter);
LatLng getCameraTarget();
MapboxMap getMapboxMap();
void viewsAreAssignedToPresenter(NearbyParentFragmentContract.ViewsAreReadyCallback viewsAreReadyCallback);
void addOnCameraMoveListener(MapboxMap.OnCameraMoveListener onCameraMoveListener);
void centerMapToPlace(Place place, boolean isPortraitMode);
void removeCurrentLocationMarker();
List<NearbyBaseMarker> getBaseMarkerOptions();
void filterMarkersByLabels(List<Label> labelList, boolean displayExists, boolean displayNeeds, boolean filterForPlaceState, boolean filterForAllNoneType);
void filterOutAllMarkers();
void displayAllMarkers();
}
}

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.nearby.contract;
import android.content.Context;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.maps.MapboxMap;
@ -9,16 +11,16 @@ 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.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
public interface NearbyParentFragmentContract {
interface View {
void registerLocationUpdates(LocationServiceManager locationServiceManager);
boolean isNetworkConnectionEstablished();
void addNetworkBroadcastReceiver();
void listOptionMenuItemClicked();
void populatePlaces(LatLng curlatLng, LatLng searchLatLng);
void populatePlaces(LatLng curlatLng);
boolean isListBottomSheetExpanded();
void checkPermissionsAndPerformAction(Runnable runnable);
void displayLoginSkippedWarning();
@ -29,7 +31,7 @@ public interface NearbyParentFragmentContract {
void hideBottomSheet();
void hideBottomDetailsSheet();
void displayBottomSheetWithInfo(Marker marker);
void addOnCameraMoveListener(MapboxMap.OnCameraMoveListener onCameraMoveListener);
void addOnCameraMoveListener();
void addSearchThisAreaButtonAction();
void setSearchThisAreaButtonVisibility(boolean isVisible);
void setProgressBarVisibility(boolean isVisible);
@ -44,6 +46,29 @@ public interface NearbyParentFragmentContract {
void setFilterState();
void disableFABRecenter();
void enableFABRecenter();
void addCurrentLocationMarker(LatLng curLatLng);
void updateMapToTrackPosition(LatLng curLatLng);
Context getContext();
void updateMapMarkers(List<NearbyBaseMarker> nearbyBaseMarkers, Marker selectedMarker);
void filterOutAllMarkers();
void displayAllMarkers();
void filterMarkersByLabels(List<Label> selectedLabels, boolean existsSelected, boolean needPhotoSelected, boolean filterForPlaceState, boolean filterForAllNoneType);
LatLng getCameraTarget();
void centerMapToPlace(Place placeToCenter);
void updateListFragment(List<Place> placeList);
LatLng getLastLocation();
com.mapbox.mapboxsdk.geometry.LatLng getLastFocusLocation();
}
interface NearbyListView {
@ -51,19 +76,21 @@ public interface NearbyParentFragmentContract {
}
interface UserActions {
void onTabSelected();
void checkForPermission();
void updateMapAndList(LocationServiceManager.LocationChangeType locationChangeType, LatLng cameraTarget);
void updateMapAndList(LocationServiceManager.LocationChangeType locationChangeType);
void lockUnlockNearby(boolean isNearbyLocked);
void attachView(View view);
void detachView();
void setActionListeners(JsonKvStore applicationKvStore);
void backButtonClicked();
MapboxMap.OnCameraMoveListener onCameraMove(MapboxMap mapboxMap);
void onCameraMove(com.mapbox.mapboxsdk.geometry.LatLng latLng);
void filterByMarkerType(List<Label> selectedLabels, int state, boolean filterForPlaceState, boolean filterForAllNoneType);
void updateMapMarkersToController(List<NearbyBaseMarker> nearbyBaseMarkers);
void searchViewGainedFocus();
void setCheckboxUnknown();
}
interface ViewsAreReadyCallback {
void nearbyFragmentsAreReady();
}
}

View file

@ -1,104 +0,0 @@
package fr.free.nrw.commons.nearby.fragments;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.pedrogomez.renderers.RVRendererAdapter;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.NearbyAdapterFactory;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
import timber.log.Timber;
public class NearbyListFragment extends CommonsDaggerSupportFragment implements NearbyParentFragmentContract.NearbyListView {
private static final Type LIST_TYPE = new TypeToken<List<Place>>() {
}.getType();
private static final Type CUR_LAT_LNG_TYPE = new TypeToken<LatLng>() {
}.getType();
private NearbyAdapterFactory adapterFactory;
private RecyclerView recyclerView;
@Inject ContributionController controller;
@Inject Gson gson;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
Timber.d("NearbyListFragment created");
View view = inflater.inflate(R.layout.fragment_nearby_list, container, false);
recyclerView = view.findViewById(R.id.listView);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
adapterFactory = new NearbyAdapterFactory(this, controller);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// Check that this is the first time view is created,
// to avoid double list when screen orientation changed
Bundle bundle = this.getArguments();
recyclerView.setAdapter(adapterFactory.create(getPlaceListFromBundle(bundle)));
}
/**
* When user moved too much, we need to update nearby list too. This operation is made by passing
* a bundle from NearbyFragment to NearbyListFragment and NearbyMapFragment. This method extracts
* place list from bundle to a list variable.
* @param bundle Bundle passed from NearbyFragment on users significant moving
* @return List of new nearby places
*/
private List<Place> getPlaceListFromBundle(Bundle bundle) {
List<Place> placeList = Collections.emptyList();
if (bundle != null) {
String gsonPlaceList = bundle.getString("PlaceList", "[]");
placeList = gson.fromJson(gsonPlaceList, LIST_TYPE);
String gsonLatLng = bundle.getString("CurLatLng");
LatLng curLatLng = gson.fromJson(gsonLatLng, CUR_LAT_LNG_TYPE);
placeList = NearbyController.loadAttractionsFromLocationToPlaces(curLatLng, placeList);
}
return placeList;
}
@Override
public void updateListFragment(List<Place> placeList) {
Timber.d("Update list fragment");
adapterFactory.updateAdapterData(placeList, (RVRendererAdapter<Place>) recyclerView.getAdapter());
}
}

View file

@ -1,596 +0,0 @@
package fr.free.nrw.commons.nearby.fragments;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import com.mapbox.mapboxsdk.annotations.Icon;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.Polygon;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.maps.MapFragment;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
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;
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;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.contract.NearbyMapContract;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
import fr.free.nrw.commons.utils.LocationUtils;
import fr.free.nrw.commons.utils.UiUtils;
import timber.log.Timber;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
/**
* Support Fragment wrapper around a map view.
* <p>
* A Map component in an app. This fragment is the simplest way to place a map in an application.
* It's a wrapper around a view of a map to automatically handle the necessary life cycle needs.
* Being a fragment, this component can be added to an activity's layout or can dynamically be added
* using a FragmentManager.
* </p>
* <p>
* To get a reference to the MapView, use {@link #getMapAsync(OnMapReadyCallback)}}
* </p>
*
* @see #getMapAsync(OnMapReadyCallback)
*/
public class NearbyMapFragment extends CommonsDaggerSupportFragment
implements OnMapReadyCallback, NearbyMapContract.View{
@Inject
BookmarkLocationsDao bookmarkLocationDao;
private final List<OnMapReadyCallback> mapReadyCallbackList = new ArrayList<>();
private MapFragment.OnMapViewReadyCallback mapViewReadyCallback;
private MapboxMap mapboxMap;
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;
private static final double ZOOM_LEVEL = 14f;
/**
* Creates a MapFragment instance
*
* @param mapboxMapOptions The configuration options to be used.
* @return MapFragment created.
*/
@NonNull
public static NearbyMapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOptions) {
NearbyMapFragment mapFragment = new NearbyMapFragment();
mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions));
return mapFragment;
}
/**
* Called when the context attaches to this fragment.
*
* @param context the context attaching
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof MapFragment.OnMapViewReadyCallback) {
mapViewReadyCallback = (MapFragment.OnMapViewReadyCallback) context;
}
}
/**
* Called when this fragment is inflated, parses XML tag attributes.
*
* @param context The context inflating this fragment.
* @param attrs The XML tag attributes.
* @param savedInstanceState The saved instance state for the map fragment.
*/
@Override
public void onInflate(@NonNull Context context, AttributeSet attrs, Bundle savedInstanceState) {
super.onInflate(context, attrs, savedInstanceState);
setArguments(MapFragmentUtils.createFragmentArgs(MapboxMapOptions.createFromAttributes(context, attrs)));
}
/**
* Creates the fragment view hierarchy.
*
* @param inflater Inflater used to inflate content.
* @param container The parent layout for the map fragment.
* @param savedInstanceState The saved instance state for the map fragment.
* @return The view created
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
Context context = inflater.getContext();
map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments()));
return map;
}
/**
* Called when the fragment view hierarchy is created.
*
* @param view The content view of the fragment
* @param savedInstanceState THe saved instance state of the framgnt
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
map.onCreate(savedInstanceState);
map.getMapAsync(this);
// notify listeners about MapView creation
if (mapViewReadyCallback != null) {
mapViewReadyCallback.onMapViewReady(map);
}
}
@Override
public void onMapReady(@NonNull MapboxMap mapboxMap) {
this.mapboxMap = mapboxMap;
for (OnMapReadyCallback onMapReadyCallback : mapReadyCallbackList) {
onMapReadyCallback.onMapReady(mapboxMap);
}
}
/**
* Called when the fragment is visible for the users.
*/
@Override
public void onStart() {
super.onStart();
map.onStart();
}
/**
* Called when the fragment is ready to be interacted with.
*/
@Override
public void onResume() {
super.onResume();
map.onResume();
}
/**
* Called when the fragment is pausing.
*/
@Override
public void onPause() {
super.onPause();
map.onPause();
}
/**
* Called when the fragment state needs to be saved.
*
* @param outState The saved state
*/
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
if (map != null) {
map.onSaveInstanceState(outState);
}
}
/**
* Called when the fragment is no longer visible for the user.
*/
@Override
public void onStop() {
super.onStop();
map.onStop();
}
/**
* Called when the fragment receives onLowMemory call from the hosting Activity.
*/
@Override
public void onLowMemory() {
super.onLowMemory();
if (map != null) {
map.onLowMemory();
}
}
/**
* Called when the fragment is view hierarchy is being destroyed.
*/
@Override
public void onDestroyView() {
super.onDestroyView();
map.onDestroy();
}
/**
* Called when the fragment is destroyed.
*/
@Override
public void onDestroy() {
super.onDestroy();
mapReadyCallbackList.clear();
}
/**
* Sets a callback object which will be triggered when the MapboxMap instance is ready to be used.
*
* @param onMapReadyCallback The callback to be invoked.
*/
public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) {
if (mapboxMap == null) {
mapReadyCallbackList.add(onMapReadyCallback);
} else {
onMapReadyCallback.onMapReady(mapboxMap);
}
}
/**
* Clears map, adds nearby markers to map
* @param latLng current location
* @param placeList all places will be displayed on the map
* @param selectedMarker clicked marker by user
* @param nearbyParentFragmentPresenter presenter
*/
@Override
public void updateMapMarkers(LatLng latLng, List<Place> placeList
, Marker selectedMarker
, NearbyParentFragmentPresenter nearbyParentFragmentPresenter) {
Timber.d("Updates map markers");
customBaseMarkerOptions = NearbyController
.loadAttractionsFromLocationToBaseMarkerOptions(latLng, // Curlatlang will be used to calculate distances
placeList,
getActivity(),
bookmarkLocationDao.getAllBookmarksLocations());
mapboxMap.clear();
addNearbyMarkersToMapBoxMap(customBaseMarkerOptions, selectedMarker, nearbyParentFragmentPresenter);
// Re-enable mapbox gestures on custom location markers load
mapboxMap.getUiSettings().setAllGesturesEnabled(true);
}
/**
* Makes map camera follow users location with animation
* @param curLatLng current location of user
*/
@Override
public void updateMapToTrackPosition(LatLng curLatLng) {
Timber.d("Updates map camera to track user position");
CameraPosition cameraPosition = new CameraPosition.Builder().target
(LocationUtils.commonsLatLngToMapBoxLatLng(curLatLng)).build();
mapboxMap.setCameraPosition(cameraPosition);
mapboxMap.animateCamera(CameraUpdateFactory
.newCameraPosition(cameraPosition), 1000);
}
/**
* Adds a marker for the user's current position. Adds a
* circle which uses the accuracy * 2, to draw a circle
* which represents the user's position with an accuracy
* of 95%.
*
* Should be called only on creation of mapboxMap, there
* is other method to update markers location with users
* move.
* @param curLatLng current location
*/
@Override
public void addCurrentLocationMarker(LatLng curLatLng) {
if (null != curLatLng) {
removeCurrentLocationMarker();
Timber.d("Adds current location marker");
Icon icon = IconFactory.getInstance(getContext())
.fromResource(R.drawable.current_location_marker);
MarkerOptions currentLocationMarkerOptions = new MarkerOptions()
.position(new com.mapbox.mapboxsdk.geometry.LatLng(curLatLng.getLatitude(),
curLatLng.getLongitude()));
currentLocationMarkerOptions.setIcon(icon); // Set custom icon
currentLocationMarker = mapboxMap.addMarker(currentLocationMarkerOptions);
List<com.mapbox.mapboxsdk.geometry.LatLng> circle = UiUtils
.createCircleArray(curLatLng.getLatitude(), curLatLng.getLongitude(),
curLatLng.getAccuracy() * 2, 100);
PolygonOptions currentLocationPolygonOptions = new PolygonOptions()
.addAll(circle)
.strokeColor(getResources().getColor(R.color.current_marker_stroke))
.fillColor(getResources().getColor(R.color.current_marker_fill));
currentLocationPolygon = mapboxMap.addPolygon(currentLocationPolygonOptions);
} else {
Timber.d("not adding current location marker..current location is null");
}
}
@Override
public void removeCurrentLocationMarker() {
if (currentLocationMarker != null) {
mapboxMap.removeMarker(currentLocationMarker);
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
* @param selectedMarker is selected marker by user
* @param nearbyParentFragmentPresenter presenter
*/
@Override
public void addNearbyMarkersToMapBoxMap(@Nullable List<NearbyBaseMarker> baseMarkerList
, Marker selectedMarker
, NearbyParentFragmentPresenter nearbyParentFragmentPresenter) {
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);
});
}
@Override
public LatLng getCameraTarget() {
return LocationUtils
.mapBoxLatLngToCommonsLatLng(mapboxMap.getCameraPosition().target);
}
@Override
public MapboxMap getMapboxMap() {
return mapboxMap;
}
@Override
public void viewsAreAssignedToPresenter(NearbyParentFragmentContract.ViewsAreReadyCallback viewsAreReadyCallback) {
}
@Override
public void addOnCameraMoveListener(MapboxMap.OnCameraMoveListener onCameraMoveListener) {
}
void setMapMarkerActions(Marker selected, NearbyParentFragmentPresenter nearbyParentFragmentPresenter) {
getMapboxMap().setOnInfoWindowCloseListener(marker -> {
if (marker == selected) {
nearbyParentFragmentPresenter.markerUnselected();
}
});
getMapboxMap().setOnMarkerClickListener(marker -> {
if (marker instanceof NearbyMarker) {
nearbyParentFragmentPresenter.markerSelected(marker);
}
return false;
});
}
/**
* Sets marker icon according to marker status. Sets title and distance.
* @param isBookmarked true if place is bookmarked
* @param place
* @param curLatLng current location
*/
public void updateMarker(boolean isBookmarked, Place place, @Nullable LatLng curLatLng) {
VectorDrawableCompat vectorDrawable;
if (isBookmarked) {
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()
);
}
for (Marker marker : mapboxMap.getMarkers()) {
if (marker.getTitle() != null && marker.getTitle().equals(place.getName())) {
Bitmap icon = UiUtils.getBitmap(vectorDrawable);
if (curLatLng != null) {
String distance = formatDistanceBetween(curLatLng, place.location);
place.setDistance(distance);
}
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
nearbyBaseMarker.title(place.name);
nearbyBaseMarker.position(
new com.mapbox.mapboxsdk.geometry.LatLng(
place.location.getLatitude(),
place.location.getLongitude()));
nearbyBaseMarker.place(place);
nearbyBaseMarker.icon(IconFactory.getInstance(getContext())
.fromBitmap(icon));
marker.setIcon(IconFactory.getInstance(getContext()).fromBitmap(icon));
}
}
}
/**
* 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
*/
@Override
public void centerMapToPlace(Place place, boolean isPortraitMode) {
Timber.d("Map is centered to place");
double cameraShift;
if (isPortraitMode) {
cameraShift = CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT;
} else {
cameraShift = CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE;
}
CameraPosition position = new CameraPosition.Builder()
.target(LocationUtils.commonsLatLngToMapBoxLatLng(
new LatLng(place.location.getLatitude()-cameraShift,
place.getLocation().getLongitude(),
0))) // Sets the new camera position
.zoom(ZOOM_LEVEL) // Same zoom level
.build();
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(position), 1000);
}
}

View file

@ -3,21 +3,27 @@ package fr.free.nrw.commons.nearby.presenter;
import android.view.View;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import javax.inject.Inject;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
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.MarkerPlaceGroup;
import fr.free.nrw.commons.nearby.NearbyBaseMarker;
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;
import fr.free.nrw.commons.upload.UploadContract;
import fr.free.nrw.commons.utils.LocationUtils;
import fr.free.nrw.commons.wikidata.WikidataEditListener;
import timber.log.Timber;
@ -32,118 +38,58 @@ import static fr.free.nrw.commons.nearby.CheckBoxTriStates.UNKNOWN;
public class NearbyParentFragmentPresenter
implements NearbyParentFragmentContract.UserActions,
WikidataEditListener.WikidataP18EditListener,
LocationUpdateListener,
NearbyParentFragmentContract.ViewsAreReadyCallback{
WikidataEditListener.WikidataP18EditListener,
LocationUpdateListener {
private NearbyParentFragmentContract.View nearbyParentFragmentView;
private NearbyMapContract.View nearbyMapFragmentView;
private NearbyParentFragmentContract.NearbyListView nearbyListFragmentView;
private boolean isNearbyLocked;
private LatLng curLatLng;
private boolean nearbyViewsAreReady;
private boolean onTabSelected;
private boolean mapInitialized;
private Place placeToCenter;
private boolean isPortraitMode;
private boolean placesLoadedOnce;
private LocationServiceManager locationServiceManager;
BookmarkLocationsDao bookmarkLocationDao;
public static NearbyParentFragmentPresenter presenterInstance;
private static final NearbyParentFragmentContract.View DUMMY = (NearbyParentFragmentContract.View) Proxy.newProxyInstance(
NearbyParentFragmentContract.View.class.getClassLoader(),
new Class[]{NearbyParentFragmentContract.View.class}, (proxy, method, args) -> {
if (method.getName().equals("onMyEvent")) {
return null;
} else if (String.class == method.getReturnType()) {
return "";
} else if (Integer.class == method.getReturnType()) {
return Integer.valueOf(0);
} else if (int.class == method.getReturnType()) {
return 0;
} else if (Boolean.class == method.getReturnType()) {
return Boolean.FALSE;
} else if (boolean.class == method.getReturnType()) {
return false;
} else {
return null;
}
}
);
private NearbyParentFragmentContract.View nearbyParentFragmentView = DUMMY;
private NearbyParentFragmentPresenter(NearbyParentFragmentContract.NearbyListView nearbyListFragmentView,
NearbyParentFragmentContract.View nearbyParentFragmentView,
NearbyMapContract.View nearbyMapFragmentView,
LocationServiceManager locationServiceManager) {
this.nearbyListFragmentView = nearbyListFragmentView;
this.nearbyParentFragmentView = nearbyParentFragmentView;
this.nearbyMapFragmentView = nearbyMapFragmentView;
this.nearbyMapFragmentView.viewsAreAssignedToPresenter(this);
this.locationServiceManager = locationServiceManager;
public NearbyParentFragmentPresenter(BookmarkLocationsDao bookmarkLocationDao){
this.bookmarkLocationDao=bookmarkLocationDao;
}
// static method to create instance of Singleton class
public static NearbyParentFragmentPresenter getInstance(NearbyParentFragmentContract.NearbyListView nearbyListFragmentView,
NearbyParentFragmentContract.View nearbyParentFragmentView,
NearbyMapContract.View nearbyMapFragmentView,
LocationServiceManager locationServiceManager) {
if (presenterInstance == null) {
presenterInstance = new NearbyParentFragmentPresenter(nearbyListFragmentView,
nearbyParentFragmentView,
nearbyMapFragmentView,
locationServiceManager);
}
return presenterInstance;
}
// We call this method when we are sure presenterInstance is not null
public static NearbyParentFragmentPresenter getInstance() {
return presenterInstance;
}
/**
* Note: To initialize nearby operations both views should be ready and tab is selected.
* Initializes nearby operations if nearby views are ready
*/
@Override
public void onTabSelected() {
Timber.d("Nearby tab selected");
onTabSelected = true;
// The condition for initialize operations is both having views ready and tab is selected
if (nearbyViewsAreReady && !mapInitialized) {
checkForPermission();
}
public void attachView(NearbyParentFragmentContract.View view){
this.nearbyParentFragmentView=view;
}
/**
* -To initialize nearby operations both views should be ready and tab is selected.
* Initializes nearby operations if tab selected, otherwise just sets nearby views are ready
*/
@Override
public void nearbyFragmentsAreReady() {
Timber.d("Nearby fragments are ready to be used by presenter");
nearbyViewsAreReady = true;
// The condition for initialize operations is both having views ready and tab is selected
if (onTabSelected) {
checkForPermission();
}
}
/**
* Initializes nearby operations by following these steps:
* -Checks for permission and perform if given
*/
@Override
public void checkForPermission() {
Timber.d("checking for permission");
nearbyParentFragmentView.checkPermissionsAndPerformAction(this::performNearbyOperationsIfPermissionGiven);
}
/**
* - Adds search this area button action
* - Adds camera move action listener
* - Initializes nearby operations, registers listeners, broadcast receivers etc.
*/
public void performNearbyOperationsIfPermissionGiven() {
Timber.d("Permission is given, performing actions");
this.nearbyParentFragmentView.addSearchThisAreaButtonAction();
this.nearbyParentFragmentView.addOnCameraMoveListener(onCameraMove(getMapboxMap()));
initializeMapOperations();
public void detachView(){
this.nearbyParentFragmentView=DUMMY;
}
public void initializeMapOperations() {
lockUnlockNearby(false);
registerUnregisterLocationListener(false);
nearbyParentFragmentView.addNetworkBroadcastReceiver();
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED, null);
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED);
this.nearbyParentFragmentView.addSearchThisAreaButtonAction();
this.nearbyMapFragmentView.addOnCameraMoveListener(onCameraMove(getMapboxMap()));
nearbyParentFragmentView.setCheckBoxAction();
mapInitialized = true;
}
/**
@ -204,34 +150,13 @@ public class NearbyParentFragmentPresenter
}
}
public void registerUnregisterLocationListener(boolean removeLocationListener) {
if (removeLocationListener) {
locationServiceManager.unregisterLocationManager();
locationServiceManager.removeLocationListener(this);
Timber.d("Location service manager unregistered and removed");
} else {
locationServiceManager.addLocationListener(this);
nearbyParentFragmentView.registerLocationUpdates(locationServiceManager);
Timber.d("Location service manager added and registered");
}
}
public LatLng getCameraTarget() {
return nearbyMapFragmentView.getCameraTarget();
}
public MapboxMap getMapboxMap() {
return nearbyMapFragmentView.getMapboxMap();
}
/**
* This method should be the single point to update Map and List. Triggered by location
* changes
* @param locationChangeType defines if location changed significantly or slightly
* @param cameraTarget will be used for search this area mode, when searching around
* user's camera target
*/
@Override
public void updateMapAndList(LocationServiceManager.LocationChangeType locationChangeType, LatLng cameraTarget) {
public void updateMapAndList(LocationServiceManager.LocationChangeType locationChangeType) {
Timber.d("Presenter updates map and list");
if (isNearbyLocked) {
Timber.d("Nearby is locked, so updateMapAndList returns");
@ -243,7 +168,7 @@ public class NearbyParentFragmentPresenter
return;
}
LatLng lastLocation = locationServiceManager.getLastLocation();
LatLng lastLocation = nearbyParentFragmentView.getLastLocation();
curLatLng = lastLocation;
if (curLatLng == null) {
@ -260,14 +185,13 @@ public class NearbyParentFragmentPresenter
Timber.d("LOCATION_SIGNIFICANTLY_CHANGED");
lockUnlockNearby(true);
nearbyParentFragmentView.setProgressBarVisibility(true);
nearbyParentFragmentView.populatePlaces(lastLocation, lastLocation);
nearbyParentFragmentView.populatePlaces(lastLocation);
} else if (locationChangeType.equals(SEARCH_CUSTOM_AREA)) {
Timber.d("SEARCH_CUSTOM_AREA");
lockUnlockNearby(true);
nearbyParentFragmentView.setProgressBarVisibility(true);
nearbyParentFragmentView.populatePlaces(lastLocation, cameraTarget);
nearbyParentFragmentView.populatePlaces(nearbyParentFragmentView.getCameraTarget());
} else { // Means location changed slightly, ie user is walking or driving.
Timber.d("Means location changed slightly");
if (!nearbyParentFragmentView.isSearchThisAreaButtonVisible()) { // Do not track users position if the user is checking around
@ -281,28 +205,23 @@ public class NearbyParentFragmentPresenter
* location where you are not at.
* @param nearbyPlacesInfo This variable has placeToCenter list information and distances.
*/
public void updateMapMarkers(NearbyController.NearbyPlacesInfo nearbyPlacesInfo, Marker selectedMarker) {
nearbyMapFragmentView.updateMapMarkers(nearbyPlacesInfo.curLatLng, nearbyPlacesInfo.placeList, selectedMarker, this);
nearbyMapFragmentView.addCurrentLocationMarker(nearbyPlacesInfo.curLatLng);
nearbyMapFragmentView.updateMapToTrackPosition(nearbyPlacesInfo.curLatLng);
lockUnlockNearby(false); // So that new location updates wont come
nearbyParentFragmentView.setProgressBarVisibility(false);
nearbyListFragmentView.updateListFragment(nearbyPlacesInfo.placeList);
handleCenteringTaskIfAny();
}
/**
* 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 placeToCenter list information and distances.
*/
public void updateMapMarkersForCustomLocation(NearbyController.NearbyPlacesInfo nearbyPlacesInfo, Marker selectedMarker) {
nearbyMapFragmentView.updateMapMarkers(nearbyPlacesInfo.curLatLng, nearbyPlacesInfo.placeList, selectedMarker, this);
nearbyMapFragmentView.addCurrentLocationMarker(nearbyPlacesInfo.curLatLng);
lockUnlockNearby(false); // So that new location updates wont come
nearbyParentFragmentView.setProgressBarVisibility(false);
nearbyListFragmentView.updateListFragment(nearbyPlacesInfo.placeList);
handleCenteringTaskIfAny();
public void updateMapMarkers(NearbyController.NearbyPlacesInfo nearbyPlacesInfo, Marker selectedMarker, boolean shouldTrackPosition) {
if(null!=nearbyParentFragmentView) {
List<NearbyBaseMarker> nearbyBaseMarkers = NearbyController
.loadAttractionsFromLocationToBaseMarkerOptions(nearbyPlacesInfo.curLatLng, // Curlatlang will be used to calculate distances
nearbyPlacesInfo.placeList,
nearbyParentFragmentView.getContext(),
bookmarkLocationDao.getAllBookmarksLocations());
nearbyParentFragmentView.updateMapMarkers(nearbyBaseMarkers, selectedMarker);
nearbyParentFragmentView.addCurrentLocationMarker(nearbyPlacesInfo.curLatLng);
if(shouldTrackPosition){
nearbyParentFragmentView.updateMapToTrackPosition(nearbyPlacesInfo.curLatLng);
}
lockUnlockNearby(false); // So that new location updates wont come
nearbyParentFragmentView.setProgressBarVisibility(false);
nearbyParentFragmentView.updateListFragment(nearbyPlacesInfo.placeList);
handleCenteringTaskIfAny();
}
}
/**
@ -312,25 +231,25 @@ public class NearbyParentFragmentPresenter
private void handleCenteringTaskIfAny() {
if (!placesLoadedOnce) {
placesLoadedOnce = true;
nearbyMapFragmentView.centerMapToPlace(placeToCenter, isPortraitMode);
nearbyParentFragmentView.centerMapToPlace(null);
}
}
@Override
public void onWikidataEditSuccessful() {
updateMapAndList(MAP_UPDATED, null);
updateMapAndList(MAP_UPDATED);
}
@Override
public void onLocationChangedSignificantly(LatLng latLng) {
Timber.d("Location significantly changed");
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED, null);
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED);
}
@Override
public void onLocationChangedSlightly(LatLng latLng) {
Timber.d("Location significantly changed");
updateMapAndList(LOCATION_SLIGHTLY_CHANGED, null);
updateMapAndList(LOCATION_SLIGHTLY_CHANGED);
}
@Override
@ -339,11 +258,10 @@ public class NearbyParentFragmentPresenter
}
@Override
public MapboxMap.OnCameraMoveListener onCameraMove(MapboxMap mapboxMap) {
return () -> {
public void onCameraMove(com.mapbox.mapboxsdk.geometry.LatLng latLng) {
// If our nearby markers are calculated at least once
if (NearbyController.currentLocation != null) {
double distance = mapboxMap.getCameraPosition().target.distanceTo
if (NearbyController.latestSearchLocation != null) {
double distance =latLng.distanceTo
(LocationUtils.commonsLatLngToMapBoxLatLng(NearbyController.latestSearchLocation));
if (nearbyParentFragmentView.isNetworkConnectionEstablished()) {
if (distance > NearbyController.latestSearchRadius) {
@ -355,7 +273,6 @@ public class NearbyParentFragmentPresenter
} else {
nearbyParentFragmentView.setSearchThisAreaButtonVisibility(false);
}
};
}
@Override
@ -366,19 +283,34 @@ public class NearbyParentFragmentPresenter
// Do nothing
break;
case UNCHECKED:
nearbyMapFragmentView.filterOutAllMarkers();
nearbyParentFragmentView.filterOutAllMarkers();
nearbyParentFragmentView.setRecyclerViewAdapterItemsGreyedOut();
break;
case CHECKED:
nearbyMapFragmentView.displayAllMarkers();
nearbyParentFragmentView.displayAllMarkers();
nearbyParentFragmentView.setRecyclerViewAdapterAllSelected();
break;
}
} else {
nearbyMapFragmentView.filterMarkersByLabels(selectedLabels,
nearbyParentFragmentView.filterMarkersByLabels(selectedLabels,
NearbyFilterState.getInstance().isExistsSelected(),
NearbyFilterState.getInstance().isNeedPhotoSelected(),
filterForPlaceState, filterForAllNoneType);
filterForPlaceState, false);
}
}
@Override
public void updateMapMarkersToController(List<NearbyBaseMarker> nearbyBaseMarkers) {
NearbyController.markerExistsMap = new HashMap<>();
NearbyController.markerNeedPicMap = new HashMap<>();
NearbyController.markerLabelList.clear();
for (int i = 0; i < nearbyBaseMarkers.size(); i++) {
NearbyBaseMarker nearbyBaseMarker = nearbyBaseMarkers.get(i);
NearbyController.markerLabelList.add(
new MarkerPlaceGroup(nearbyBaseMarkers.get(i).getMarker(), bookmarkLocationDao.findBookmarkLocation(nearbyBaseMarkers.get(i).getPlace()), nearbyBaseMarker.getPlace()));
//TODO: fix bookmark location
NearbyController.markerExistsMap.put((nearbyBaseMarkers.get(i).getPlace().hasWikidataLink()), nearbyBaseMarkers.get(i).getMarker());
NearbyController.markerNeedPicMap.put(((nearbyBaseMarkers.get(i).getPlace().pic == null) ? true : false), nearbyBaseMarkers.get(i).getMarker());
}
}
@ -403,11 +335,9 @@ public class NearbyParentFragmentPresenter
nearbyParentFragmentView.setSearchThisAreaButtonVisibility(false);
if (searchCloseToCurrentLocation()){
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED,
null);
updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED);
} else {
updateMapAndList(SEARCH_CUSTOM_AREA,
getCameraTarget());
updateMapAndList(SEARCH_CUSTOM_AREA);
}
};
}
@ -418,9 +348,11 @@ public class NearbyParentFragmentPresenter
* @return Returns true if search this area button is used around our current location
*/
public boolean searchCloseToCurrentLocation() {
double distance = LocationUtils.commonsLatLngToMapBoxLatLng(getCameraTarget())
.distanceTo(new com.mapbox.mapboxsdk.geometry.LatLng(NearbyController.currentLocation.getLatitude()
, NearbyController.currentLocation.getLongitude()));
if (null == nearbyParentFragmentView.getLastFocusLocation()) {
return true;
}
double distance = LocationUtils.commonsLatLngToMapBoxLatLng(nearbyParentFragmentView.getCameraTarget())
.distanceTo(nearbyParentFragmentView.getLastFocusLocation());
if (distance > NearbyController.currentLocationSearchRadius * 3 / 4) {
return false;
} else {
@ -428,16 +360,20 @@ public class NearbyParentFragmentPresenter
}
}
/**
* Centers the map in nearby fragment to a given placeToCenter
* @param place is new center of the map
*/
public void centerMapToPlace(Place place, boolean isPortraitMode) {
if (placesLoadedOnce) {
nearbyMapFragmentView.centerMapToPlace(place, isPortraitMode);
public void onMapReady() {
if(null!=nearbyParentFragmentView) {
nearbyParentFragmentView.addSearchThisAreaButtonAction();
initializeMapOperations();
}
}
public boolean areLocationsClose(LatLng cameraTarget, LatLng lastKnownLocation) {
double distance = LocationUtils.commonsLatLngToMapBoxLatLng(cameraTarget)
.distanceTo(LocationUtils.commonsLatLngToMapBoxLatLng(lastKnownLocation));
if (distance > NearbyController.currentLocationSearchRadius * 3 / 4) {
return false;
} else {
this.isPortraitMode = isPortraitMode;
this.placeToCenter = place;
return true;
}
}
}

View file

@ -4,6 +4,8 @@ import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorUtils {
@ -19,4 +21,11 @@ public class ExecutorUtils {
return uiExecutor;
}
private static final ExecutorService executor = Executors.newFixedThreadPool(3);
public static ExecutorService get() {
return executor;
}
}