/** * 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.activities; import android.content.Intent; import android.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.TextView; import com.github.clans.fab.FloatingActionButton; import com.github.clans.fab.FloatingActionMenu; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; 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.mapboxsdk.offline.OfflineRegion; import com.mapbox.mapboxsdk.offline.OfflineRegionStatus; import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; import com.yarolegovich.lovelydialog.LovelyTextInputDialog; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; import io.jawg.osmcontributor.BuildConfig; import io.jawg.osmcontributor.OsmTemplateApplication; import io.jawg.osmcontributor.R; import io.jawg.osmcontributor.offline.OfflineRegionManager; import io.jawg.osmcontributor.offline.events.OfflineRegionCreatedEvent; import io.jawg.osmcontributor.service.OfflineRegionDownloadService; import io.jawg.osmcontributor.ui.adapters.OfflineRegionItem; import io.jawg.osmcontributor.ui.adapters.OfflineRegionsAdapter; import io.jawg.osmcontributor.ui.listeners.RecyclerItemClickListener; import io.jawg.osmcontributor.ui.managers.tutorial.OfflineTutoManager; import io.jawg.osmcontributor.ui.managers.tutorial.TutorialManager; import io.jawg.osmcontributor.utils.OsmAnswers; /** * @author Tommy Buonomo on 08/08/16. */ public class OfflineRegionsActivity extends AppCompatActivity { private static final String TAG = "OfflineRegionsActivity"; private static final int PADDING_TOP_BOUNDS = 100; private static final int PADDING_OTHER_BOUNDS = 20; private static final int MIN_DOWNLOAD_ZOOM = 11; private enum Mode { STATUS_COMPLETE(false, View.VISIBLE, View.VISIBLE, View.GONE, View.GONE, View.GONE, View.GONE), STATUS_INCOMPLETE(false, View.VISIBLE, View.VISIBLE, View.GONE, View.VISIBLE, View.GONE, View.GONE), NO_REGIONS(false, View.GONE, View.VISIBLE, View.VISIBLE, View.GONE, View.GONE, View.GONE), ADD_REGION(true, View.GONE, View.GONE, View.GONE, View.GONE, View.VISIBLE, View.GONE); private final int downloadButtonVisibility; private final int downloadNewButtonVisibility; private final int editMenuVisibility; private final int addMenuVisibility; private final int messageTextVisibility; private final int renameButtonVisibility; private final boolean mapGesturesEnabled; Mode(boolean mapGesturesEnabled, int editMenuVisibility, int addButtonVisibility, int messageTextVisibility, int downloadButtonVisibility, int downloadNewButtonVisibility, int renameButtonVisibility) { this.mapGesturesEnabled = mapGesturesEnabled; this.editMenuVisibility = editMenuVisibility; this.addMenuVisibility = addButtonVisibility; this.messageTextVisibility = messageTextVisibility; this.downloadButtonVisibility = downloadButtonVisibility; this.downloadNewButtonVisibility = downloadNewButtonVisibility; this.renameButtonVisibility = renameButtonVisibility; } } @Inject OfflineRegionManager offlineRegionManager; @Inject EventBus eventBus; @BindView(R.id.offline_regions_view) View activityView; @BindView(R.id.offline_regions_list) RecyclerView offlineRegionsRecyclerView; @BindView(R.id.mapview) MapView mapView; @BindView(R.id.offline_regions_empty_text) TextView messageTextView; @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.edit_region_floating_action_menu) FloatingActionMenu editRegionFloatingActionMenu; @BindView(R.id.add_offline_region_floating_button) FloatingActionButton addRegionFloatingActionButton; @BindView(R.id.rename_region_floating_button) FloatingActionButton renameRegionFloatingActionButton; @BindView(R.id.delete_region_floating_button) FloatingActionButton deleteRegionFloatingActionButton; @BindView(R.id.download_region_floating_button) FloatingActionButton downloadRegionFloatingActionButton; @BindView(R.id.download_new_region_floating_button) FloatingActionButton downloadNewRegionFloatingActionButton; private OfflineRegionItem selectedRegionItem; private MapboxMap mapboxMap; private OfflineRegionsAdapter adapter; private Mode currentMode; private OfflineTutoManager offlineTutoManager; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_offline_regions); ((OsmTemplateApplication) getApplication()).getOsmTemplateComponent().inject(this); ButterKnife.bind(this); OsmAnswers.visitedActivity("Page des cartes hors ligne"); offlineTutoManager = new OfflineTutoManager(this, TutorialManager.forceDisplayOfflineTuto); switchMode(Mode.NO_REGIONS); initToolbar(); initMapView(savedInstanceState); initRecyclerView(); initOfflineRegions(); offlineTutoManager.startTuto(); initFloatingActionButtons(); } private void initToolbar() { setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } } private void initMapView(Bundle savedInstanceState) { mapView.onCreate(savedInstanceState); mapView.setStyleUrl(BuildConfig.MAP_STYLE); mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(MapboxMap mapboxMap) { OfflineRegionsActivity.this.mapboxMap = mapboxMap; mapboxMap.getUiSettings().setCompassEnabled(false); mapboxMap.getUiSettings().setRotateGesturesEnabled(false); mapboxMap.getUiSettings().setTiltGesturesEnabled(false); enableMapGestures(false); } }); } private void initRecyclerView() { adapter = new OfflineRegionsAdapter(new ArrayList<OfflineRegionItem>()); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); offlineRegionsRecyclerView.setLayoutManager(linearLayoutManager); offlineRegionsRecyclerView.setAdapter(adapter); offlineRegionsRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this, offlineRegionsRecyclerView, new RecyclerItemClickListener.OnItemClickListener() { @Override public void onItemClick(View view, int position) { selectOfflineRegion(position); } @Override public void onLongItemClick(View view, int position) { } })); } private void initOfflineRegions() { refreshOfflineRegions(); } private void initFloatingActionButtons() { deleteRegionFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (selectedRegionItem != null && selectedRegionItem.getOfflineRegion() != null) { int position = adapter.getOfflineRegionItems().indexOf(selectedRegionItem); offlineRegionManager.deleteOfflineRegion(selectedRegionItem.getOfflineRegion(), getOfflineRegionDeletedListener()); adapter.removeOfflineRegion(selectedRegionItem); if (adapter.getItemCount() == 0) { switchMode(Mode.NO_REGIONS); } else { int newPosition = position == 0 ? 0 : position == adapter.getItemCount() ? adapter.getItemCount() - 1 : position - 1; selectOfflineRegion(newPosition); } closeEditMenu(); } } }); downloadRegionFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (selectedRegionItem != null && selectedRegionItem.getOfflineRegion() != null) { Intent intent = new Intent(OfflineRegionsActivity.this, OfflineRegionDownloadService.class); intent.putStringArrayListExtra(OfflineRegionDownloadService.LIST_PARAM + 0, convertDoubleList(selectedRegionItem.getOfflineRegion(), true)); intent.putExtra(OfflineRegionDownloadService.SIZE_PARAM, 1); startService(intent); closeEditMenu(); } } }); addRegionFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { switchMode(Mode.ADD_REGION); } }); downloadNewRegionFloatingActionButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mapboxMap.getCameraPosition().zoom < MIN_DOWNLOAD_ZOOM) { Snackbar snackbar = Snackbar.make(activityView, R.string.region_to_large, Snackbar.LENGTH_LONG); TextView textView = (TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text); textView.setTextColor(ContextCompat.getColor(OfflineRegionsActivity.this, R.color.error_color)); snackbar.show(); return; } final Intent intent = new Intent(OfflineRegionsActivity.this, OfflineRegionDownloadService.class); new LovelyTextInputDialog(OfflineRegionsActivity.this) .setTopColorRes(R.color.colorPrimary) .setTitle(R.string.enter_region_name) .setIcon(R.drawable.ic_rename) .setInputFilter(R.string.region_name_error, new LovelyTextInputDialog.TextFilter() { @Override public boolean check(String text) { return text.length() < 20; } }) .setConfirmButton(R.string.ok, new LovelyTextInputDialog.OnTextInputConfirmListener() { @Override public void onTextInputConfirmed(String text) { intent.putExtra(OfflineRegionDownloadService.REGION_NAME_PARAM, text); LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; intent.putStringArrayListExtra(OfflineRegionDownloadService.LIST_PARAM + 0, convertDoubleList(bounds, false)); intent.putExtra(OfflineRegionDownloadService.SIZE_PARAM, 1); startService(intent); } }) .show(); } }); } public ArrayList<String> convertDoubleList(OfflineRegion region, boolean inversed) { OfflineTilePyramidRegionDefinition definition = (OfflineTilePyramidRegionDefinition) region.getDefinition(); return convertDoubleList(definition.getBounds(), inversed); } public ArrayList<String> convertDoubleList(LatLngBounds bounds, boolean inversed) { ArrayList<String> latLngBounds = new ArrayList<>(); if (inversed) { latLngBounds.add(String.valueOf(bounds.getLatSouth())); latLngBounds.add(String.valueOf(bounds.getLonWest())); latLngBounds.add(String.valueOf(bounds.getLatNorth())); latLngBounds.add(String.valueOf(bounds.getLonEast())); } else { latLngBounds.add(String.valueOf(bounds.getLonWest())); latLngBounds.add(String.valueOf(bounds.getLatSouth())); latLngBounds.add(String.valueOf(bounds.getLonEast())); latLngBounds.add(String.valueOf(bounds.getLatNorth())); } return latLngBounds; } public OfflineRegionManager.OnOfflineRegionDeletedListener getOfflineRegionDeletedListener() { return new OfflineRegionManager.OnOfflineRegionDeletedListener() { @Override public void onOfflineRegionDeleted() { Snackbar.make(activityView, R.string.offline_regions_deleted, Snackbar.LENGTH_LONG).show(); } }; } private void refreshOfflineRegions() { offlineRegionManager.listOfflineRegions(new OfflineRegionManager.OnOfflineRegionsListedListener() { @Override public void onOfflineRegionsListed(List<OfflineRegion> offlineRegions) { for (final OfflineRegion region : offlineRegions) { region.getStatus(new OfflineRegion.OfflineRegionStatusCallback() { @Override public void onStatus(OfflineRegionStatus status) { OfflineRegionItem regionItem = new OfflineRegionItem(region, status); if (!adapter.getOfflineRegionItems().contains(regionItem)) { adapter.addOfflineRegion(regionItem); if (adapter.getItemCount() == 1) { selectOfflineRegion(0); } } } @Override public void onError(String error) { Log.e(TAG, "Refresh Offline Regions error: " + error); } }); } } }); } private void selectOfflineRegion(int position) { if (selectedRegionItem != null) { selectedRegionItem.setSelected(false); } selectedRegionItem = adapter.getOfflineRegion(position); selectedRegionItem.setSelected(true); OfflineTilePyramidRegionDefinition definition = (OfflineTilePyramidRegionDefinition) selectedRegionItem .getOfflineRegion() .getDefinition(); animateCameraToBounds(definition.getBounds()); if (selectedRegionItem.getStatus().isComplete()) { switchMode(Mode.STATUS_COMPLETE); } else { switchMode(Mode.STATUS_INCOMPLETE); } closeEditMenu(); adapter.notifyDataSetChanged(); } private void switchMode(final Mode mode) { if (mode.equals(currentMode)) { return; } enableMapGestures(mode.mapGesturesEnabled); editRegionFloatingActionMenu.setVisibility(mode.editMenuVisibility); addRegionFloatingActionButton.setVisibility(mode.addMenuVisibility); messageTextView.setVisibility(mode.messageTextVisibility); downloadNewRegionFloatingActionButton.setVisibility(mode.downloadNewButtonVisibility); downloadRegionFloatingActionButton.setVisibility(mode.downloadButtonVisibility); renameRegionFloatingActionButton.setVisibility(mode.renameButtonVisibility); currentMode = mode; } private void enableMapGestures(boolean enable) { if (mapboxMap != null) { mapboxMap.getUiSettings().setScrollGesturesEnabled(enable); mapboxMap.getUiSettings().setZoomGesturesEnabled(enable); } } private void closeEditMenu() { if (editRegionFloatingActionMenu.isOpened()) { editRegionFloatingActionMenu.toggle(true); } } private void animateCameraToBounds(final LatLngBounds bounds) { mapboxMap.easeCamera(CameraUpdateFactory .newLatLngBounds(inverseLatLngBounds(bounds), PADDING_OTHER_BOUNDS, PADDING_TOP_BOUNDS, PADDING_OTHER_BOUNDS, PADDING_OTHER_BOUNDS), 500); } private LatLngBounds inverseLatLngBounds(LatLngBounds latLngBounds) { return new LatLngBounds.Builder() .include(new LatLng(latLngBounds.getLonEast(), latLngBounds.getLatSouth())) .include(new LatLng(latLngBounds.getLonWest(), latLngBounds.getLatNorth())) .build(); } @Subscribe(threadMode = ThreadMode.MAIN) @SuppressWarnings("unused") public void onOfflineRegionCreatedEvent(final OfflineRegionCreatedEvent event) { if (adapter != null) { refreshOfflineRegions(); } } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == android.R.id.home) { finish(); } return super.onOptionsItemSelected(item); } @Override protected void onPause() { super.onPause(); mapView.onPause(); eventBus.unregister(this); } @Override protected void onResume() { super.onResume(); mapView.onResume(); eventBus.register(this); } @Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public void onLowMemory() { super.onLowMemory(); mapView.onLowMemory(); } }