Skip to content

Commit

Permalink
Migrate container fragments to use AbstractMapViewerFragment
Browse files Browse the repository at this point in the history
  • Loading branch information
shobhitagarwal1612 committed Oct 12, 2021
1 parent 270e033 commit 848b56f
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 110 deletions.
Expand Up @@ -22,12 +22,10 @@
import static java8.util.stream.StreamSupport.stream;

import android.os.Bundle;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
Expand All @@ -37,39 +35,32 @@
import com.google.android.gnd.model.feature.Feature;
import com.google.android.gnd.model.feature.Point;
import com.google.android.gnd.model.feature.PointFeature;
import com.google.android.gnd.persistence.mbtiles.MbtilesFootprintParser;
import com.google.android.gnd.repository.MapsRepository;
import com.google.android.gnd.rx.BooleanOrError;
import com.google.android.gnd.rx.Loadable;
import com.google.android.gnd.system.PermissionsManager.PermissionDeniedException;
import com.google.android.gnd.system.SettingsManager.SettingsChangeRequestCanceled;
import com.google.android.gnd.ui.common.AbstractFragment;
import com.google.android.gnd.ui.common.AbstractMapViewerFragment;
import com.google.android.gnd.ui.home.BottomSheetState;
import com.google.android.gnd.ui.home.HomeScreenViewModel;
import com.google.android.gnd.ui.home.mapcontainer.MapContainerViewModel.Mode;
import com.google.android.gnd.ui.map.MapAdapter;
import com.google.android.gnd.ui.map.MapProvider;
import com.google.android.gnd.ui.util.FileUtil;
import com.google.android.gnd.ui.map.MapFragment;
import com.google.android.gnd.ui.map.MapType;
import com.google.common.collect.ImmutableList;
import dagger.hilt.android.AndroidEntryPoint;
import io.reactivex.Single;
import java8.util.Optional;
import javax.inject.Inject;
import timber.log.Timber;

/** Main app view, displaying the map and related controls (center cross-hairs, add button, etc). */
@AndroidEntryPoint
public class MapContainerFragment extends AbstractFragment {
public class MapContainerFragment extends AbstractMapViewerFragment {

private static final String MAP_FRAGMENT_KEY = MapProvider.class.getName() + "#fragment";

@Inject FileUtil fileUtil;
@Inject MbtilesFootprintParser mbtilesFootprintParser;
@Inject MapProvider mapProvider;
@Inject MapsRepository mapsRepository;
PolygonDrawingViewModel polygonDrawingViewModel;
private MapContainerViewModel mapContainerViewModel;
private HomeScreenViewModel homeScreenViewModel;
private MapContainerFragBinding binding;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
Expand All @@ -79,38 +70,30 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
FeatureRepositionViewModel featureRepositionViewModel =
getViewModel(FeatureRepositionViewModel.class);
polygonDrawingViewModel = getViewModel(PolygonDrawingViewModel.class);
Single<MapAdapter> mapAdapter = mapProvider.getMapAdapter();
mapAdapter.as(autoDisposable(this)).subscribe(this::onMapReady);
mapAdapter
.toObservable()
.flatMap(MapAdapter::getMapPinClicks)
getMapFragment()
.getMapPinClicks()
.as(disposeOnDestroy(this))
.subscribe(mapContainerViewModel::onMarkerClick);
mapAdapter
.toObservable()
.flatMap(MapAdapter::getMapPinClicks)
getMapFragment()
.getMapPinClicks()
.as(disposeOnDestroy(this))
.subscribe(homeScreenViewModel::onMarkerClick);
mapAdapter
.toObservable()
.flatMap(MapAdapter::getFeatureClicks)
getMapFragment()
.getFeatureClicks()
.as(disposeOnDestroy(this))
.subscribe(homeScreenViewModel::onFeatureClick);
mapAdapter
.toFlowable()
.flatMap(MapAdapter::getStartDragEvents)
getMapFragment()
.getStartDragEvents()
.onBackpressureLatest()
.as(disposeOnDestroy(this))
.subscribe(__ -> mapContainerViewModel.onMapDrag());
mapAdapter
.toFlowable()
.flatMap(MapAdapter::getCameraMovedEvents)
getMapFragment()
.getCameraMovedEvents()
.onBackpressureLatest()
.as(disposeOnDestroy(this))
.subscribe(mapContainerViewModel::onCameraMove);
mapAdapter
.toObservable()
.flatMap(MapAdapter::getTileProviders)
getMapFragment()
.getTileProviders()
.as(disposeOnDestroy(this))
.subscribe(mapContainerViewModel::queueTileProvider);

Expand Down Expand Up @@ -138,7 +121,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
@Override
public View onCreateView(
LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
MapContainerFragBinding binding = MapContainerFragBinding.inflate(inflater, container, false);
binding = MapContainerFragBinding.inflate(inflater, container, false);
binding.setViewModel(mapContainerViewModel);
binding.setHomeScreenViewModel(homeScreenViewModel);
binding.setLifecycleOwner(this);
Expand All @@ -148,18 +131,17 @@ public View onCreateView(
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

disableAddFeatureBtn();

if (savedInstanceState == null) {
replaceFragment(R.id.map, mapProvider.getFragment());
} else {
mapProvider.restore(restoreChildFragment(savedInstanceState, MAP_FRAGMENT_KEY));
}
}

private void onMapReady(MapAdapter map) {
@Override
protected void onMapReady(MapFragment map) {
Timber.d("MapAdapter ready. Updating subscriptions");

// Custom views rely on the same instance of MapFragment. That couldn't be injected via Dagger.
// Hence, initializing them here instead of inflating in layout.
attachCustomViews(map);

mapContainerViewModel.setLocationLockEnabled(true);
polygonDrawingViewModel.setLocationLockEnabled(true);

Expand All @@ -182,19 +164,32 @@ private void onMapReady(MapAdapter map) {
map.setMapType(mapsRepository.getSavedMapType());
}

private void attachCustomViews(MapFragment map) {
FeatureRepositionView repositionView = new FeatureRepositionView(getContext(), map);
mapContainerViewModel.getMoveFeatureVisibility().observe(this, repositionView::setVisibility);
binding.mapOverlay.addView(repositionView);

PolygonDrawingView polygonDrawingView = new PolygonDrawingView(getContext(), map);
mapContainerViewModel
.isAddPolygonButtonVisible()
.observe(this, polygonDrawingView::setVisibility);
binding.mapOverlay.addView(polygonDrawingView);
}

private void showMapTypeSelectorDialog() {
ImmutableList<Pair<Integer, String>> mapTypes = mapProvider.getMapTypes();
ImmutableList<Integer> typeNos = stream(mapTypes).map(p -> p.first).collect(toImmutableList());
int selectedIdx = typeNos.indexOf(mapProvider.getMapType());
String[] labels = stream(mapTypes).map(p -> p.second).toArray(String[]::new);
ImmutableList<MapType> mapTypes = getMapFragment().getAvailableMapTypes();
ImmutableList<Integer> mapTypeValues =
stream(mapTypes).map(MapType::getType).collect(toImmutableList());
int selectedIdx = mapTypeValues.indexOf(getMapFragment().getMapType());
String[] labels = stream(mapTypes).map(p -> getString(p.getLabelId())).toArray(String[]::new);
new AlertDialog.Builder(requireContext())
.setTitle(R.string.select_map_type)
.setSingleChoiceItems(
labels,
selectedIdx,
(dialog, which) -> {
int mapType = typeNos.get(which);
mapProvider.setMapType(mapType);
int mapType = mapTypeValues.get(which);
getMapFragment().setMapType(mapType);
mapsRepository.saveMapType(mapType);
dialog.dismiss();
})
Expand Down Expand Up @@ -227,7 +222,7 @@ private void moveToNewPosition(Point point) {
homeScreenViewModel.updateFeature(newFeature);
}

private void onBottomSheetStateChange(BottomSheetState state, MapAdapter map) {
private void onBottomSheetStateChange(BottomSheetState state, MapFragment map) {
mapContainerViewModel.setSelectedFeature(state.getFeature());
switch (state.getVisibility()) {
case VISIBLE:
Expand Down Expand Up @@ -271,7 +266,7 @@ private void disableAddFeatureBtn() {
mapContainerViewModel.setFeatureButtonBackgroundTint(R.color.colorGrey500);
}

private void onLocationLockStateChange(BooleanOrError result, MapAdapter map) {
private void onLocationLockStateChange(BooleanOrError result, MapFragment map) {
result.error().ifPresent(this::onLocationLockError);
if (result.isTrue()) {
Timber.d("Location lock enabled");
Expand All @@ -295,7 +290,7 @@ private void showUserActionFailureMessage(@StringRes int resId) {
Toast.makeText(getContext(), resId, Toast.LENGTH_LONG).show();
}

private void onCameraUpdate(MapContainerViewModel.CameraUpdate update, MapAdapter map) {
private void onCameraUpdate(MapContainerViewModel.CameraUpdate update, MapFragment map) {
Timber.v("Update camera: %s", update);
if (update.getZoomLevel().isPresent()) {
float zoomLevel = update.getZoomLevel().get();
Expand All @@ -308,12 +303,6 @@ private void onCameraUpdate(MapContainerViewModel.CameraUpdate update, MapAdapte
}
}

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
saveChildFragment(outState, mapProvider.getFragment(), MAP_FRAGMENT_KEY);
}

@Override
public void onDestroy() {
mapContainerViewModel.closeProviders();
Expand Down
Expand Up @@ -104,7 +104,7 @@ public class MapContainerViewModel extends AbstractViewModel {
@Hot(replays = true)
private final MutableLiveData<Integer> mapControlsVisibility = new MutableLiveData<>(VISIBLE);

private final MutableLiveData<Boolean> addPolygonVisible = new MutableLiveData<>(false);
private final MutableLiveData<Integer> addPolygonVisible = new MutableLiveData<>(GONE);

@Hot(replays = true)
private final MutableLiveData<Integer> moveFeaturesVisibility = new MutableLiveData<>(GONE);
Expand Down Expand Up @@ -445,7 +445,7 @@ public void closeProviders() {
public void setViewMode(Mode viewMode) {
mapControlsVisibility.postValue(viewMode == Mode.DEFAULT ? VISIBLE : GONE);
moveFeaturesVisibility.postValue(viewMode == Mode.REPOSITION ? VISIBLE : GONE);
addPolygonVisible.postValue(viewMode == Mode.DRAW_POLYGON);
addPolygonVisible.postValue(viewMode == Mode.DRAW_POLYGON ? VISIBLE : GONE);
}

public void onMapTypeButtonClicked() {
Expand All @@ -472,7 +472,7 @@ public LiveData<Integer> getMoveFeatureVisibility() {
return moveFeaturesVisibility;
}

public LiveData<Boolean> isAddPolygonButtonVisible() {
public LiveData<Integer> isAddPolygonButtonVisible() {
return addPolygonVisible;
}

Expand Down
Expand Up @@ -29,23 +29,18 @@
import com.google.android.gnd.R;
import com.google.android.gnd.databinding.OfflineBaseMapSelectorFragBinding;
import com.google.android.gnd.model.basemap.tile.TileSource;
import com.google.android.gnd.ui.common.AbstractFragment;
import com.google.android.gnd.ui.common.AbstractMapViewerFragment;
import com.google.android.gnd.ui.common.EphemeralPopups;
import com.google.android.gnd.ui.common.Navigator;
import com.google.android.gnd.ui.map.MapAdapter;
import com.google.android.gnd.ui.map.MapProvider;
import com.google.android.gnd.ui.map.MapFragment;
import com.google.android.gnd.ui.offlinebasemap.selector.OfflineBaseMapSelectorViewModel.DownloadMessage;
import dagger.hilt.android.AndroidEntryPoint;
import io.reactivex.Single;
import javax.inject.Inject;

@AndroidEntryPoint
public class OfflineBaseMapSelectorFragment extends AbstractFragment {

private static final String MAP_FRAGMENT = MapProvider.class.getName() + "#fragment";
public class OfflineBaseMapSelectorFragment extends AbstractMapViewerFragment {

@Inject Navigator navigator;
@Inject MapProvider mapProvider;
@Inject EphemeralPopups popups;

private OfflineBaseMapSelectorViewModel viewModel;
Expand All @@ -58,9 +53,6 @@ public static OfflineBaseMapSelectorFragment newInstance() {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = getViewModel(OfflineBaseMapSelectorViewModel.class);
// TODO: use the viewmodel
Single<MapAdapter> mapAdapter = mapProvider.getMapAdapter();
mapAdapter.as(autoDisposable(this)).subscribe(this::onMapReady);
viewModel.getDownloadMessages().observe(this, e -> e.ifUnhandled(this::onDownloadMessage));
}

Expand Down Expand Up @@ -91,16 +83,7 @@ public View onCreateView(
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
replaceFragment(R.id.map, mapProvider.getFragment());
} else {
mapProvider.restore(restoreChildFragment(savedInstanceState, MAP_FRAGMENT));
}
}

private void onMapReady(MapAdapter map) {
protected void onMapReady(MapFragment map) {
viewModel
.getRemoteTileSources()
.map(tileSources -> stream(tileSources).map(TileSource::getUrl).collect(toImmutableList()))
Expand Down
Expand Up @@ -16,21 +16,17 @@

package com.google.android.gnd.ui.offlinebasemap.viewer;

import static com.google.android.gnd.rx.RxAutoDispose.autoDisposable;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.google.android.gnd.MainActivity;
import com.google.android.gnd.R;
import com.google.android.gnd.databinding.OfflineBaseMapViewerFragBinding;
import com.google.android.gnd.model.basemap.OfflineBaseMap;
import com.google.android.gnd.ui.common.AbstractFragment;
import com.google.android.gnd.ui.common.AbstractMapViewerFragment;
import com.google.android.gnd.ui.common.Navigator;
import com.google.android.gnd.ui.map.MapAdapter;
import com.google.android.gnd.ui.map.MapProvider;
import com.google.android.gnd.ui.map.MapFragment;
import dagger.hilt.android.AndroidEntryPoint;
import javax.inject.Inject;

Expand All @@ -39,15 +35,11 @@
* device.
*/
@AndroidEntryPoint
public class OfflineBaseMapViewerFragment extends AbstractFragment {

private static final String MAP_FRAGMENT = MapProvider.class.getName() + "#fragment";
public class OfflineBaseMapViewerFragment extends AbstractMapViewerFragment {

@Inject Navigator navigator;
@Inject MapProvider mapProvider;

private OfflineBaseMapViewerViewModel viewModel;
@Nullable private MapAdapter map;

@Inject
public OfflineBaseMapViewerFragment() {}
Expand All @@ -59,7 +51,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
OfflineBaseMapViewerFragmentArgs.fromBundle(getArguments());
viewModel = getViewModel(OfflineBaseMapViewerViewModel.class);
viewModel.loadOfflineArea(args);
mapProvider.getMapAdapter().as(autoDisposable(this)).subscribe(this::onMapReady);
viewModel.getOfflineArea().observe(this, this::panMap);
}

Expand All @@ -77,26 +68,12 @@ public View onCreateView(
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
replaceFragment(R.id.map, mapProvider.getFragment());
} else {
mapProvider.restore(restoreChildFragment(savedInstanceState, MAP_FRAGMENT));
}
}

private void onMapReady(MapAdapter map) {
this.map = map;
protected void onMapReady(MapFragment map) {
map.disable();
}

private void panMap(OfflineBaseMap offlineBaseMap) {
if (map == null) {
return;
}

map.setBounds(offlineBaseMap.getBounds());
getMapFragment().setBounds(offlineBaseMap.getBounds());
}

/** Removes the area associated with this fragment from the user's device. */
Expand Down

0 comments on commit 848b56f

Please sign in to comment.