diff --git a/gnd/src/main/java/com/google/android/gnd/MainActivityModule.java b/gnd/src/main/java/com/google/android/gnd/MainActivityModule.java index 1c32425d0c..96a48b06aa 100644 --- a/gnd/src/main/java/com/google/android/gnd/MainActivityModule.java +++ b/gnd/src/main/java/com/google/android/gnd/MainActivityModule.java @@ -19,6 +19,8 @@ import androidx.appcompat.app.AppCompatActivity; import com.google.android.gnd.inject.ActivityScoped; import com.google.android.gnd.inject.FragmentScoped; +import com.google.android.gnd.ui.basemapselector.BasemapSelectorFragment; +import com.google.android.gnd.ui.basemapselector.BasemapSelectorModule; import com.google.android.gnd.ui.editrecord.EditRecordFragment; import com.google.android.gnd.ui.editrecord.EditRecordModule; import com.google.android.gnd.ui.home.AddFeatureDialogFragment; @@ -89,4 +91,8 @@ public abstract class MainActivityModule { @FragmentScoped @ContributesAndroidInjector(modules = EditRecordModule.class) abstract EditRecordFragment editRecordFragmentInjector(); + + @FragmentScoped + @ContributesAndroidInjector(modules = BasemapSelectorModule.class) + abstract BasemapSelectorFragment basemapSelectorFragmentInjector(); } diff --git a/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorFragment.java b/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorFragment.java new file mode 100644 index 0000000000..2204bcd482 --- /dev/null +++ b/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorFragment.java @@ -0,0 +1,78 @@ +package com.google.android.gnd.ui.basemapselector; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import com.google.android.gnd.MainViewModel; +import com.google.android.gnd.R; +import com.google.android.gnd.databinding.BasemapSelectorFragBinding; +import com.google.android.gnd.inject.ActivityScoped; +import com.google.android.gnd.ui.common.AbstractFragment; +import com.google.android.gnd.ui.map.MapProvider; +import com.google.android.gnd.ui.map.MapProvider.MapAdapter; + +import javax.inject.Inject; + +import io.reactivex.Single; + +/** + * Allows the user to select specific areas on a map for offline display. Users can toggle sections of + * the map to add or remove imagery. Upon selection, basemap tiles are queued for download. When + * deselected, they are removed from the device. + */ +@ActivityScoped +public class BasemapSelectorFragment extends AbstractFragment { + + private static final String TAG = BasemapSelectorFragment.class.getName(); + private static final String MAP_FRAGMENT_KEY = MapProvider.class.getName() + "#fragment"; + private BasemapSelectorViewModel viewModel; + private MainViewModel mainViewModel; + + @Inject MapProvider mapProvider; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + viewModel = getViewModel(BasemapSelectorViewModel.class); + mainViewModel = getViewModel(MainViewModel.class); + Single mapAdapter = mapProvider.getMapAdapter(); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + BasemapSelectorFragBinding binding = + BasemapSelectorFragBinding.inflate(inflater, container, false); + binding.setViewModel(viewModel); + return binding.getRoot(); + } + + @Override + public void onViewCreated(@NonNull 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_KEY)); + } + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mainViewModel.getWindowInsets().observe(this, this::onApplyWindowInsets); + } + + private void onApplyWindowInsets(WindowInsetsCompat windowInsets) { + ViewCompat.onApplyWindowInsets(mapProvider.getFragment().getView(), windowInsets); + // TODO: Once we add control UI elements, translate them based on the inset to avoid collision + // with the android navbar. + } +} diff --git a/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorModule.java b/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorModule.java new file mode 100644 index 0000000000..ab4358966b --- /dev/null +++ b/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorModule.java @@ -0,0 +1,16 @@ +package com.google.android.gnd.ui.basemapselector; + +import androidx.fragment.app.Fragment; + +import com.google.android.gnd.inject.FragmentScoped; + +import dagger.Binds; +import dagger.Module; + +@Module +public abstract class BasemapSelectorModule { + + @Binds + @FragmentScoped + abstract Fragment fragment(BasemapSelectorFragment fragment); +} diff --git a/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorViewModel.java b/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorViewModel.java new file mode 100644 index 0000000000..a178496b5b --- /dev/null +++ b/gnd/src/main/java/com/google/android/gnd/ui/basemapselector/BasemapSelectorViewModel.java @@ -0,0 +1,19 @@ +package com.google.android.gnd.ui.basemapselector; + +import androidx.lifecycle.ViewModel; + +import javax.inject.Inject; + +/** + * This view model is responsible for managing state for the {@link BasemapSelectorFragment}. + * Together, they constitute a basemap selector that users can interact with to select portions of a + * basemap for offline viewing. Among other things, this view model is responsible for receiving + * requests to download basemap files and for scheduling those requests with an {@link + * com.google.android.gnd.workers.FileDownloadWorker}. + */ +public class BasemapSelectorViewModel extends ViewModel { + @Inject + BasemapSelectorViewModel() {} + + // TODO: Implement view model. +} diff --git a/gnd/src/main/java/com/google/android/gnd/ui/common/Navigator.java b/gnd/src/main/java/com/google/android/gnd/ui/common/Navigator.java index 4e45befb0d..f2a6f6d17c 100644 --- a/gnd/src/main/java/com/google/android/gnd/ui/common/Navigator.java +++ b/gnd/src/main/java/com/google/android/gnd/ui/common/Navigator.java @@ -69,6 +69,14 @@ public void showRecordDetails(String projectId, String featureId, String recordI navigate(HomeScreenFragmentDirections.showRecordDetails(projectId, featureId, recordId)); } + /** + * Navigates from a {@link com.google.android.gnd.ui.home.HomeScreenFragment} to a {@link + * com.google.android.gnd.ui.basemapselector.BasemapSelectorFragment}. + */ + public void showBasemapSelector() { + navigate(HomeScreenFragmentDirections.showBasemapSelector()); + } + /** * Navigates from the {@link com.google.android.gnd.ui.home.HomeScreenFragment} to a {@link * com.google.android.gnd.ui.editrecord.EditRecordFragment} initialized with a new empty record diff --git a/gnd/src/main/java/com/google/android/gnd/ui/common/ViewModelModule.java b/gnd/src/main/java/com/google/android/gnd/ui/common/ViewModelModule.java index f17c81fc3b..42e9fab7ba 100644 --- a/gnd/src/main/java/com/google/android/gnd/ui/common/ViewModelModule.java +++ b/gnd/src/main/java/com/google/android/gnd/ui/common/ViewModelModule.java @@ -19,6 +19,7 @@ import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import com.google.android.gnd.MainViewModel; +import com.google.android.gnd.ui.basemapselector.BasemapSelectorViewModel; import com.google.android.gnd.ui.editrecord.EditRecordViewModel; import com.google.android.gnd.ui.home.HomeScreenViewModel; import com.google.android.gnd.ui.home.featuresheet.FeatureSheetViewModel; @@ -38,6 +39,11 @@ public abstract class ViewModelModule { @ViewModelKey(MapContainerViewModel.class) abstract ViewModel bindMapContainerViewModel(MapContainerViewModel viewModel); + @Binds + @IntoMap + @ViewModelKey(BasemapSelectorViewModel.class) + abstract ViewModel bindBasemapSelectorViewModel(BasemapSelectorViewModel viewModel); + @Binds @IntoMap @ViewModelKey(MainViewModel.class) diff --git a/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenFragment.java b/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenFragment.java index 67105d9e0b..50bc26a51a 100644 --- a/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenFragment.java +++ b/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenFragment.java @@ -224,6 +224,10 @@ private void showProjectSelector() { ProjectSelectorDialogFragment.show(getFragmentManager()); } + private void showBasemapSelector() { + viewModel.showBasemapSelector(); + } + private void onApplyWindowInsets(WindowInsetsCompat insets) { statusBarScrim.setPadding(0, insets.getSystemWindowInsetTop(), 0, 0); toolbarWrapper.setPadding(0, insets.getSystemWindowInsetTop(), 0, 0); @@ -335,6 +339,10 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { showProjectSelector(); closeDrawer(); break; + case R.id.nav_offline_maps: + showBasemapSelector(); + closeDrawer(); + break; case R.id.nav_sign_out: authenticationManager.signOut(); break; diff --git a/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenViewModel.java b/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenViewModel.java index 3d3d596b37..ba0649d97c 100644 --- a/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenViewModel.java +++ b/gnd/src/main/java/com/google/android/gnd/ui/home/HomeScreenViewModel.java @@ -146,6 +146,10 @@ public void addRecord() { navigator.addRecord(feature.getProject().getId(), feature.getId(), selectedForm.getId()); } + public void showBasemapSelector() { + navigator.showBasemapSelector(); + } + /** * Reactivates the last active project, emitting true once loaded, or false if no project was * previously activated. diff --git a/gnd/src/main/res/layout/basemap_selector_frag.xml b/gnd/src/main/res/layout/basemap_selector_frag.xml new file mode 100644 index 0000000000..4dddcb085f --- /dev/null +++ b/gnd/src/main/res/layout/basemap_selector_frag.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + diff --git a/gnd/src/main/res/menu/nav_drawer_menu.xml b/gnd/src/main/res/menu/nav_drawer_menu.xml index 15c30d38a5..1b9f3a2ee4 100644 --- a/gnd/src/main/res/menu/nav_drawer_menu.xml +++ b/gnd/src/main/res/menu/nav_drawer_menu.xml @@ -21,6 +21,10 @@ android:id="@+id/nav_join_project" android:icon="@drawable/ic_menu_project" android:title="@string/select_project_menu_item"/> + + + + + Google Play Services installation failed Sign in unsuccessful Sign out + Offline maps