Skip to content

Commit

Permalink
Merge pull request google#1022 from shobhitagarwal1612/map-refactor
Browse files Browse the repository at this point in the history
Merge MapAdapter with MapFragment and cleanup usages
  • Loading branch information
shobhitagarwal1612 committed Oct 15, 2021
2 parents f9b08fc + c6bc7b4 commit 9d578f2
Show file tree
Hide file tree
Showing 15 changed files with 734 additions and 1,039 deletions.
Expand Up @@ -18,12 +18,10 @@

import android.content.Context;
import android.content.ContextWrapper;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.fragment.app.FragmentActivity;
Expand All @@ -34,8 +32,8 @@ public abstract class AbstractView extends FrameLayout {

@Inject protected ViewModelFactory viewModelFactory;

public AbstractView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
public AbstractView(@NonNull Context context) {
super(context);
}

protected FragmentActivity getActivity() {
Expand Down
Expand Up @@ -19,41 +19,27 @@
import static com.google.android.gnd.rx.RxAutoDispose.disposeOnDestroy;

import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import com.google.android.gnd.R;
import com.google.android.gnd.databinding.MapMoveFeatureLayoutBinding;
import com.google.android.gnd.ui.common.AbstractView;
import com.google.android.gnd.ui.map.CameraPosition;
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 dagger.hilt.android.WithFragmentBindings;
import javax.inject.Inject;

@WithFragmentBindings
@AndroidEntryPoint
public class FeatureRepositionView extends AbstractView {

private final FeatureRepositionViewModel viewModel;
@Inject MapProvider mapProvider;

public FeatureRepositionView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

viewModel = getViewModel(FeatureRepositionViewModel.class);
public FeatureRepositionView(Context context, MapFragment mapFragment) {
super(context);
FeatureRepositionViewModel viewModel = getViewModel(FeatureRepositionViewModel.class);
MapMoveFeatureLayoutBinding binding =
(MapMoveFeatureLayoutBinding) inflate(R.layout.map_move_feature_layout);
binding.setViewModel(viewModel);
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
mapProvider
.getMapAdapter()
.toFlowable()
.flatMap(MapAdapter::getCameraMovedEvents)
mapFragment
.getCameraMovedEvents()
.map(CameraPosition::getTarget)
.onBackpressureLatest()
.as(disposeOnDestroy(getActivity()))
Expand Down
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,33 @@
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.CameraPosition;
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 +71,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 +122,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 +132,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 @@ -178,23 +161,39 @@ private void onMapReady(MapAdapter map) {
mapContainerViewModel.getMbtilesFilePaths().observe(this, map::addLocalTileOverlays);

// TODO: Do this the RxJava way
map.moveCamera(mapContainerViewModel.getCameraPosition().getValue());
CameraPosition cameraPosition = mapContainerViewModel.getCameraPosition().getValue();
if (cameraPosition != null) {
map.moveCamera(cameraPosition.getTarget(), cameraPosition.getZoomLevel());
}
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
.getAddPolygonVisibility()
.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,11 +226,11 @@ 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:
map.disable();
map.disableGestures();
// TODO(#358): Once polygon drawing is implemented, pan & zoom to polygon when
// selected. This will involve calculating centroid and possibly zoom level based on
// vertices.
Expand All @@ -245,7 +244,7 @@ private void onBottomSheetStateChange(BottomSheetState state, MapAdapter map) {
});
break;
case HIDDEN:
map.enable();
map.enableGestures();
break;
default:
Timber.e("Unhandled visibility: %s", state.getVisibility());
Expand All @@ -271,7 +270,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 +294,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 +307,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> addPolygonVisibility = 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);
addPolygonVisibility.postValue(viewMode == Mode.DRAW_POLYGON ? VISIBLE : GONE);
}

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

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

public Optional<Feature> getReposFeature() {
Expand Down
Expand Up @@ -19,41 +19,27 @@
import static com.google.android.gnd.rx.RxAutoDispose.disposeOnDestroy;

import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import com.google.android.gnd.R;
import com.google.android.gnd.databinding.PolygonDrawingControlsBinding;
import com.google.android.gnd.ui.common.AbstractView;
import com.google.android.gnd.ui.map.CameraPosition;
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 dagger.hilt.android.WithFragmentBindings;
import javax.inject.Inject;

@WithFragmentBindings
@AndroidEntryPoint
public class PolygonDrawingView extends AbstractView {

private final PolygonDrawingViewModel viewModel;
private final PolygonDrawingControlsBinding binding;
@Inject MapProvider mapProvider;

public PolygonDrawingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

viewModel = getViewModel(PolygonDrawingViewModel.class);
binding = (PolygonDrawingControlsBinding) inflate(R.layout.polygon_drawing_controls);
public PolygonDrawingView(Context context, MapFragment mapFragment) {
super(context);
PolygonDrawingViewModel viewModel = getViewModel(PolygonDrawingViewModel.class);
PolygonDrawingControlsBinding binding =
(PolygonDrawingControlsBinding) inflate(R.layout.polygon_drawing_controls);
binding.setViewModel(viewModel);
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
mapProvider
.getMapAdapter()
.toFlowable()
.flatMap(MapAdapter::getCameraMovedEvents)
mapFragment
.getCameraMovedEvents()
.map(CameraPosition::getTarget)
.onBackpressureLatest()
.as(disposeOnDestroy(getActivity()))
Expand Down

0 comments on commit 9d578f2

Please sign in to comment.