io.jawg.osmcontributor.ui.fragments.MapFragment.java Source code

Java tutorial

Introduction

Here is the source code for io.jawg.osmcontributor.ui.fragments.MapFragment.java

Source

/**
 * Copyright (C) 2016 eBusiness Information
 *
 * This file is part of OSM Contributor.
 *
 * OSM Contributor is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * OSM Contributor is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OSM Contributor.  If not, see <http://www.gnu.org/licenses/>.
 */
package io.jawg.osmcontributor.ui.fragments;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.github.clans.fab.FloatingActionButton;
import com.github.clans.fab.FloatingActionMenu;
import com.mapbox.mapboxsdk.annotations.Annotation;
import com.mapbox.mapboxsdk.annotations.IconFactory;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.services.Constants;
import com.mapbox.services.commons.geojson.LineString;
import com.mapbox.services.commons.models.Position;
import com.mapbox.services.directions.v5.models.DirectionsRoute;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.inject.Inject;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
import io.jawg.osmcontributor.BuildConfig;
import io.jawg.osmcontributor.OsmTemplateApplication;
import io.jawg.osmcontributor.R;
import io.jawg.osmcontributor.api.IssueMarker;
import io.jawg.osmcontributor.api.ReportAPI;
import io.jawg.osmcontributor.model.entities.Note;
import io.jawg.osmcontributor.model.entities.Poi;
import io.jawg.osmcontributor.model.entities.PoiNodeRef;
import io.jawg.osmcontributor.model.entities.PoiTag;
import io.jawg.osmcontributor.model.entities.PoiType;
import io.jawg.osmcontributor.model.entities.PoiTypeTag;
import io.jawg.osmcontributor.model.entities.Way;
import io.jawg.osmcontributor.model.event.PleaseLoadIssuesEvent;
import io.jawg.osmcontributor.model.events.IssuesLoadedEvent;
import io.jawg.osmcontributor.model.events.PleaseDeletePoiEvent;
import io.jawg.osmcontributor.model.events.PleaseRemoveArpiMarkerEvent;
import io.jawg.osmcontributor.rest.events.SyncDownloadWayEvent;
import io.jawg.osmcontributor.rest.events.SyncFinishUploadPoiEvent;
import io.jawg.osmcontributor.rest.events.error.SyncConflictingNodeErrorEvent;
import io.jawg.osmcontributor.rest.events.error.SyncDownloadRetrofitErrorEvent;
import io.jawg.osmcontributor.rest.events.error.SyncNewNodeErrorEvent;
import io.jawg.osmcontributor.rest.events.error.SyncUnauthorizedEvent;
import io.jawg.osmcontributor.rest.events.error.SyncUploadNoteRetrofitErrorEvent;
import io.jawg.osmcontributor.rest.events.error.SyncUploadRetrofitErrorEvent;
import io.jawg.osmcontributor.rest.events.error.TooManyRequestsEvent;
import io.jawg.osmcontributor.ui.activities.EditPoiActivity;
import io.jawg.osmcontributor.ui.adapters.PoiTypePickerAdapter;
import io.jawg.osmcontributor.ui.dialogs.NoteCommentDialogFragment;
import io.jawg.osmcontributor.ui.events.edition.PleaseApplyNodeRefPositionChange;
import io.jawg.osmcontributor.ui.events.edition.PleaseApplyPoiPositionChange;
import io.jawg.osmcontributor.ui.events.map.AddressFoundEvent;
import io.jawg.osmcontributor.ui.events.map.ChangeMapModeEvent;
import io.jawg.osmcontributor.ui.events.map.DoneCalculatingDirectionsEvent;
import io.jawg.osmcontributor.ui.events.map.EditionWaysLoadedEvent;
import io.jawg.osmcontributor.ui.events.map.LastUsePoiTypeLoaded;
import io.jawg.osmcontributor.ui.events.map.MapCenterValueEvent;
import io.jawg.osmcontributor.ui.events.map.NewNoteCreatedEvent;
import io.jawg.osmcontributor.ui.events.map.NewPoiTypeSelected;
import io.jawg.osmcontributor.ui.events.map.OnBackPressedMapEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseApplyAccessibilityFilter;
import io.jawg.osmcontributor.ui.events.map.POIMarkerClick;
import io.jawg.osmcontributor.ui.events.map.PleaseApplyNoteFilterEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseApplyPoiFilter;
import io.jawg.osmcontributor.ui.events.map.PleaseChangePoiPosition;
import io.jawg.osmcontributor.ui.events.map.PleaseChangeToolbarColor;
import io.jawg.osmcontributor.ui.events.map.PleaseChangeValuesDetailNoteFragmentEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseChangeValuesDetailPoiFragmentEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseCreateNoTagPoiEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseDeletePoiFromMapEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseDisplayTutorialEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseDuplicatePoiEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseGiveMeMapCenterEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseInitializeArpiEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseInitializeNoteDrawerEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseLoadEditWaysEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseLoadLastUsedPoiType;
import io.jawg.osmcontributor.ui.events.map.PleaseOpenEditionEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseShowMeArpiglEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseSwitchMapStyleEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseSwitchWayEditionModeEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseToggleArpiEvent;
import io.jawg.osmcontributor.ui.events.map.PleaseToggleDrawer;
import io.jawg.osmcontributor.ui.events.map.PleaseToggleDrawerLock;
import io.jawg.osmcontributor.ui.events.map.PoiNoTypeCreated;
import io.jawg.osmcontributor.ui.events.map.StartDirectionsCalculationEvent;
import io.jawg.osmcontributor.ui.events.note.ApplyNewCommentFailedEvent;
import io.jawg.osmcontributor.ui.listeners.MapboxListener;
import io.jawg.osmcontributor.ui.listeners.OnZoomAnimationFinishedListener;
import io.jawg.osmcontributor.ui.listeners.OsmAnimatorUpdateListener;
import io.jawg.osmcontributor.ui.listeners.OsmCreationAnimatorUpdateListener;
import io.jawg.osmcontributor.ui.managers.tutorial.AddPoiTutoManager;
import io.jawg.osmcontributor.ui.managers.tutorial.TutorialManager;
import io.jawg.osmcontributor.ui.presenters.MapFragmentPresenter;
import io.jawg.osmcontributor.ui.utils.BitmapHandler;
import io.jawg.osmcontributor.ui.utils.MapMode;
import io.jawg.osmcontributor.ui.utils.views.ButteryProgressBar;
import io.jawg.osmcontributor.ui.utils.views.LocationMarkerViewAdapter;
import io.jawg.osmcontributor.ui.utils.views.map.marker.LocationMarkerView;
import io.jawg.osmcontributor.ui.utils.views.map.marker.LocationMarkerViewOptions;
import io.jawg.osmcontributor.ui.utils.views.map.marker.WayMarker;
import io.jawg.osmcontributor.ui.utils.views.map.marker.WayMarkerOptions;
import io.jawg.osmcontributor.utils.ConfigManager;
import io.jawg.osmcontributor.utils.DirectionsCalculator;
import io.jawg.osmcontributor.utils.FlavorUtils;
import io.jawg.osmcontributor.utils.OsmAnswers;
import io.jawg.osmcontributor.utils.StringUtils;
import io.jawg.osmcontributor.utils.ways.Geocoder;
import io.jawg.osmcontributor.utils.ways.LevelBar;
import timber.log.Timber;

public class MapFragment extends Fragment {

    private static final String LOCATION = "location";
    private static final String ZOOM_LEVEL = "zoom level";
    private static final String LEVEL = "level";
    private static final String MARKER_TYPE = "MARKER_TYPE";
    public static final String CREATION_MODE = "CREATION_MODE";
    public static final String POI_TYPE_ID = "POI_TYPE_ID";
    public static final String SELECTED_MARKER_ID = "SELECTED_MARKER_ID";
    public static final String HIDDEN_POI_TYPE = "HIDDEN_POI_TYPE";
    private static final String DISPLAY_OPEN_NOTES = "DISPLAY_OPEN_NOTES";
    private static final String DISPLAY_CLOSED_NOTES = "DISPLAY_CLOSED_NOTES";

    private LocationMarkerView markerSelected = null;

    private WayMarker wayMarkerSelected = null;

    // when resuming app we use this id to re-select the good marker
    private Long markerSelectedId = -1L;
    private MapMode mapMode = MapMode.DEFAULT;

    private boolean isMenuLoaded = false;
    private boolean pleaseSwitchToPoiSelected = false;

    private Map<Long, LocationMarkerViewOptions<Poi>> markersPoi;
    private Map<Long, LocationMarkerViewOptions<Note>> markersNotes;
    private Map<Long, LocationMarkerViewOptions<IssueMarker>> markersIssues;
    private Map<Long, WayMarkerOptions> markersNodeRef;

    private List<IssueMarker> markerIssues; //Our custom markers

    private int maxPoiType;
    private PoiType poiTypeSelected;
    private ButteryProgressBar progressBar;
    private MapFragmentPresenter presenter;
    private MapboxListener mapboxListener;

    public MapboxMap mapboxMap;

    private Unbinder unbinder;

    private Location lastLocation;

    private boolean route_mode;
    private List<Poi> selectedPois;

    @Inject
    BitmapHandler bitmapHandler;

    @Inject
    EventBus eventBus;

    @BindView(R.id.mapview)
    MapView mapView;

    @BindView(R.id.poi_detail_wrapper)
    RelativeLayout poiDetailWrapper;

    @BindView(R.id.progressbar)
    RelativeLayout progressbarWrapper;

    @BindView(R.id.note_detail_wrapper)
    RelativeLayout noteDetailWrapper;

    @BindView(R.id.osm_copyright)
    TextView osmCopyrightTextView;

    @Inject
    SharedPreferences sharedPreferences;

    @Inject
    ConfigManager configManager;

    @BindView(R.id.zoom_level)
    TextView zoomLevelText;

    private DirectionsCalculator dc;
    private Annotation polyline;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        OsmTemplateApplication application = ((OsmTemplateApplication) getActivity().getApplication());
        application.getOsmTemplateComponent().inject(this);

        measureMaxPoiType();

        presenter = new MapFragmentPresenter(this);
        mapboxListener = new MapboxListener(this, eventBus);

        dc = new DirectionsCalculator(eventBus);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_map, container, false);

        markersPoi = new HashMap<>();
        markersNotes = new HashMap<>();
        markersNodeRef = new HashMap<>();
        markerIssues = new LinkedList<>();

        unbinder = ButterKnife.bind(this, rootView);
        setHasOptionsMenu(true);

        zoomVectorial = configManager.getZoomVectorial();

        if (savedInstanceState != null) {
            currentLevel = savedInstanceState.getDouble(LEVEL);
            selectedMarkerType = LocationMarkerView.MarkerType.values()[savedInstanceState.getInt(MARKER_TYPE)];
        }

        instantiateProgressBar();
        instantiateMapView(savedInstanceState);
        instantiateLevelBar();
        instantiatePoiTypePicker();
        instantiateCopyrightBar();

        instantiateSelectedPois();

        eventBus.register(this);
        eventBus.register(presenter);
        return rootView;
    }

    private void instantiateCopyrightBar() {
        osmCopyrightTextView.setText(Html.fromHtml(getString(R.string.osm_copyright)));
    }

    private void instantiateProgressBar() {
        progressBar = new ButteryProgressBar(getActivity());
        progressBar.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, 24));
        RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);

        p.addRule(RelativeLayout.BELOW, R.id.poi_type_value_wrapper);
        progressBar.setVisibility(View.GONE);
        progressBar.setLayoutParams(p);
        progressbarWrapper.addView(progressBar);
    }

    private void instantiateLevelBar() {
        Drawable d = ContextCompat.getDrawable(getActivity(), R.drawable.level_thumb);
        levelBar.setThumb(d);
        levelBar.setDrawableHeight(d.getIntrinsicHeight());
        levelBar.setLevel(currentLevel);
        levelBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                Timber.v("onProgressChanged");
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }

    private void instantiateMapBox(Bundle savedInstanceState) {
        // LocationView settings
        mapboxMap.getMyLocationViewSettings().setAccuracyAlpha(0);
        mapboxMap.getMyLocationViewSettings()
                .setForegroundTintColor(ContextCompat.getColor(getActivity(), R.color.colorPrimaryTransparent));

        // Set the map center and zoom to the saved values or use the default values
        getUserLocation();
        CameraPosition.Builder cameraBuilder = new CameraPosition.Builder();
        if (savedInstanceState == null) {
            double lat = sharedPreferences.getFloat("latitude", 0);
            double lon = sharedPreferences.getFloat("longitude", 0);
            if (lat != 0 && lon != 0) {
                cameraBuilder.target(new LatLng(lat, lon)).zoom(11);
            }
        } else {
            cameraBuilder.target((LatLng) savedInstanceState.getParcelable(LOCATION))
                    .zoom(savedInstanceState.getFloat(ZOOM_LEVEL));
        }

        mapboxMap.setCameraPosition(cameraBuilder.build());
        eventBus.post(new PleaseInitializeArpiEvent());
        presenter.setForceRefreshPoi();
        presenter.setForceRefreshNotes();
        presenter.loadPoisIfNeeded();
        eventBus.post(new PleaseInitializeNoteDrawerEvent(displayOpenNotes, displayClosedNotes));
        if (poiTypePickerAdapter != null) {
            poiTypePickerAdapter.setExpertMode(
                    sharedPreferences.getBoolean(getString(R.string.shared_prefs_expert_mode), false));
        }
        mapboxListener.listen(mapboxMap, mapView);

        mapboxMap.getMarkerViewManager()
                .addMarkerViewAdapter(new LocationMarkerViewAdapter(getActivity().getApplicationContext()));
    }

    private void instantiateSelectedPois() {
        selectedPois = new ArrayList<>();
    }

    private void instantiateMapView(final Bundle savedInstanceState) {
        if (mapView != null) {
            mapMode = MapMode.DEFAULT;
            mapView.onCreate(savedInstanceState);
            mapView.setStyleUrl(BuildConfig.MAP_STYLE);
            mapView.getMapAsync(new OnMapReadyCallback() {
                @Override
                public void onMapReady(MapboxMap mapboxMap) {
                    MapFragment.this.mapboxMap = mapboxMap;
                    instantiateMapBox(savedInstanceState);
                }
            });
        }
    }

    private void getUserLocation() {
        mapboxMap.setMyLocationEnabled(true);
        lastLocation = mapboxMap.getMyLocation();
        if (lastLocation != null) {
            mapboxMap.setCameraPosition(new CameraPosition.Builder()
                    .target(new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()))
                    .zoom(configManager.getDefaultZoom()).build());
        } else {
            mapboxMap.setCameraPosition(new CameraPosition.Builder().target(configManager.getDefaultCenter())
                    .zoom(configManager.getDefaultZoom()).build());
        }
    }

    private void measureMaxPoiType() {
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();

        float toolbarSize = getResources().getDimension(R.dimen.abc_action_bar_default_height_material)
                / displayMetrics.density;
        float dpHeight = displayMetrics.heightPixels / displayMetrics.density;

        // 80 size of one floating btn in dp
        // 180 size of the menu btn plus location btn
        // -1 because we always have note

        maxPoiType = (int) ((dpHeight - toolbarSize - 160) / 80) - 1;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (savedInstanceState != null) {
            Integer creationModeInt = savedInstanceState.getInt(CREATION_MODE);
            savePoiTypeId = savedInstanceState.getLong(POI_TYPE_ID);

            markerSelectedId = savedInstanceState.getLong(SELECTED_MARKER_ID, -1);

            mapMode = MapMode.values()[creationModeInt];
            if (mapMode == MapMode.DETAIL_NOTE || mapMode == MapMode.DETAIL_POI
                    || mapMode == MapMode.POI_POSITION_EDITION) {
                mapMode = MapMode.DEFAULT;
            } else if (mapMode == MapMode.NODE_REF_POSITION_EDITION) {
                mapMode = MapMode.WAY_EDITION;
            }

            long[] hidden = savedInstanceState.getLongArray(HIDDEN_POI_TYPE);
            if (hidden != null) {
                for (long l : hidden) {
                    poiTypeHidden.add(l);
                }
            }

            displayOpenNotes = savedInstanceState.getBoolean(DISPLAY_OPEN_NOTES);
            displayClosedNotes = savedInstanceState.getBoolean(DISPLAY_CLOSED_NOTES);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        mapView.onResume();
        if (mapboxMap != null) {
            switchMode(MapMode.DEFAULT);
            presenter.setForceRefreshPoi();
            presenter.setForceRefreshNotes();
            presenter.loadPoisIfNeeded();
            nextTuto(2);
            double lat = sharedPreferences.getFloat("latitude", 0);
            double lon = sharedPreferences.getFloat("longitude", 0);
            if (lat != 0 && lon != 0) {
                CameraPosition cameraPosition = new CameraPosition.Builder().target(new LatLng(lat, lon))
                        .zoom(mapboxMap.getCameraPosition().zoom).build();
                mapboxMap.setCameraPosition(cameraPosition);
            } else {
                getUserLocation();
            }
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        mapView.onPause();
        if (mapboxMap != null) {
            LatLng location = mapboxMap.getCameraPosition().target;
            sharedPreferences.edit().putFloat("latitude", (float) location.getLatitude()).apply();
            sharedPreferences.edit().putFloat("longitude", (float) location.getLongitude()).apply();
            mapboxMap.setMyLocationEnabled(false);
        }
        if (valueAnimator != null) {
            valueAnimator.cancel();
            valueAnimator.removeAllListeners();
            valueAnimator.removeAllUpdateListeners();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mapView != null) {
            mapView.onDestroy();
        }
        // Clear bitmapHandler even if activity leaks.
        bitmapHandler = null;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        progressBar.removeListeners();
        unbinder.unbind();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mapView.onSaveInstanceState(outState);
        outState.putParcelable(LOCATION, mapboxMap.getCameraPosition().target);
        outState.putFloat(ZOOM_LEVEL, getZoomLevel());
        outState.putInt(CREATION_MODE, mapMode.ordinal());
        outState.putLong(POI_TYPE_ID, poiTypeSelected == null ? -1 : poiTypeSelected.getId());
        outState.putDouble(LEVEL, currentLevel);
        outState.putBoolean(DISPLAY_OPEN_NOTES, displayOpenNotes);
        outState.putBoolean(DISPLAY_CLOSED_NOTES, displayClosedNotes);

        int markerType = markerSelected == null ? LocationMarkerView.MarkerType.NONE.ordinal()
                : markerSelected.getType().ordinal();
        outState.putInt(MARKER_TYPE, markerType);

        if (markerSelected != null) {
            switch (markerSelected.getType()) {
            case POI:
                markerSelectedId = ((Poi) markerSelected.getRelatedObject()).getId();
                break;
            case NODE_REF:
                markerSelectedId = ((PoiNodeRef) markerSelected.getRelatedObject()).getId();
                break;
            case NOTE:
                markerSelectedId = ((Note) markerSelected.getRelatedObject()).getId();
                break;
            }
        } else {
            markerSelectedId = -1L;
        }
        outState.putLong(SELECTED_MARKER_ID, markerSelectedId);

        long[] hidden = new long[poiTypeHidden.size()];
        int index = 0;

        for (Long value : poiTypeHidden) {
            hidden[index] = value;
            index++;
        }

        outState.putLongArray(HIDDEN_POI_TYPE, hidden);
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        mapView.onLowMemory();
    }

    /*-----------------------------------------------------------
    * ACTIONBAR
    *---------------------------------------------------------*/

    private MenuItem filter;
    private MenuItem confirm;
    private MenuItem downloadArea;
    private boolean homeActionBtnMode = true;

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        displayHomeButton(true); //show the home button
        confirm = menu.findItem(R.id.action_confirm_position);
        downloadArea = menu.findItem(R.id.action_download_area);
        filter = menu.findItem(R.id.action_filter_drawer);
        // the menu has too be created because we modify it depending on mode
        isMenuLoaded = true;
        if (pleaseSwitchToPoiSelected) {
            pleaseSwitchToPoiSelected = false;
            mapboxListener.onLocationMarkerClick(markerSelected);
        } else {
            switchMode(mapMode);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        switch (id) {
        case R.id.action_confirm_position:
            confirmPosition();
            break;

        case R.id.action_download_area:
            onDownloadZoneClick();
            break;

        case android.R.id.home:
            if (homeActionBtnMode) {
                eventBus.post(new PleaseToggleDrawer());
            } else {
                eventBus.post(new OnBackPressedMapEvent());
            }
            break;

        default:
            super.onOptionsItemSelected(item);
            break;
        }

        return true;
    }

    private void confirmPosition() {
        LatLng newPoiPosition;
        LatLng pos;

        switch (mapMode) {
        case POI_CREATION:
            if (getZoomLevel() < zoomVectorial) {
                changeMapZoomSmooth(15d);
            }
            createPoi();
            break;

        case NOTE_CREATION:
            pos = mapboxMap.getCameraPosition().target;
            NoteCommentDialogFragment dialog = NoteCommentDialogFragment.newInstance(pos.getLatitude(),
                    pos.getLongitude());
            dialog.show(getActivity().getFragmentManager(), "dialog");
            break;

        case POI_POSITION_EDITION:
            Poi poi = (Poi) markerSelected.getRelatedObject();
            newPoiPosition = mapboxMap.getCameraPosition().target;
            eventBus.post(new PleaseApplyPoiPositionChange(newPoiPosition, poi.getId()));
            markerSelected.setPosition(newPoiPosition);
            markerSelected.setIcon(IconFactory.getInstance(getActivity())
                    .fromBitmap(bitmapHandler.getMarkerBitmap(poi.getType(),
                            Poi.computeState(false, false, true, selectedPois.contains(poi)),
                            poi.computeAccessibilityType())));
            poi.setUpdated(true);
            mapboxMap.updateMarker(markerSelected);
            switchMode(MapMode.DETAIL_POI);
            break;

        case NODE_REF_POSITION_EDITION:
            PoiNodeRef poiNodeRef = wayMarkerSelected.getPoiNodeRef();
            newPoiPosition = mapboxMap.getCameraPosition().target;
            eventBus.post(new PleaseApplyNodeRefPositionChange(newPoiPosition, poiNodeRef.getId()));
            wayMarkerSelected.setPosition(newPoiPosition);
            wayMarkerSelected.setIcon(IconFactory.getInstance(getActivity())
                    .fromBitmap(bitmapHandler.getNodeRefBitmap(PoiNodeRef.State.SELECTED)));
            removePolyline(editionPolyline);
            switchMode(MapMode.WAY_EDITION);
            break;

        case DETAIL_POI:
            getActivity().finish();
            break;

        default:
            break;
        }
    }

    //if is back will show the back arrow, else will display the menu icon
    private void toggleBackButton(boolean homeActionBtnMode) {
        ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
        if (actionBar != null) {
            this.homeActionBtnMode = homeActionBtnMode;
            if (homeActionBtnMode) {
                actionBar.setHomeAsUpIndicator(R.drawable.menu);
            } else {
                actionBar.setHomeAsUpIndicator(R.drawable.back);
            }
        }
    }

    private void displayHomeButton(boolean show) {
        AppCompatActivity appCompatActivity = (AppCompatActivity) getActivity();
        if (appCompatActivity != null) {
            ActionBar actionBar = appCompatActivity.getSupportActionBar();
            if (actionBar != null) {
                actionBar.setDisplayHomeAsUpEnabled(show);
            }
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onOnBackPressedMapEvent(OnBackPressedMapEvent event) {
        Timber.d("Received event OnBackPressedMap");
        TutorialManager.forceDisplayAddTuto = false;
        switch (mapMode) {
        case POI_POSITION_EDITION:
            mapboxMap.updateMarker(markerSelected);
            switchMode(MapMode.DEFAULT);
            break;
        case NODE_REF_POSITION_EDITION:
            switchMode(MapMode.WAY_EDITION);
            removePolyline(editionPolyline);
            break;

        case WAY_EDITION:
            switchMode(MapMode.DEFAULT);
            break;
        case DETAIL_POI:
        case DETAIL_NOTE:
        case POI_CREATION:
        case NOTE_CREATION:
        case TYPE_PICKER:
            switchMode(MapMode.DEFAULT);
            InputMethodManager imm = (InputMethodManager) getActivity()
                    .getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(mapView.getWindowToken(), 0);
            break;

        case ARPIGL:
            switchMode(MapMode.DEFAULT);
            eventBus.post(new PleaseToggleArpiEvent());
            break;
        default:
            getActivity().finish();
            break;
        }

    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseGiveMeMapCenterEvent(PleaseGiveMeMapCenterEvent event) {
        eventBus.post(new MapCenterValueEvent(mapboxMap.getCameraPosition().target));
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseSwitchMapStyleEvent(PleaseSwitchMapStyleEvent event) {
        if (mapboxMap != null) {
            if (event.isSatelliteMode()) {
                mapboxMap.setStyleUrl("mapbox://styles/mapbox/satellite-streets-v9");
            } else {
                mapboxMap.setStyleUrl("asset://mapnik.json");
            }
        }
    }

    public void switchMode(MapMode mode) {
        mapMode = mode;
        Bitmap bitmap;

        switchToolbarMode(mapMode);
        editNodeRefPosition.setVisibility(View.GONE);
        progressBar.setVisibility(View.GONE);

        final MapMode.MapModeProperties properties = mode.getProperties();

        if (properties.isUnSelectIcon()) {
            unselectMarker();
        }

        showFloatingButtonAddPoi(properties.isShowAddPoiFab());
        displayPoiTypePicker();
        displayPoiDetailBanner(properties.isShowPoiBanner());
        displayNoteDetailBanner(properties.isShowNodeBanner());

        switch (mode) {
        case DETAIL_POI:
            break;

        case DETAIL_NOTE:
            break;

        case TYPE_PICKER:
            eventBus.post(new PleaseLoadLastUsedPoiType());
            break;

        case POI_CREATION:
            animationPoiCreation();
            break;

        case NOTE_CREATION:
            noteSelected();
            animationPoiCreation();
            break;

        case POI_POSITION_EDITION:
            // This marker is being moved
            bitmap = bitmapHandler.getMarkerBitmap(((Poi) markerSelected.getRelatedObject()).getType(),
                    Poi.computeState(false, true, false, selectedPois.contains(markerSelected.getRelatedObject())));
            creationPin.setImageBitmap(bitmap);
            break;

        case NODE_REF_POSITION_EDITION:
            wayCreationPin.setImageBitmap(bitmapHandler.getNodeRefBitmap(PoiNodeRef.State.MOVING));
            break;

        case WAY_EDITION:
            if (getZoomLevel() < zoomVectorial) {
                changeMapZoomSmooth(configManager.getZoomVectorial());
            }
            loadAreaForEdition();
            break;

        case ROUTE_MODE:
            if (properties.isShowButtons()) {
                route_mode_button.setVisibility(View.VISIBLE);
                floatingButtonLocalisation.setVisibility(View.VISIBLE);
                addPoiFloatingMenu.setVisibility(View.VISIBLE);
            }
            break;

        default:
            poiTypeSelected = null;
            poiTypeEditText.setText("");
            clearAllNodeRef();
            switchToolbarMode(mapMode);
            displayHomeButton(true);
            unselectMarker();
            break;
        }
        //the marker is displayed at the end of the animation
        creationPin.setVisibility(properties.isShowCreationPin() ? View.VISIBLE : View.GONE);
        wayCreationPin.setVisibility(properties.isShowCreationPin() ? View.VISIBLE : View.GONE);

        if (addPoiFloatingMenu.isOpened()) {
            addPoiFloatingMenu.toggle(true);
        }
    }

    private void switchToolbarMode(MapMode mode) {
        MapMode.MapModeProperties properties = mode.getProperties();

        confirm.setVisible(properties.isShowConfirmBtn());
        downloadArea.setVisible(properties.isShowDownloadArea());
        filter.setVisible(!properties.isLockDrawer());
        toggleBackButton(properties.isMenuBtn());
        getActivity().setTitle(properties.getTitle(getActivity()));
        eventBus.post(new PleaseChangeToolbarColor(properties.isEditColor()));
        eventBus.post(new PleaseToggleDrawerLock(properties.isLockDrawer()));
    }

    /**
     * Show or hide the {@link ButteryProgressBar}.
     *
     * @param show Whether we should show the progressBar.
     */
    public void showProgressBar(boolean show) {
        progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseSwitchWayEditionModeEvent(PleaseSwitchWayEditionModeEvent event) {
        if (getZoomLevel() < zoomVectorial) {
            changeMapZoomSmooth(zoomVectorial, new OnZoomAnimationFinishedListener() {
                @Override
                public void onZoomAnimationFinished() {
                    switchMode(MapMode.WAY_EDITION);
                    downloadAreaForEdition();
                }
            });
            eventBus.post(new PleaseToggleDrawer());
        } else {
            switchMode(MapMode.WAY_EDITION);
            downloadAreaForEdition();
        }
    }

    /**
     * Download data from backend. If we are in {@link MapMode#WAY_EDITION}, download ways
     * and if not, download Pois.
     */
    public void onDownloadZoneClick() {
        if (mapMode == MapMode.WAY_EDITION) {
            downloadAreaForEdition();
        } else {
            // If flavor Store, allow the download only if the zoom > 18
            if (!FlavorUtils.isStore() || getZoomLevel() >= 16) {
                presenter.downloadAreaPoisAndNotes();
                Toast.makeText(getActivity(), R.string.download_in_progress, Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(getActivity(), R.string.zoom_more, Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void defaultMap() {
        presenter.setForceRefreshPoi();
        presenter.setForceRefreshNotes();
        presenter.loadPoisIfNeeded();
        switchMode(MapMode.DEFAULT);
    }

    public void unselectMarker() {
        if (markerSelected != null) {
            Bitmap bitmap = null;

            switch (markerSelected.getType()) {
            case POI:
                Poi poi = (Poi) markerSelected.getRelatedObject();
                bitmap = bitmapHandler.getMarkerBitmap(poi.getType(),
                        Poi.computeState(false, false, poi.getUpdated(), selectedPois.contains(poi)),
                        poi.computeAccessibilityType());
                break;

            case NOTE:
                bitmap = bitmapHandler
                        .getNoteBitmap(Note.computeState((Note) markerSelected.getRelatedObject(), false, false));
                break;

            default:
                break;
            }
            if (bitmap != null) {
                markerSelected.setIcon(IconFactory.getInstance(getActivity()).fromBitmap(bitmap));
            }
            markerSelectedId = -1L;
            markerSelected = null;
        }
    }

    public void unselectWayMarker() {
        if (wayMarkerSelected != null) {
            wayMarkerSelected.setIcon(IconFactory.getInstance(getActivity())
                    .fromBitmap(bitmapHandler.getNodeRefBitmap(PoiNodeRef.State.NONE)));
        }
    }

    public void removeAllNotes() {
        for (LocationMarkerViewOptions<Note> markerNote : markersNotes.values()) {
            removeMarkerView(markerNote);
        }
        markersNotes.clear();
    }

    public void removeAllPoiMarkers() {
        for (Long markerId : markersPoi.keySet()) {
            removeMarkerView(markersPoi.get(markerId));
        }
        markersPoi.clear();
    }

    public void removePoiMarkersNotIn(List<Long> poiIds) {
        Set<Long> idsToRemove = new HashSet<>(markersPoi.keySet());
        idsToRemove.removeAll(poiIds);
        for (Long id : idsToRemove) {
            removeMarkerView(markersPoi.get(id));
        }
        markersPoi.keySet().removeAll(idsToRemove);
    }

    public void removeNoteMarkersNotIn(List<Long> noteIds) {
        Set<Long> idsToRemove = new HashSet<>(markersNotes.keySet());
        idsToRemove.removeAll(noteIds);
        for (Long id : idsToRemove) {
            removeMarkerView(markersNotes.get(id));
        }
        markersNotes.keySet().removeAll(idsToRemove);
    }

    private void removeMarkerView(LocationMarkerViewOptions marker) {
        if (marker != null) {
            mapboxMap.removeMarker(marker.getMarker());
            Object poi = marker.getMarker().getRelatedObject();
            eventBus.post(new PleaseRemoveArpiMarkerEvent(poi));
        }
    }

    private void removeWayMarker(WayMarkerOptions markerOptions) {
        mapboxMap.removeMarker(markerOptions.getMarker());
    }

    private void removePolyline(PolylineOptions polylineOptions) {
        if (polylineOptions != null) {
            mapboxMap.removePolyline(polylineOptions.getPolyline());
        }
    }

    public void reselectMarker() {
        // if I found the marker selected I click on it
        if (markerSelected != null) {
            if (isMenuLoaded) {
                mapboxListener.onLocationMarkerClick(markerSelected);
            } else {
                pleaseSwitchToPoiSelected = true;
            }
        }
    }

    public void addPoi(LocationMarkerViewOptions<Poi> marker) {
        markersPoi.put(marker.getMarker().getRelatedObject().getId(), marker);
        addPoiMarkerDependingOnFilters(marker);
    }

    public void addNote(LocationMarkerViewOptions<Note> marker) {
        markersNotes.put(marker.getMarker().getRelatedObject().getId(), marker);
        // add the note to the map
        addNoteMarkerDependingOnFilters(marker);
    }

    public void addIssue(LocationMarkerViewOptions<IssueMarker> marker) {
        markersIssues.put(marker.getMarker().getRelatedObject().getId(), marker);
        addIssueMarkerDependingOnFilters(marker);
    }

    public LocationMarkerViewOptions<Note> getNote(Long id) {
        return markersNotes.get(id);
    }

    /*-----------------------------------------------------------
    * WAY EDITION
    *---------------------------------------------------------*/

    @BindView(R.id.edit_way_point_position)
    FloatingActionButton editNodeRefPosition;

    @BindView(R.id.level_bar)
    LevelBar levelBar;

    @BindView(R.id.way_pin)
    ImageButton wayCreationPin;

    private boolean isVectorial = false;
    private double currentLevel = 0;
    private LocationMarkerView.MarkerType selectedMarkerType = LocationMarkerView.MarkerType.NONE;
    private int zoomVectorial;
    private Map<Long, PolylineOptions> polylinesWays = new HashMap<>();
    private PolylineOptions editionPolyline;

    @OnClick(R.id.edit_way_point_position)
    public void editNodeRefPosition() {
        buildEditionPolygon();
        switchMode(MapMode.NODE_REF_POSITION_EDITION);
        wayCreationPin.setVisibility(View.VISIBLE);
        hideMarker(wayMarkerSelected);
    }

    private void buildEditionPolygon() {
        // Current selected poiNodeRef
        PoiNodeRef currentPoiNodeRef = wayMarkerSelected.getPoiNodeRef();

        // Polyline related to this poiNodeRef
        PolylineOptions currentPolyline = polylinesWays.get(currentPoiNodeRef.getId());

        // Item of the poiNodeRef in the polilyne
        int indexOfPoiNodeRef = currentPolyline.getPoints()
                .indexOf(new LatLng(currentPoiNodeRef.getLatitude(), currentPoiNodeRef.getLongitude()));

        LatLng previousPoint = currentPolyline.getPoints()
                .get(indexOfPoiNodeRef == 0 ? indexOfPoiNodeRef + 1 : indexOfPoiNodeRef - 1);
        LatLng nextPoint = currentPolyline.getPoints()
                .get(indexOfPoiNodeRef == currentPolyline.getPoints().size() - 1 ? indexOfPoiNodeRef - 1
                        : indexOfPoiNodeRef + 1);
        editionPolyline = new PolylineOptions()
                .add(previousPoint, currentPolyline.getPoints().get(indexOfPoiNodeRef), nextPoint).alpha(0.4f)
                .width(1.8f).color(Color.parseColor("#F57C00"));

        mapboxMap.addPolyline(editionPolyline);
    }

    //get data from overpass
    private void downloadAreaForEdition() {
        if (getZoomLevel() >= zoomVectorial) {
            progressBar.setVisibility(View.VISIBLE);
            LatLngBounds viewLatLngBounds = getViewLatLngBounds();
            eventBus.post(new SyncDownloadWayEvent(viewLatLngBounds));
        } else {
            Toast.makeText(getActivity(), getString(R.string.zoom_to_edit), Toast.LENGTH_SHORT).show();
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEditionWaysLoadedEvent(EditionWaysLoadedEvent event) {
        if (event.isRefreshFromOverpass()) {
            progressBar.setVisibility(View.GONE);
        }
        if (mapMode == MapMode.WAY_EDITION) {
            Timber.d("Showing nodesRefs : " + event.getWays().size());
            clearAllNodeRef();
            updateVectorials(event.getWays(), event.getLevels());
        }
    }

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onPleaseLoadIssues(PleaseLoadIssuesEvent event) {
        List<IssueMarker> listIssues = ReportAPI.getListOfIssues(null);
        eventBus.post(new IssuesLoadedEvent(listIssues));
    }

    public void onCameraChangeUpdatePolyline() {
        if (editionPolyline != null) {
            List<LatLng> points = editionPolyline.getPoints();
            points.set(1, mapboxMap.getCameraPosition().target);

            removePolyline(editionPolyline);
            editionPolyline = new PolylineOptions().addAll(points).alpha(0.4f).width(1.8f)
                    .color(Color.parseColor("#F57C00"));
            mapboxMap.addPolyline(editionPolyline);
        }
    }

    public void updateVectorials(Set<Way> ways, TreeSet<Double> levels) {
        for (Way way : ways) {
            mapboxMap.addPolyline(way.getPolylineOptions());
            for (PoiNodeRef poiNodeRef : way.getPoiNodeRefs()) {
                WayMarkerOptions wayMarkerOptions = new WayMarkerOptions().position(poiNodeRef.getPosition())
                        .poiNodeRef(poiNodeRef).icon(IconFactory.getInstance(getActivity())
                                .fromBitmap(bitmapHandler.getNodeRefBitmap(PoiNodeRef.State.NONE)));
                addWayMarker(wayMarkerOptions);
                markersNodeRef.put(poiNodeRef.getId(), wayMarkerOptions);
                polylinesWays.put(poiNodeRef.getId(), way.getPolylineOptions());
            }
        }
    }

    //get data from the bd
    private void loadAreaForEdition() {
        if (getZoomLevel() >= zoomVectorial) {
            eventBus.post(new PleaseLoadEditWaysEvent(false));
        } else {
            Toast.makeText(getActivity(), getString(R.string.zoom_to_edit), Toast.LENGTH_SHORT).show();
        }
    }

    private void clearAllNodeRef() {
        for (WayMarkerOptions locationMarker : markersNodeRef.values()) {
            removeWayMarker(locationMarker);
        }
        for (PolylineOptions polylineOptions : polylinesWays.values()) {
            removePolyline(polylineOptions);
        }
        markersNodeRef.clear();
        polylinesWays.clear();
    }

    public void selectWayMarker() {
        editNodeRefPosition.setVisibility(View.VISIBLE);
        wayMarkerSelected.setIcon(IconFactory.getInstance(getActivity())
                .fromBitmap(bitmapHandler.getNodeRefBitmap(PoiNodeRef.State.SELECTED)));
        changeMapPositionSmooth(wayMarkerSelected.getPosition());
    }

    /*-----------------------------------------------------------
    * POI CREATION
    *---------------------------------------------------------*/

    @BindView(R.id.hand)
    ImageView handImageView;

    @BindView(R.id.add_poi_few_values)
    FloatingActionMenu addPoiFloatingMenu;

    @BindView(R.id.floating_btn_wrapper)
    RelativeLayout floatingBtnWrapper;

    @BindView(R.id.pin)
    ImageButton creationPin;

    ValueAnimator valueAnimator;

    private void createPoi() {
        LatLng pos = mapboxMap.getCameraPosition().target;
        boolean needTagEdition = false;

        for (PoiTypeTag poiTypeTag : poiTypeSelected.getTags()) {
            if (StringUtils.isEmpty(poiTypeTag.getValue())) {
                needTagEdition = true;
                break;
            }
        }

        if (needTagEdition) {
            Intent intent = new Intent(getActivity(), EditPoiActivity.class);
            intent.putExtra(EditPoiActivity.CREATION_MODE, true);
            intent.putExtra(EditPoiActivity.POI_LAT, pos.getLatitude());
            intent.putExtra(EditPoiActivity.POI_LNG, pos.getLongitude());
            intent.putExtra(EditPoiActivity.POI_LEVEL, isVectorial ? currentLevel : 0d);
            intent.putExtra(EditPoiActivity.POI_TYPE, poiTypeSelected.getId());

            switchMode(MapMode.DEFAULT);
            getActivity().startActivityForResult(intent, EditPoiActivity.EDIT_POI_ACTIVITY_CODE);
        } else {
            eventBus.post(new PleaseCreateNoTagPoiEvent(poiTypeSelected, pos, isVectorial ? currentLevel : 0d));
            switchMode(MapMode.DEFAULT);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPoiNoTypeCreated(PoiNoTypeCreated event) {
        presenter.setForceRefreshPoi();
        presenter.loadPoisIfNeeded();
    }

    private void animationPoiCreation() {
        handImageView.setVisibility(View.VISIBLE);
        valueAnimator = ValueAnimator.ofFloat(0, OsmCreationAnimatorUpdateListener.STEPS_CENTER_ANIMATION);
        valueAnimator.setDuration(1000);
        valueAnimator.addListener(new OsmCreationAnimatorUpdateListener(mapboxMap, handImageView, getActivity()));
        valueAnimator
                .addUpdateListener(new OsmCreationAnimatorUpdateListener(mapboxMap, handImageView, getActivity()));
        valueAnimator.start();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onNewPoiTypeSelected(NewPoiTypeSelected event) {
        Timber.d("Received event NewPoiTypeSelected");
        poiTypeSelected(event.getPoiType());
        switchMode(MapMode.POI_CREATION);
    }

    private void poiTypeSelected(PoiType poiType) {
        poiTypeTextView.setText(poiType.getName());
        poiTypeSelected = poiType;
        savePoiTypeId = 0;
        Bitmap bitmap;

        bitmap = bitmapHandler.getMarkerBitmap(poiType, Poi.computeState(false, true, false, false));
        if (poiTypeHidden.contains(poiType.getId())) {
            poiTypeHidden.remove(poiType.getId());
            applyPoiFilter();
        }

        if (presenter.getNumberOfPoiTypes() > maxPoiType) {
            editPoiTypeBtn.setVisibility(View.VISIBLE);
        }

        creationPin.setImageBitmap(bitmap);
        creationPin.setVisibility(View.VISIBLE);
    }

    private void noteSelected() {
        Bitmap bitmap = bitmapHandler.getNoteBitmap(Note.computeState(null, false, true));
        creationPin.setImageBitmap(bitmap);
        creationPin.setVisibility(View.VISIBLE);
        poiTypeSelected = null;
    }

    private void showFloatingButtonAddPoi(boolean show) {
        //animation show and hide
        if (show) {
            if (floatingBtnWrapper.getVisibility() == View.GONE) {
                Animation bottomUp = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_up_detail);
                floatingBtnWrapper.startAnimation(bottomUp);
                floatingBtnWrapper.setVisibility(View.VISIBLE);
            }
        } else {
            if (floatingBtnWrapper.getVisibility() == View.VISIBLE) {
                Animation upDown = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_down_detail);
                floatingBtnWrapper.startAnimation(upDown);
                floatingBtnWrapper.setVisibility(View.GONE);
            }
        }

        //which kind of add to display depending on screen size
        // if in store flavor show the spinner
        addPoiFloatingMenu.setVisibility(View.VISIBLE);
        addPoiFloatingMenu.setOnMenuButtonClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                easterEgg();
                addPoiFloatingMenu.toggle(true);
                nextTuto(0);
            }
        });
    }

    public void loadPoiTypeFloatingBtn() {
        if (addPoiFloatingMenu != null) {
            addPoiFloatingMenu.removeAllMenuButtons();
        }
        FloatingActionButton floatingActionButton;

        //add note
        if (!FlavorUtils.isBus()) {
            if (getActivity() == null) {
                return;
            }
            floatingActionButton = new FloatingActionButton(getActivity());
            floatingActionButton.setLabelText(getString(R.string.note));
            floatingActionButton.setColorPressed(ContextCompat.getColor(getActivity(), R.color.material_green_700));
            floatingActionButton.setColorNormal(ContextCompat.getColor(getActivity(), R.color.material_green_500));
            floatingActionButton.setButtonSize(FloatingActionButton.SIZE_MINI);
            floatingActionButton.setImageDrawable(bitmapHandler.getIconWhite(null));
            floatingActionButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    switchMode(MapMode.NOTE_CREATION);
                    addPoiFloatingMenu.close(true);
                }
            });
            addPoiFloatingMenu.addMenuButton(floatingActionButton);
        }

        if (presenter.getNumberOfPoiTypes() <= maxPoiType) {
            //add a btn per poiType
            for (final PoiType poiType : presenter.getPoiTypes()) {
                floatingActionButton = new FloatingActionButton(getActivity());
                floatingActionButton.setLabelText(poiType.getName());
                floatingActionButton
                        .setColorPressed(ContextCompat.getColor(getActivity(), R.color.material_blue_grey_800));
                floatingActionButton
                        .setColorNormal(ContextCompat.getColor(getActivity(), R.color.material_blue_500));
                floatingActionButton.setButtonSize(FloatingActionButton.SIZE_MINI);
                floatingActionButton.setImageDrawable(bitmapHandler.getIconWhite(poiType));
                floatingActionButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        if (configManager.hasPoiAddition()) {
                            switchMode(MapMode.POI_CREATION);
                            poiTypeSelected(poiType);
                            addPoiFloatingMenu.close(true);
                        } else {
                            Toast.makeText(getActivity(),
                                    getResources().getString(R.string.point_modification_forbidden),
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                });
                addPoiFloatingMenu.addMenuButton(floatingActionButton);
            }
        } else {
            // add a btn for all poiTypes
            floatingActionButton = new FloatingActionButton(getActivity());
            floatingActionButton.setLabelText(getResources().getString(R.string.add_poi));
            floatingActionButton
                    .setColorPressed(ContextCompat.getColor(getActivity(), R.color.material_blue_grey_800));
            floatingActionButton.setColorNormal(ContextCompat.getColor(getActivity(), R.color.material_blue_500));
            floatingActionButton.setButtonSize(FloatingActionButton.SIZE_MINI);
            floatingActionButton.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.fab_poi));
            floatingActionButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (configManager.hasPoiAddition()) {
                        switchMode(MapMode.TYPE_PICKER);
                    } else {
                        Toast.makeText(getActivity(),
                                getResources().getString(R.string.point_modification_forbidden), Toast.LENGTH_SHORT)
                                .show();
                    }
                }
            });
            addPoiFloatingMenu.addMenuButton(floatingActionButton);
        }
    }

    public void loadPoiTypeSpinner() {
        poiTypePickerAdapter.addAll(presenter.getPoiTypes());
        if (savePoiTypeId != 0) {
            for (PoiType poiType : presenter.getPoiTypes()) {
                if (poiType.getId().equals(savePoiTypeId)) {
                    poiTypeSelected(poiType);
                }
            }
        }
    }

    /*-----------------------------------------------------------
    * POI EDITION
    *---------------------------------------------------------*/

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseOpenEditionEvent(PleaseOpenEditionEvent event) {
        Timber.d("Received event PleaseOpenEdition");
        Intent intent = new Intent(getActivity(), EditPoiActivity.class);
        intent.putExtra(EditPoiActivity.CREATION_MODE, false);
        intent.putExtra(EditPoiActivity.POI_ID, ((Poi) markerSelected.getRelatedObject()).getId());
        startActivity(intent);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseChangePoiPosition(PleaseChangePoiPosition event) {
        Timber.d("Received event PleaseChangePoiPosition");
        if (configManager.hasPoiModification()) {
            switchMode(MapMode.POI_POSITION_EDITION);
            creationPin.setVisibility(View.GONE);

            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,
                    OsmAnimatorUpdateListener.STEPS_CENTER_ANIMATION);
            valueAnimator.setDuration(900);
            valueAnimator.addUpdateListener(new OsmAnimatorUpdateListener(mapboxMap.getCameraPosition().target,
                    markerSelected.getPosition(), mapboxMap));

            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    creationPin.setVisibility(View.VISIBLE);
                    hideMarker(markerSelected);
                }
            });
            valueAnimator.start();
        } else {
            Toast.makeText(getActivity(), getResources().getString(R.string.point_modification_forbidden),
                    Toast.LENGTH_SHORT).show();
        }
    }

    /*-----------------------------------------------------------
    * POI DELETION
    *---------------------------------------------------------*/

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseDeletePoiFromMapEvent(PleaseDeletePoiFromMapEvent event) {
        Poi poi = (Poi) markerSelected.getRelatedObject();
        poi.setToDelete(true);
        removeMarkerView(markersPoi.get(poi.getId()));
        markersPoi.remove(poi.getId());
        eventBus.post(new PleaseDeletePoiEvent(poi));
        OsmAnswers.localPoiAction(poi.getType().getTechnicalName(), "delete");
        switchMode(MapMode.DEFAULT);
    }

    /*-----------------------------------------------------------
    * POI DUPLICATION
    *---------------------------------------------------------*/

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseDuplicatePoiFromMapEvent(PleaseDuplicatePoiEvent event) {
        Poi poi = (Poi) markerSelected.getRelatedObject();
        poiTypeSelected(poi.getType());
        mapboxMap.setCameraPosition(
                new CameraPosition.Builder().target(new LatLng(poi.getLatitude(), poi.getLongitude())).build());
        switchMode(MapMode.POI_CREATION);
    }

    /*-----------------------------------------------------------
    * POI DETAIL
    *---------------------------------------------------------*/
    private void displayPoiDetailBanner(boolean display) {
        if (display) {
            if (markerSelected != null) {
                Poi poi = (Poi) markerSelected.getRelatedObject();
                eventBus.post(new PleaseChangeValuesDetailPoiFragmentEvent(poi));
            }

            if (poiDetailWrapper.getVisibility() != View.VISIBLE) {
                Animation bottomUp = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_up_detail);

                poiDetailWrapper.startAnimation(bottomUp);
                poiDetailWrapper.setVisibility(View.VISIBLE);
            }

        } else {
            if (poiDetailWrapper.getVisibility() == View.VISIBLE) {
                Animation upDown = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_down_detail);
                poiDetailWrapper.startAnimation(upDown);
                poiDetailWrapper.setVisibility(View.GONE);
            }
        }
    }

    /*-----------------------------------------------------------
    * NOTE DETAIL
    *---------------------------------------------------------*/
    private void displayNoteDetailBanner(boolean display) {
        if (display) {
            if (markerSelected != null && !markerSelected.getType().equals(LocationMarkerView.MarkerType.POI)) {
                eventBus.post(
                        new PleaseChangeValuesDetailNoteFragmentEvent((Note) markerSelected.getRelatedObject()));
            }

            if (noteDetailWrapper.getVisibility() != View.VISIBLE) {
                Animation bottomUp = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_up_detail);

                noteDetailWrapper.startAnimation(bottomUp);
                noteDetailWrapper.setVisibility(View.VISIBLE);
            }
        } else {
            if (noteDetailWrapper.getVisibility() == View.VISIBLE) {
                Animation bottomUp = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_down_detail);

                noteDetailWrapper.startAnimation(bottomUp);
                noteDetailWrapper.setVisibility(View.GONE);
            }
        }
    }

    /*-----------------------------------------------------------
    * ROUTE MODE
    *---------------------------------------------------------*/

    @BindView(R.id.route_mode_button)
    FloatingActionButton route_mode_button;

    @BindView(R.id.validate_route_button)
    FloatingActionButton validate_route_button;

    public boolean selectedPoisContains(Poi poi) {
        return selectedPois.contains(poi);
    }

    private void resetMarkerColors() {
        Iterator it = markersPoi.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            LocationMarkerViewOptions<Poi> lmvo = (LocationMarkerViewOptions<Poi>) pair.getValue();
            Marker marker = lmvo.getMarker();
            Poi poi = lmvo.getRelatedObject();
            Bitmap bitmap = bitmapHandler.getMarkerBitmap(poi.getType(),
                    Poi.computeState(false, false, poi.getUpdated(), selectedPois.contains(poi)),
                    poi.computeAccessibilityType());
            if (bitmap != null) {
                marker.setIcon(IconFactory.getInstance(getActivity()).fromBitmap(bitmap));
            }
        }
    }

    @OnClick(R.id.route_mode_button)
    public void toggleRouteMode() {
        route_mode = !route_mode;

        List<Polyline> allPolylines = mapboxMap.getPolylines();
        if (!selectedPois.isEmpty()) {
            selectedPois.clear();
        }
        if (!allPolylines.isEmpty()) {
            mapboxMap.getPolylines().clear();
        }
        resetMarkerColors();

        if (route_mode) {
            validate_route_button.setVisibility(View.VISIBLE);
        } else {
            if (!allPolylines.isEmpty()) {
                allPolylines.get(0).setPoints(new ArrayList<LatLng>());
            }
            validate_route_button.setVisibility(View.GONE);
        }
    }

    @OnClick(R.id.validate_route_button)
    public void validateRoute() {
        dc.setPoints(selectedPois);
        StartDirectionsCalculationEvent startEvent = new StartDirectionsCalculationEvent(selectedPois);
        eventBus.post(startEvent);
    }

    @Subscribe
    public void onPOIMarkerClick(POIMarkerClick event) {
        LocationMarkerView<Poi> marker = event.getMarker();
        Poi poi = (Poi) marker.getRelatedObject();

        if (route_mode) {
            if (selectedPois.contains(poi)) {
                selectedPois.remove(poi);
            } else {
                selectedPois.add(poi);
                if (!markersPoi.containsValue(poi))
                    ;
            }
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onDirectionCalculationDone(DoneCalculatingDirectionsEvent event) {
        DirectionsRoute route = event.getRoute();
        drawRoute(route);
    }

    private void drawRoute(DirectionsRoute route) {
        mapboxMap.getPolylines().clear();
        // Convert LineString coordinates into LatLng[]
        LineString lineString = LineString.fromPolyline(route.getGeometry(), Constants.OSRM_PRECISION_V5);
        List<Position> coordinates = lineString.getCoordinates();
        LatLng[] points = new LatLng[coordinates.size()];
        for (int i = 0; i < coordinates.size(); i++) {
            points[i] = new LatLng(coordinates.get(i).getLatitude(), coordinates.get(i).getLongitude());
        }

        // Draw Points on MapView
        mapboxMap.addPolyline(new PolylineOptions().add(points).color(Color.parseColor("#009688")).width(5));
    }

    /*-----------------------------------------------------------
    * MAP UTILS
    *---------------------------------------------------------*/
    @BindView(R.id.localisation)
    FloatingActionButton floatingButtonLocalisation;

    @OnClick(R.id.localisation)
    public void setOnPosition() {
        Timber.d("Center position on user location");
        getUserLocation();
        if (lastLocation != null) {
            mapboxMap.setCameraPosition(new CameraPosition.Builder().target(new LatLng(lastLocation)).build());
        } else {
            Toast.makeText(getActivity(), getResources().getString(R.string.location_not_found), Toast.LENGTH_SHORT)
                    .show();
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onChangeMapModeEvent(ChangeMapModeEvent event) {
        switchMode(event.getMapMode());
    }

    public void setZoomLevelText(String zoom) {
        zoomLevelText.setText(zoom);
    }

    public LocationMarkerViewOptions getMarkerOptions(LocationMarkerView.MarkerType markerType, Long id) {
        switch (markerType) {
        case POI:
            return markersPoi.get(id);
        case NOTE:
            return markersNotes.get(id);
        default:
            return null;
        }
    }

    public LatLngBounds getViewLatLngBounds() {
        return mapboxMap.getProjection().getVisibleRegion().latLngBounds;
    }

    public float getZoomLevel() {
        return (float) mapboxMap.getCameraPosition().zoom;
    }

    public void changeMapZoomSmooth(final double zoom) {
        mapboxMap.easeCamera(new CameraUpdate() {
            @Override
            public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
                return new CameraPosition.Builder().target(mapboxMap.getCameraPosition().target).zoom(zoom).build();
            }
        }, 700);
    }

    public void changeMapZoomSmooth(final double zoom,
            final OnZoomAnimationFinishedListener onZoomAnimationFinishedListener) {
        mapboxMap.easeCamera(new CameraUpdate() {
            @Override
            public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
                return new CameraPosition.Builder().target(mapboxMap.getCameraPosition().target).zoom(zoom).build();
            }
        }, 700);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                onZoomAnimationFinishedListener.onZoomAnimationFinished();
            }
        }, 750);
    }

    public void changeMapPositionSmooth(final LatLng newPosition) {
        mapboxMap.easeCamera(new CameraUpdate() {
            @Override
            public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) {
                return new CameraPosition.Builder().target(newPosition).build();
            }
        }, 700);
    }

    public void hideMarker(Marker marker) {
        marker.setIcon(IconFactory.getInstance(getActivity()).fromResource(R.drawable.hidden_marker));
    }

    private void addMarkerView(LocationMarkerViewOptions markerOptions) {
        mapboxMap.addMarker(markerOptions);
    }

    private void addWayMarker(WayMarkerOptions wayMarkerOptions) {
        mapboxMap.addMarker(wayMarkerOptions);
    }

    /*-----------------------------------------------------------
    * EASTER EGG
    *---------------------------------------------------------*/
    Long previousTime = System.currentTimeMillis();
    int nbClick = 0;

    private void easterEgg() {
        Long time = System.currentTimeMillis();

        if (time - previousTime < 500) {
            if (++nbClick == 4) {
                eventBus.post(new PleaseShowMeArpiglEvent());
                eventBus.post(new PleaseToggleArpiEvent());
                Toast.makeText(getActivity(), getString(R.string.easter_egg_activation), Toast.LENGTH_SHORT).show();
                sharedPreferences.edit().putBoolean(getString(R.string.easter_egg), true).apply();
                nbClick = 0;
            }
        } else {
            nbClick = 0;
        }

        previousTime = time;

    }

    /*-----------------------------------------------------------
    * SYNC
    *---------------------------------------------------------*/

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncFinishUploadPoiEvent(SyncFinishUploadPoiEvent event) {
        boolean forceRefresh = event.getSuccessfullyAddedPoisCount() > 0
                || event.getSuccessfullyUpdatedPoisCount() > 0 || event.getSuccessfullyDeletedPoisCount() > 0;

        if (forceRefresh) {
            presenter.setForceRefreshPoi();
            presenter.loadPoisIfNeeded();
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onNewNoteCreatedEvent(NewNoteCreatedEvent event) {
        // a note has been created, select it
        markerSelectedId = event.getNoteId();
        markerSelected = null;
        selectedMarkerType = LocationMarkerView.MarkerType.NOTE;
        presenter.setForceRefreshNotes();
        presenter.loadPoisIfNeeded();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onApplyNewCommentFailedEvent(ApplyNewCommentFailedEvent event) {
        Toast.makeText(getActivity(), getString(R.string.failed_apply_comment), Toast.LENGTH_SHORT).show();
        markerSelectedId = null;
        markerSelected = null;
        selectedMarkerType = LocationMarkerView.MarkerType.NONE;
        switchMode(MapMode.DEFAULT);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncUnauthorizedEvent(SyncUnauthorizedEvent event) {
        Toast.makeText(getActivity(), R.string.couldnt_connect_retrofit, Toast.LENGTH_LONG).show();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncDownloadRetrofitErrorEvent(SyncDownloadRetrofitErrorEvent event) {
        Toast.makeText(getActivity(), R.string.couldnt_download_retrofit, Toast.LENGTH_SHORT).show();
        showProgressBar(false);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncConflictingNodeErrorEvent(SyncConflictingNodeErrorEvent event) {
        Toast.makeText(getActivity(), R.string.couldnt_update_node, Toast.LENGTH_LONG).show();
        removePoiMarkerInError(event.getPoiIdInError());
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncNewNodeErrorEvent(SyncNewNodeErrorEvent event) {
        Toast.makeText(getActivity(), R.string.couldnt_create_node, Toast.LENGTH_LONG).show();
        removePoiMarkerInError(event.getPoiIdInError());
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncUploadRetrofitErrorEvent(SyncUploadRetrofitErrorEvent event) {
        Toast.makeText(getActivity(), R.string.couldnt_upload_retrofit, Toast.LENGTH_SHORT).show();
        removePoiMarkerInError(event.getPoiIdInError());
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onSyncUploadNoteRetrofitErrorEvent(SyncUploadNoteRetrofitErrorEvent event) {
        Toast.makeText(getActivity(), getString(R.string.couldnt_upload_retrofit), Toast.LENGTH_SHORT).show();
        removeNoteMarkerInError(event.getNoteIdInError());
    }

    // markers were added on the map the sync failed we remove them
    private void removePoiMarkerInError(Long id) {
        Marker m = markersPoi.remove(id).getMarker();
        if (m != null) {
            mapboxMap.removeMarker(m);
        }
        defaultMap();
    }

    // note was added on the map the sync failed we remove it
    private void removeNoteMarkerInError(Long id) {
        Marker m = markersNotes.remove(id).getMarker();
        if (m != null) {
            mapboxMap.removeMarker(m);
        }
        defaultMap();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onTooManyRequestsEvent(TooManyRequestsEvent event) {
        progressBar.setVisibility(View.GONE);
    }

    /*-----------------------------------------------------------
    * PoiType picker
    *---------------------------------------------------------*/

    @BindView(R.id.poi_type_value)
    EditText poiTypeEditText;

    @BindView(R.id.poi_type)
    TextView poiTypeTextView;

    @BindView(R.id.edit_poi_type)
    ImageButton editPoiTypeBtn;

    @BindView(R.id.autocomplete_list)
    ListView poiTypeListView;

    @BindView(R.id.poi_type_value_wrapper)
    RelativeLayout poiTypeHeaderWrapper;

    @OnClick(R.id.edit_poi_type)
    public void onEditPoiTypeWrapperClick() {
        editPoiType();
    }

    @OnClick(R.id.poi_type_value_wrapper)
    public void onEditPoiTypeClick() {
        editPoiType();
    }

    public void editPoiType() {
        if (mapMode == MapMode.POI_CREATION) {
            switchMode(MapMode.TYPE_PICKER);
        }
    }

    private long savePoiTypeId = 0;
    private PoiTypePickerAdapter poiTypePickerAdapter;
    private List<PoiType> autocompletePoiTypeValues = new ArrayList<>();

    private void instantiatePoiTypePicker() {
        poiTypePickerAdapter = new PoiTypePickerAdapter(getActivity(), autocompletePoiTypeValues, poiTypeEditText,
                eventBus, bitmapHandler,
                sharedPreferences.getBoolean(getString(R.string.shared_prefs_expert_mode), false));
        poiTypeListView.setAdapter(poiTypePickerAdapter);

        // Add Text Change Listener to EditText
        poiTypeEditText.addTextChangedListener(new TextWatcher() {

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // Call back the Adapter with current character to Filter
                poiTypePickerAdapter.getFilter().filter(s.toString());
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }

    private void displayPoiTypePicker() {
        if (mapMode == MapMode.TYPE_PICKER) {
            if (poiTypeListView.getVisibility() != View.VISIBLE) {
                Animation bottomUp = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_up_poi_type);

                if (android.os.Build.VERSION.SDK_INT >= 21) {
                    bottomUp.setInterpolator(getActivity(), android.R.interpolator.linear_out_slow_in);
                }

                //the text view will be changed to edit text at the end of the animation
                bottomUp.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                        poiTypeEditText.setVisibility(View.VISIBLE);
                        poiTypeTextView.setVisibility(View.GONE);
                        editPoiTypeBtn.setVisibility(View.GONE);
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });

                poiTypeListView.startAnimation(bottomUp);
                poiTypeListView.setVisibility(View.VISIBLE);

                // animation of the edit text from the top
                if (poiTypeTextView.getVisibility() == View.GONE) {
                    Animation slideTop = AnimationUtils.loadAnimation(getActivity(), R.anim.slide_top_annimation);
                    poiTypeHeaderWrapper.startAnimation(slideTop);
                    poiTypeHeaderWrapper.setVisibility(View.VISIBLE);
                    poiTypeEditText.setVisibility(View.VISIBLE);
                }
            }
        } else if (mapMode == MapMode.POI_CREATION) {
            if (poiTypeListView.getVisibility() == View.VISIBLE) {
                Animation upBottom = AnimationUtils.loadAnimation(getActivity(), R.anim.anim_down_poi_type);
                if (android.os.Build.VERSION.SDK_INT >= 21) {
                    upBottom.setInterpolator(getActivity(), android.R.interpolator.linear_out_slow_in);
                }
                poiTypeListView.startAnimation(upBottom);
            }
            poiTypeListView.setVisibility(View.GONE);
            poiTypeEditText.setVisibility(View.GONE);
            // if we have few value we hide the editPoiType btn
            if (presenter.getNumberOfPoiTypes() > maxPoiType) {
                editPoiTypeBtn.setVisibility(View.VISIBLE);
            }
            poiTypeTextView.setVisibility(View.VISIBLE);
        } else if (mapMode == MapMode.NOTE_CREATION) {
            poiTypeHeaderWrapper.setVisibility(View.VISIBLE);
            poiTypeTextView.setVisibility(View.VISIBLE);
            poiTypeListView.setVisibility(View.GONE);
            poiTypeEditText.setVisibility(View.GONE);
            editPoiTypeBtn.setVisibility(View.GONE);
            poiTypeTextView.setText(getResources().getString(R.string.note));
        } else {
            //For default behavior hide all poitype picker components
            poiTypeListView.setVisibility(View.GONE);
            poiTypeEditText.setVisibility(View.GONE);
            poiTypeTextView.setVisibility(View.GONE);
            editPoiTypeBtn.setVisibility(View.GONE);
            poiTypeHeaderWrapper.setVisibility(View.GONE);
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onLastUsePoiTypeLoaded(LastUsePoiTypeLoaded event) {
        poiTypePickerAdapter.addAllLastUse(event.getAutocompleteLastUsePoiTypeValues());
        poiTypeListView.setSelectionAfterHeaderView();
        nextTuto(1);
    }

    /*-----------------------------------------------------------
    * ADDRESS
    *---------------------------------------------------------*/
    @BindView(R.id.addressView)
    TextView addressView;

    @Inject
    Geocoder geocoder;

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onAddressFoundEvent(AddressFoundEvent event) {
        Timber.d("Received event AddressFound");
        if (getZoomLevel() >= zoomVectorial) {
            addressView.setVisibility(View.VISIBLE);
        }
        addressView.setText(event.getAddress());
    }

    /*-----------------------------------------------------------
    * FILTERS
    *---------------------------------------------------------*/

    private List<Long> poiTypeHidden = new ArrayList<>();
    private List<Poi.AccessibilityType> poiAccessibilityTypeHidden = new ArrayList<>();

    private boolean displayOpenNotes = true;
    private boolean displayClosedNotes = true;

    public List<Long> getPoiTypeHidden() {
        return poiTypeHidden;
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseApplyAccessibilityFilter(PleaseApplyAccessibilityFilter event) {
        Timber.d("filtering Pois by accessibility");
        poiAccessibilityTypeHidden = event.getAccessibilityTypesToHide();
        applyAccessibilityFilter();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseApplyPoiFilter(PleaseApplyPoiFilter event) {
        Timber.d("filtering Pois by type");
        poiTypeHidden = event.getPoiTypeIdsToHide();
        applyPoiFilter();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseApplyNoteFilterEvent(PleaseApplyNoteFilterEvent event) {
        Timber.d("filtering Notes");
        displayOpenNotes = event.isDisplayOpenNotes();
        displayClosedNotes = event.isDisplayClosedNotes();
        applyNoteFilter();
    }

    public void applyNoteFilter() {
        for (LocationMarkerViewOptions marker : markersNotes.values()) {
            removeMarkerView(marker);
            addNoteMarkerDependingOnFilters(marker);
        }
    }

    public void applyPoiFilter() {
        for (LocationMarkerViewOptions marker : markersPoi.values()) {
            removeMarkerView(marker);
            addPoiMarkerDependingOnFilters(marker);
        }
    }

    public void applyAccessibilityFilter() {
        Timber.d("Display Colored POI with accessibility-associated color");
        for (LocationMarkerViewOptions marker : markersPoi.values()) {
            removeMarkerView(marker);
            addPoiMarkerDependingOnFilters(marker);
        }
    }

    private void addNoteMarkerDependingOnFilters(LocationMarkerViewOptions<Note> markerOption) {
        Note note = markerOption.getMarker().getRelatedObject();

        if ((displayOpenNotes && Note.STATUS_OPEN.equals(note.getStatus()))
                || Note.STATUS_SYNC.equals(note.getStatus())
                || (displayClosedNotes && Note.STATUS_CLOSE.equals(note.getStatus()))) {
            addMarkerView(markerOption);
        } else if (mapMode.equals(MapMode.DETAIL_NOTE)
                && ((Note) markerSelected.getRelatedObject()).getId().equals(note.getId())) {
            switchMode(MapMode.DEFAULT);
        }
        creationPin.setVisibility(View.GONE);
    }

    private void addPoiMarkerDependingOnFilters(LocationMarkerViewOptions<Poi> markerOption) {
        Poi poi = markerOption.getMarker().getRelatedObject();
        //if we are in vectorial mode we hide all poi not at the current level
        if (poi.getType() != null && !poiTypeHidden.contains(poi.getType().getId())
                && !poiAccessibilityTypeHidden.contains(poi.computeAccessibilityType())
                && (!isVectorial || poi.isAtLevel(currentLevel) || !poi.isOnLevels(levelBar.getLevels()))) {
            mapboxMap.addMarker(markerOption);
        } else if (mapMode.equals(MapMode.DETAIL_POI)
                && ((Poi) markerSelected.getRelatedObject()).getId().equals(poi.getId())) {
            //if the poi selected is hidden close the detail mode
            switchMode(MapMode.DEFAULT);
        }
    }

    private void addIssueMarkerDependingOnFilters(LocationMarkerViewOptions<IssueMarker> markerOption) {
        IssueMarker issue = markerOption.getMarker().getRelatedObject();
        addMarkerView(markerOption);
    }

    /*-----------------------------------------------------------
    * TUTORIAL
    *---------------------------------------------------------*/
    private AddPoiTutoManager addPoiTutoManager;

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onPleaseDisplayTutorialEvent(PleaseDisplayTutorialEvent event) {
        TutorialManager.forceDisplayAddTuto = true;
        TutorialManager.forceDisplaySyncTuto = true;
        TutorialManager.forceDisplayOfflineTuto = true;
        switchMode(MapMode.DEFAULT);
        Toast.makeText(getActivity(), getString(R.string.replay_tuto_info), Toast.LENGTH_LONG).show();
    }

    private void nextTuto(int step) {
        if (addPoiTutoManager == null) {
            addPoiTutoManager = new AddPoiTutoManager(getActivity(), TutorialManager.forceDisplayAddTuto);
        }

        addPoiTutoManager.setForceDisplay(TutorialManager.forceDisplayAddTuto);

        switch (step) {
        // Display when the user clicks on the "+" button.
        case 0:
            addPoiTutoManager.addPoiBtnTuto(addPoiFloatingMenu);
            break;

        // Display when the user clicks on "Add Poi" button.
        case 1:
            addPoiTutoManager.choosePoiTypeTuto(poiTypeListView);
            break;

        // Display when a use add a poi on the map.
        case 2:
            addPoiTutoManager.synchronizedModificationsTuto(mapView);
            TutorialManager.forceDisplayAddTuto = false;
            break;
        }
    }

    public FloatingActionMenu getAddPoiFloatingMenu() {
        return addPoiFloatingMenu;
    }

    /*-----------------------------------------------------------
    * GETTERS AND SETTERS
    *---------------------------------------------------------*/

    public int getZoomVectorial() {
        return zoomVectorial;
    }

    public MapMode getMapMode() {
        return mapMode;
    }

    public LocationMarkerView getMarkerSelected() {
        return markerSelected;
    }

    public void setMarkerSelected(LocationMarkerView markerSelected) {
        selectedMarkerType = markerSelected.getType();
        this.markerSelected = markerSelected;
    }

    public void setWayMarkerSelected(WayMarker wayMarkerSelected) {
        this.wayMarkerSelected = wayMarkerSelected;
    }

    public Long getMarkerSelectedId() {
        return markerSelectedId;
    }

    public void setMarkerSelectedId(Long markerSelectedId) {
        this.markerSelectedId = markerSelectedId;
    }

    public LocationMarkerView.MarkerType getSelectedMarkerType() {
        return selectedMarkerType;
    }

    public BitmapHandler getBitmapHandler() {
        return bitmapHandler;
    }

    public LevelBar getLevelBar() {
        return levelBar;
    }

    public TextView getAddressView() {
        return addressView;
    }

    public boolean isVectorial() {
        return isVectorial;
    }

    public void setVectorial(boolean vectorial) {
        isVectorial = vectorial;
    }

    public Geocoder getGeocoder() {
        return geocoder;
    }

    public MapFragmentPresenter getPresenter() {
        return presenter;
    }

}