diff --git a/app/build.gradle b/app/build.gradle
index bd25e8f..bdb83c2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -42,6 +42,7 @@ dependencies {
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.esri.arcgisruntime:arcgis-android:100.10.0'
+ implementation 'com.esri.arcgisruntime:arcgis-android-toolkit:100.11.0-3131'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/util/Event.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/util/Event.kt
deleted file mode 100644
index 376bb9e..0000000
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/util/Event.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2020 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.esri.arcgisruntime.opensourceapps.datacollection.util
-
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.Observer
-
-/**
- * Used as a wrapper for data that is exposed via a LiveData that represents an event.
- *
- *
- * This avoids a common problem with events: on configuration change (like rotation) an update
- * can be emitted if the observer is active. This LiveData only calls the observable if there's an
- * explicit call to postValue().
- *
- * https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
- */
-open class Event(private val content: T) {
- private var hasBeenHandled = false
- /**
- * Returns the content and prevents its use again.
- */
- fun getContentIfNotHandled(): T? {
- return if (hasBeenHandled) {
- null
- } else {
- hasBeenHandled = true
- content
- }
- }
-}
-
-/**
- * Adds the given event function to observer being added to the observers list within the lifespan
- * of the given owner. The events are dispatched on the main thread.
- *
- * @param owner The LifecycleOwner which controls the observer
- * @param onEventRaised The observer function that will receive the events from the observer
-*/
-fun LiveData>.observeEvent(owner: LifecycleOwner, onEventRaised: (T) -> Unit) {
- observe(owner, Observer> { event ->
- event?.getContentIfNotHandled()?.let { onEventRaised(it) }
- })
-}
-
-/**
- * Raises an event with given argument by calling postValue() on the MutableLiveData object.
- *
- * @param arg The argument to pass to the observers
- */
-fun MutableLiveData>.raiseEvent(arg: T) {
- postValue(Event(arg))
-}
-
-/**
- * Raises an event by calling postValue() on the MutableLiveData object.
- */
-fun MutableLiveData>.raiseEvent() {
- postValue(Event(Unit))
-}
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/DataCollectionViewModel.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/DataCollectionViewModel.kt
index 0a4320b..56d2f28 100644
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/DataCollectionViewModel.kt
+++ b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/DataCollectionViewModel.kt
@@ -25,9 +25,9 @@ import androidx.security.crypto.MasterKeys
import com.esri.arcgisruntime.loadable.LoadStatus
import com.esri.arcgisruntime.mapping.ArcGISMap
import com.esri.arcgisruntime.opensourceapps.datacollection.R
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.Event
+import com.esri.arcgisruntime.toolkit.util.Event
import com.esri.arcgisruntime.opensourceapps.datacollection.util.Logger
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.raiseEvent
+import com.esri.arcgisruntime.toolkit.util.raiseEvent
import com.esri.arcgisruntime.portal.Portal
import com.esri.arcgisruntime.portal.PortalItem
import com.esri.arcgisruntime.portal.PortalUser
@@ -73,6 +73,11 @@ class DataCollectionViewModel(application: Application, val mapViewModel: MapVie
private val _bottomSheetState: MutableLiveData = MutableLiveData(BottomSheetBehavior.STATE_HIDDEN)
val bottomSheetState: LiveData = _bottomSheetState
+ private val _isShowPopupEditControls = MutableLiveData(false)
+ // Depicts whether to show the edit layout to modify the selected popup attributes
+ // being displayed in the bottomsheet
+ val isShowPopupEditControls: LiveData = _isShowPopupEditControls
+
private val encryptedSharedPrefs by lazy {
val masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC)
EncryptedSharedPreferences.create(
@@ -180,6 +185,15 @@ class DataCollectionViewModel(application: Application, val mapViewModel: MapVie
_bottomSheetState.value = bottomSheetState
}
+ /**
+ * Sets whether to display the Popup editing control view.
+ *
+ * @param showPopupEditControls
+ */
+ fun setShowPopupEditControls(showPopupEditControls: Boolean) {
+ _isShowPopupEditControls.value = showPopupEditControls
+ }
+
/**
* Loads the Portal with the given value of the loginRequired flag and sets it on the Map.
*/
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/IdentifyResultViewModel.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/IdentifyResultViewModel.kt
index 3b72fdd..21ec487 100644
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/IdentifyResultViewModel.kt
+++ b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/IdentifyResultViewModel.kt
@@ -25,8 +25,9 @@ import com.esri.arcgisruntime.layers.FeatureLayer
import com.esri.arcgisruntime.layers.LayerContent
import com.esri.arcgisruntime.mapping.GeoElement
import com.esri.arcgisruntime.mapping.view.IdentifyLayerResult
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.Event
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.raiseEvent
+import com.esri.arcgisruntime.toolkit.util.Event
+import com.esri.arcgisruntime.toolkit.util.raiseEvent
+import com.esri.arcgisruntime.toolkit.popup.PopupViewModel
/**
* The view model for IdentifyResultFragment, that is responsible for processing the result of
@@ -38,6 +39,9 @@ class IdentifyResultViewModel(val popupViewModel: PopupViewModel) : ViewModel()
private val _showIdentifyResultEvent = MutableLiveData>()
val showIdentifyResultEvent: LiveData> = _showIdentifyResultEvent
+ private val _dismissIdentifyResultEvent = MutableLiveData>()
+ val dismissIdentifyResultEvent: LiveData> = _dismissIdentifyResultEvent
+
private val _identifyLayerResult = MutableLiveData()
val identifyLayerResult: LiveData = _identifyLayerResult
@@ -89,6 +93,13 @@ class IdentifyResultViewModel(val popupViewModel: PopupViewModel) : ViewModel()
popupViewModel.clearPopup()
}
+ /**
+ * Raises an event to dismiss the identify results
+ */
+ fun dismissIdentifyLayerResult() {
+ _dismissIdentifyResultEvent.raiseEvent()
+ }
+
/**
* Raises an event to show the Popup
*/
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/PopupViewModel.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/PopupViewModel.kt
deleted file mode 100644
index cced00f..0000000
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/viewmodels/PopupViewModel.kt
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright 2020 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels
-
-import android.app.Application
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import com.esri.arcgisruntime.ArcGISRuntimeException
-import com.esri.arcgisruntime.concurrent.ListenableFuture
-import com.esri.arcgisruntime.data.Feature
-import com.esri.arcgisruntime.data.FeatureEditResult
-import com.esri.arcgisruntime.data.FeatureTable
-import com.esri.arcgisruntime.data.ServiceFeatureTable
-import com.esri.arcgisruntime.mapping.popup.Popup
-import com.esri.arcgisruntime.mapping.popup.PopupManager
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.Event
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.raiseEvent
-
-/**
- * The view model that represents a Popup. It supports:
- *
- *
- *
Viewing a GeoElement's attributes
- *
Editing a GeoElement's attributes as well as saving
- *
- *
- * A PopupViewModel can be bound to a PopupView for visualisation of the Popup GeoElement's
- * attributes and editing experience.
- */
-class PopupViewModel(application: Application) : AndroidViewModel(application) {
-
- private val _popup = MutableLiveData()
- // The Popup whose fields are viewed and edited in the PopupView
- val popup: LiveData = _popup
-
- private val _popupManager = MutableLiveData()
- // The manager for the Popup responsible for viewing and editing of the Popup
- val popupManager: LiveData = _popupManager
-
- private val _isPopupInEditMode = MutableLiveData()
- // Depicts whether the PopupManager is currently in editing mode
- // When in edit mode the user can edit the values of fields of the Popup
- val isPopupInEditMode: LiveData = _isPopupInEditMode
-
- private val _showSavePopupErrorEvent = MutableLiveData>()
- // This event is raised when an error is encountered while trying to save edits on a popup.
- // It passes the exception message to all the observers in the observeEvent() method.
- val showSavePopupErrorEvent: LiveData> = _showSavePopupErrorEvent
-
- private val _showSavingProgressEvent = MutableLiveData>()
- // This event is raised when the save operation begins/ends.
- // It passes true to all the observers in the observeEvent() method when the save operation
- // is initiated and false when it ends.
- val showSavingProgressEvent: LiveData> = _showSavingProgressEvent
-
- private val _confirmCancelPopupEditingEvent = MutableLiveData>()
- // This event is raised when the user cancels the edit mode on the Popup.
- // It is used for showing confirmation dialog to the user, before calling cancelEditing()
- val confirmCancelPopupEditingEvent: LiveData> = _confirmCancelPopupEditingEvent
-
- /**
- * Updates popup property to set the popup field values being displayed in
- * the bottom sheet. Creates PopupManager for PopupView to perform edit operations.
- *
- * @param popup
- */
- fun setPopup(popup: Popup) {
- _popup.value = popup
- _popupManager.value = PopupManager(getApplication(), _popup.value)
- }
-
- /**
- * Enables/disables edit mode on the PopupView
- */
- fun setEditMode(isEnabled: Boolean) {
- _isPopupInEditMode.value = isEnabled
- }
-
- /**
- * Clear the popup
- */
- fun clearPopup() {
- _popup.value = null
- _popupManager.value = null
- }
-
- /**
- * Cancels the edit mode.
- */
- fun cancelEditing() {
- _popupManager.value?.cancelEditing()
- _isPopupInEditMode.value = false
- }
-
- /**
- * Raises ConfirmCancelPopupEditingEvent that can be observed and used for
- * prompting user with confirmation dialog to make sure the user wants to cancel edits.
- * To be followed by cancelEditing() if the user response is positive.
- */
- fun confirmCancelEditing() {
- _confirmCancelPopupEditingEvent.raiseEvent()
- }
-
- /**
- * Saves Popup edits by applying changes to the feature service associated with a Popup's
- * feature.
- */
- fun savePopupEdits() {
- // show the Progress bar informing user that save operation is in progress
- _showSavingProgressEvent.raiseEvent(true)
- _popupManager.value?.let { popupManager ->
- // Call finishEditingAsync() to apply edit changes locally and end the popup manager
- // editing session
- val finishEditingFuture: ListenableFuture =
- popupManager.finishEditingAsync()
- finishEditingFuture.addDoneListener {
- try {
- finishEditingFuture.get()
-
- // The edits were applied successfully to the local geodatabase,
- // push those changes to the feature service by calling applyEditsAsync()
- val feature: Feature = popupManager.popup.geoElement as Feature
- val featureTable: FeatureTable = feature.featureTable
- if (featureTable is ServiceFeatureTable) {
- val applyEditsFuture: ListenableFuture> =
- featureTable.applyEditsAsync()
- applyEditsFuture.addDoneListener {
- // dismiss the Progress bar
- _showSavingProgressEvent.raiseEvent(false)
- // dismiss edit mode
- _isPopupInEditMode.value = false
- try {
- val featureEditResults: List =
- applyEditsFuture.get()
- // Check for errors in FeatureEditResults
- if (featureEditResults.any { result -> result.hasCompletedWithErrors() }) {
- // an error was encountered when trying to apply edits
- val exception =
- featureEditResults.filter { featureEditResult -> featureEditResult.hasCompletedWithErrors() }[0].error
- // show the error message to the user
- exception.message?.let { exceptionMessage ->
- _showSavePopupErrorEvent.raiseEvent(exceptionMessage)
- }
- }
-
- } catch (exception: Exception) {
- // show the error message to the user
- exception.message?.let { exceptionMessage ->
- _showSavePopupErrorEvent.raiseEvent(exceptionMessage)
- }
- }
- }
- }
- } catch (exception: Exception) {
- // dismiss the Progress bar
- _showSavingProgressEvent.raiseEvent(false)
- // show the error message to the user
- exception.message?.let { exceptionMessage ->
- _showSavePopupErrorEvent.raiseEvent(exceptionMessage)
- }
- }
- }
- }
- }
-}
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/DataCollectionFragment.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/DataCollectionFragment.kt
index 664a4b7..2bea997 100644
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/DataCollectionFragment.kt
+++ b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/DataCollectionFragment.kt
@@ -17,6 +17,7 @@
package com.esri.arcgisruntime.opensourceapps.datacollection.views.fragments
import android.os.Bundle
+import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
@@ -27,7 +28,6 @@ import androidx.core.view.GravityCompat
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Observer
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
@@ -37,15 +37,21 @@ import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener
import com.esri.arcgisruntime.opensourceapps.datacollection.R
import com.esri.arcgisruntime.opensourceapps.datacollection.databinding.FragmentDataCollectionBinding
import com.esri.arcgisruntime.opensourceapps.datacollection.util.Logger
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.observeEvent
import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.DataCollectionViewModel
import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.IdentifyResultViewModel
import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.MapViewModel
-import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.PopupViewModel
import com.esri.arcgisruntime.security.AuthenticationManager
import com.esri.arcgisruntime.security.DefaultAuthenticationChallengeHandler
+import com.esri.arcgisruntime.toolkit.popup.PopupViewModel
+import com.esri.arcgisruntime.toolkit.util.observeEvent
import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetBehavior.*
+import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
+import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING
+import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
+import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED
+import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
+import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING
+import com.google.android.material.bottomsheet.BottomSheetBehavior.from
import kotlinx.android.synthetic.main.fragment_data_collection.*
import java.security.InvalidParameterException
import kotlin.math.roundToInt
@@ -80,25 +86,15 @@ class DataCollectionFragment : Fragment() {
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
+ dataCollectionViewModel.setShowPopupEditControls(false)
when {
// we handle the back button press here to navigate back in the bottomsheet.
// when the user presses the back button when PopupAttributesListFragment is showing
// from the bottomsheet_navigation graph we pop it from the BackStack to go to the
// previous IdentifyResultFragment. When the backstack is at identifyResultFragment
- // popBackStack() will return a false and we will hide the bottomsheet and clear the
- // selected feature in the Featurelayer. If the bottomsheet is already hidden exit
- // the DataCollectionActivity.
+ // popBackStack() will return a false and we will exit the DataCollectionActivity.
!bottomSheetNavController.popBackStack(R.id.identifyResultFragment, false) ->
- if (bottomSheetBehavior.state == STATE_COLLAPSED) {
- dataCollectionViewModel.setCurrentBottomSheetState(STATE_HIDDEN)
- resetIdentifyResult()
- } else {
- requireActivity().finish()
- }
- // if the bottomsheet is in the STATE_EXPANDED then we are showing
- // PopupAttributeListFragment and we need return back to the IdentifyResultFragment
- // which shows up in STATE_COLLAPSED
- bottomSheetBehavior.state == STATE_EXPANDED -> dataCollectionViewModel.setCurrentBottomSheetState(STATE_COLLAPSED)
+ requireActivity().finish()
}
}
}
@@ -116,6 +112,7 @@ class DataCollectionFragment : Fragment() {
val view = fragmentDataCollectionBinding.root
+ fragmentDataCollectionBinding.popupViewModel = popupViewModel
fragmentDataCollectionBinding.dataCollectionViewModel = dataCollectionViewModel
fragmentDataCollectionBinding.lifecycleOwner = this
@@ -128,8 +125,30 @@ class DataCollectionFragment : Fragment() {
?: throw InvalidParameterException("bottomSheetNavHostFragment must exist")
bottomSheetNavController = bottomSheetNavHostFragment.findNavController()
- dataCollectionViewModel.bottomSheetState.observe(viewLifecycleOwner, Observer {
- bottomSheetBehavior.state = it
+ dataCollectionViewModel.bottomSheetState.observe(viewLifecycleOwner, { bottomSheetState ->
+ if (bottomSheetState == STATE_HIDDEN) {
+ bottomSheetBehavior.isHideable = true
+ }
+ bottomSheetBehavior.state = bottomSheetState
+ })
+
+ bottomSheetBehavior.addBottomSheetCallback(object :
+ BottomSheetBehavior.BottomSheetCallback() {
+
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {
+ // handle onSlide
+ }
+
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ if (bottomSheetNavController.currentDestination?.id == R.id.popupFragment) {
+ when (newState) {
+ STATE_COLLAPSED -> dataCollectionViewModel.setShowPopupEditControls(false)
+ STATE_HIDDEN -> dataCollectionViewModel.setShowPopupEditControls(false)
+ STATE_HALF_EXPANDED -> dataCollectionViewModel.setShowPopupEditControls(true)
+ STATE_EXPANDED -> dataCollectionViewModel.setShowPopupEditControls(true)
+ }
+ }
+ }
})
// On orientation change if we have a valid value for identifyLayerResult,
@@ -143,24 +162,34 @@ class DataCollectionFragment : Fragment() {
}
identifyResultViewModel.showPopupEvent.observeEvent(viewLifecycleOwner) {
+ if (bottomSheetBehavior.state != STATE_COLLAPSED) {
+ dataCollectionViewModel.setShowPopupEditControls(true)
+ }
bottomSheetNavController.navigate(R.id.action_identifyResultFragment_to_popupFragment)
- // PopupAttributeListFragment shows all popup attributes, so we
- // show them in expanded(full screen) state of the bottom sheet
- dataCollectionViewModel.setCurrentBottomSheetState(STATE_EXPANDED)
}
identifyResultViewModel.showIdentifyResultEvent.observeEvent(viewLifecycleOwner) {
- // user has kicked off event to show IdentifyResultsFragment by tapping on the header of
- // bottomsheet containing popupAttributeListFragment.
- if (bottomSheetNavController.currentDestination?.id == R.id.popupFragment) {
- bottomSheetNavController.popBackStack()
+ // IdentifyResultFragment shows a few selected popup attributes. We
+ // show them in half expanded state of the bottom sheet
+ if (dataCollectionViewModel.bottomSheetState.value == STATE_HIDDEN) {
+ bottomSheetBehavior.isHideable = false
+ dataCollectionViewModel.setCurrentBottomSheetState(STATE_HALF_EXPANDED)
}
- // IdentifyResultFragment only shows a few selected popup attributes, so we
- // show them in collapsed state(roughly 1/4 screen size) of the bottom sheet
- dataCollectionViewModel.setCurrentBottomSheetState(STATE_COLLAPSED)
}
- requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
+ popupViewModel.dismissPopupEvent.observeEvent(viewLifecycleOwner) {
+ resetIdentifyResult()
+ dataCollectionViewModel.setShowPopupEditControls(false)
+ dataCollectionViewModel.setCurrentBottomSheetState(STATE_HIDDEN)
+ bottomSheetNavController.popBackStack(R.id.identifyResultFragment, false)
+ }
+
+ identifyResultViewModel.dismissIdentifyResultEvent.observeEvent(viewLifecycleOwner) {
+ resetIdentifyResult()
+ dataCollectionViewModel.setCurrentBottomSheetState(STATE_HIDDEN)
+ }
+
+ requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, onBackPressedCallback)
return view
}
@@ -169,7 +198,7 @@ class DataCollectionFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
// setup the app bar and drawer layout
- val navController = activity?.findNavController(R.id.bottomSheetNavHostFragment)
+ val navController = activity?.findNavController(R.id.navHostFragment)
navController?.let {
val appBarConfiguration = AppBarConfiguration(navController.graph, drawer_layout)
view.findViewById(R.id.toolbar)
@@ -189,37 +218,31 @@ class DataCollectionFragment : Fragment() {
}
}
- // Handle the navigation button clicks on the toolbar to open the drawer and act as a back
- // button
- toolbar.setNavigationOnClickListener {
- // When the user presses the navigation button to go back from PopupAttributeListFragment
- // to IdentifyResultFragment in the bottomsheet, the bottomSheetNavController will still
- // be holding on to PopupAttributeListFragment as the current destination when we last
- // navigated to it. If that is the case we navigate to IdentifyResultFragment, else we
- // have already navigated to IdentifyResultFragment in the bottomsheet and
- // show the drawer layout.
- if (bottomSheetNavController.currentDestination?.id == R.id.popupFragment) {
- requireActivity().onBackPressed()
- } else {
- drawer_layout.openDrawer(GravityCompat.START)
- }
- }
-
mapView.onTouchListener =
object : DefaultMapViewOnTouchListener(context, mapView) {
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
- dataCollectionViewModel.setCurrentBottomSheetState(STATE_HIDDEN)
-
- e?.let {
- val screenPoint = android.graphics.Point(
- it.x.roundToInt(),
- it.y.roundToInt()
- )
- identifyLayer(screenPoint)
+
+ // Only perform identify on the mapview if the Popup is not in edit mode
+ if (popupViewModel.isPopupInEditMode.value == false) {
+ // If the user tapped on the mapview to perform an identify and
+ // is currently looking at a popup's attributes we move back to
+ // IdentifyResultFragment to perform identify
+ if (bottomSheetNavController.currentDestination?.id == R.id.popupFragment) {
+ bottomSheetNavController.popBackStack(R.id.identifyResultFragment, false)
+ }
+
+ dataCollectionViewModel.setCurrentBottomSheetState(STATE_HIDDEN)
+
+ e?.let {
+ val screenPoint = android.graphics.Point(
+ it.x.roundToInt(),
+ it.y.roundToInt()
+ )
+ identifyLayer(screenPoint)
+ }
}
return true
}
-
}
}
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/IdentifyResultFragment.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/IdentifyResultFragment.kt
index ce9d236..ad6c58a 100644
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/IdentifyResultFragment.kt
+++ b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/IdentifyResultFragment.kt
@@ -26,7 +26,7 @@ import androidx.fragment.app.activityViewModels
import com.esri.arcgisruntime.opensourceapps.datacollection.R
import com.esri.arcgisruntime.opensourceapps.datacollection.databinding.FragmentIdentifyResultBinding
import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.IdentifyResultViewModel
-import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.PopupViewModel
+import com.esri.arcgisruntime.toolkit.popup.PopupViewModel
/**
* Responsible for displaying properties of the resulting GeoElement of an identify operation in
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/PopupFragment.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/PopupFragment.kt
index e69ad15..729eab1 100644
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/PopupFragment.kt
+++ b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/fragments/PopupFragment.kt
@@ -20,15 +20,16 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.view.WindowManager
import androidx.appcompat.app.AlertDialog
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
+import com.esri.arcgisruntime.mapping.popup.Popup
import com.esri.arcgisruntime.opensourceapps.datacollection.R
import com.esri.arcgisruntime.opensourceapps.datacollection.databinding.FragmentPopupBinding
-import com.esri.arcgisruntime.opensourceapps.datacollection.util.observeEvent
-import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.IdentifyResultViewModel
-import com.esri.arcgisruntime.opensourceapps.datacollection.viewmodels.PopupViewModel
+import com.esri.arcgisruntime.toolkit.popup.PopupViewModel
+import com.esri.arcgisruntime.toolkit.util.observeEvent
import kotlinx.android.synthetic.main.fragment_popup.*
/**
@@ -54,9 +55,13 @@ class PopupFragment : Fragment() {
popupViewModel.showSavingProgressEvent.observeEvent(viewLifecycleOwner) { isShowProgressBar ->
if (isShowProgressBar) {
+ requireActivity().window.setFlags(
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
progressBarLayout.visibility = View.VISIBLE
} else {
progressBarLayout.visibility = View.GONE
+ requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
}
}
@@ -68,9 +73,36 @@ class PopupFragment : Fragment() {
showConfirmCancelEditingDialog()
}
+ popupViewModel.confirmDeletePopupEvent.observeEvent(viewLifecycleOwner) {
+ showConfirmDeletePopupDialog()
+ }
+
+ popupViewModel.showDeletePopupErrorEvent.observeEvent(viewLifecycleOwner) { errorMessage ->
+ showAlertDialog(errorMessage)
+ }
+
return binding.root
}
+ /**
+ * Shows dialog to confirm deleting the popup.
+ */
+ private fun showConfirmDeletePopupDialog() {
+ val dialogBuilder = AlertDialog.Builder(requireContext())
+ dialogBuilder.setMessage("Delete ${(popupViewModel.popup.value as Popup).title}?")
+ .setCancelable(false)
+ // positive button text and action
+ .setPositiveButton(getString(R.string.ok)) { dialog, id ->
+ popupViewModel.deletePopup()
+ }
+ // negative button text and action
+ .setNegativeButton(getString(R.string.cancel)) { dialog, id -> dialog.cancel()
+ }
+ val alert = dialogBuilder.create()
+ // show alert dialog
+ alert.show()
+ }
+
/**
* Shows dialog to confirm cancelling edit mode on popup view.
*/
diff --git a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/popup/PopupView.kt b/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/popup/PopupView.kt
deleted file mode 100644
index 86566fe..0000000
--- a/app/src/main/java/com/esri/arcgisruntime/opensourceapps/datacollection/views/popup/PopupView.kt
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright 2020 Esri
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.esri.arcgisruntime.opensourceapps.datacollection.views.popup
-
-import android.content.Context
-import android.content.res.ColorStateList
-import android.graphics.Color
-import android.text.InputType
-import android.util.AttributeSet
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.EditText
-import android.widget.FrameLayout
-import android.widget.Spinner
-import android.widget.TextView
-import androidx.core.widget.doAfterTextChanged
-import androidx.databinding.DataBindingUtil
-import androidx.databinding.ViewDataBinding
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.ListAdapter
-import androidx.recyclerview.widget.RecyclerView
-import com.esri.arcgisruntime.ArcGISRuntimeException
-import com.esri.arcgisruntime.data.CodedValueDomain
-import com.esri.arcgisruntime.data.Field
-import com.esri.arcgisruntime.mapping.popup.Popup
-import com.esri.arcgisruntime.mapping.popup.PopupField
-import com.esri.arcgisruntime.mapping.popup.PopupManager
-import com.esri.arcgisruntime.opensourceapps.datacollection.BR
-import com.esri.arcgisruntime.opensourceapps.datacollection.R
-import kotlinx.android.synthetic.main.item_popup_row.view.*
-import kotlinx.android.synthetic.main.layout_popupview.view.*
-
-private const val TAG = "PopupView"
-
-/**
- * Displays the popup attribute list in a [RecyclerView].
- */
-class PopupView : FrameLayout {
-
- private val popupAttributeListAdapter by lazy { PopupAttributeListAdapter() }
- private var isEditMode: Boolean = false
-
- lateinit var popupManager: PopupManager
- lateinit var popup: Popup
-
- /**
- * Constructor used when instantiating this View directly to attach it to another view programmatically.
- */
- constructor(context: Context) : super(context) {
- init(context)
- }
-
- /**
- * Constructor used when defining this view in an XML layout.
- */
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
- init(context)
- }
-
- /**
- * Initializes this PopupView by inflating the layout and setting the [RecyclerView] adapter.
- */
- private fun init(context: Context) {
- inflate(context, R.layout.layout_popupview, this)
- popupRecyclerView.layoutManager = LinearLayoutManager(context)
- popupRecyclerView.adapter = popupAttributeListAdapter
- }
-
- /**
- * Enables/Disables edit mode on the PopupView.
- */
- fun setEditMode(isEnabled: Boolean) {
- isEditMode = isEnabled
- if (isEnabled) {
- popupAttributeListAdapter.submitList(popupManager.editableFields)
- popupManager.startEditing()
- } else {
- popupAttributeListAdapter.submitList(popupManager.displayedFields)
- }
- popupAttributeListAdapter.notifyDataSetChanged()
- }
-
- /**
- * Adapter used by PopupView to display a list of PopupAttributes in a
- * recyclerView.
- */
- private inner class PopupAttributeListAdapter :
- ListAdapter(DiffCallback()) {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
- val inflater = LayoutInflater.from(parent.context)
- val binding = DataBindingUtil.inflate(
- inflater,
- R.layout.item_popup_row,
- parent,
- false
- )
- return ViewHolder(binding)
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val popupField: PopupField = getItem(position)
- holder.updateView(popupField)
-
- holder.bind(popupField)
- }
- }
-
- /**
- * Callback for calculating the diff between two non-null items in a list.
- */
- private class DiffCallback : DiffUtil.ItemCallback() {
-
- override fun areItemsTheSame(
- oldItem: PopupField,
- newItem: PopupField
- ): Boolean {
- return oldItem == newItem
- }
-
- override fun areContentsTheSame(
- oldItem: PopupField,
- newItem: PopupField
- ): Boolean {
- return oldItem.fieldName == newItem.fieldName
- }
- }
-
- /**
- * The PopupAttributeListAdapter ViewHolder.
- */
- private inner class ViewHolder(private val binding: ViewDataBinding) :
- RecyclerView.ViewHolder(binding.root) {
-
- val labelTextView: TextView by lazy {
- binding.root.labelTextView
- }
-
- val valueTextView: TextView by lazy {
- binding.root.valueTextView
- }
-
- val valueEditText: EditText by lazy {
- binding.root.valueEditText
- }
-
- val codedValueDomainSpinner: Spinner by lazy {
- binding.root.codedValueDomainSpinner
- }
-
- fun bind(
- popupField: PopupField
- ) {
- binding.setVariable(BR.popupField, popupField)
- binding.setVariable(BR.popupManager, popupManager)
- binding.executePendingBindings()
- }
-
- /**
- * Toggles the view for popup field value from edittext to textview and vice-versa, given the
- * edit mode of the popupView.
- */
- fun updateView(popupField: PopupField) {
- if (isEditMode) {
- val codedValueDomain : CodedValueDomain? = popupManager.getDomain(popupField) as? CodedValueDomain
- if (codedValueDomain != null) {
- setUpSpinner(codedValueDomain, popupField)
- valueEditText.visibility = View.GONE
- valueTextView.visibility = View.GONE
- codedValueDomainSpinner.visibility = View.VISIBLE
- } else {
- valueEditText.inputType = getInputType(popupManager.getFieldType(popupField))
- valueEditText.visibility = View.VISIBLE
- valueTextView.visibility = View.GONE
- //save original colors
- val oldColors: ColorStateList = labelTextView.textColors
- // here we assign and hold the values of the editable fields, entered by the user
- // in popupAttribute.tempValue
- valueEditText.doAfterTextChanged {
- if (valueEditText.hasFocus()) {
-
- val validationError: ArcGISRuntimeException? = updateValue(
- popupField,
- valueEditText.text.toString()
- )
- if (validationError != null) {
- val fieldLabelWithValidationError =
- popupField.label + ": " + validationError.message
- labelTextView.text = fieldLabelWithValidationError
- labelTextView.setTextColor(Color.RED)
- } else {
- labelTextView.text = popupField.label
- labelTextView.setTextColor(oldColors)
- }
- }
- }
- }
- } else {
- valueEditText.visibility = View.GONE
- codedValueDomainSpinner.visibility = View.GONE
- valueTextView.visibility = View.VISIBLE
- }
- }
-
- /**
- * Sets up spinner for PopupFields that have a CodedValueDomain.
- */
- private fun setUpSpinner(codedValueDomain: CodedValueDomain, popupField: PopupField) {
- val codedValuesNames = mutableListOf()
- codedValueDomain.codedValues.forEach { codedValue -> codedValuesNames.add(codedValue.name) }
- codedValueDomainSpinner.adapter = ArrayAdapter(
- binding.root.context,
- android.R.layout.simple_spinner_dropdown_item,
- codedValuesNames
- )
- val codedValuePosition = codedValueDomain.codedValues.indexOfFirst { codedValue ->
- codedValue.code == popupManager.getFieldValue(popupField)
- }
- // set the PopupField value as selected in the spinner
- codedValueDomainSpinner.setSelection(codedValuePosition)
- codedValueDomainSpinner.onItemSelectedListener =
- object : AdapterView.OnItemSelectedListener {
- override fun onNothingSelected(parent: AdapterView<*>?) {
- TODO("Not yet implemented")
- }
-
- override fun onItemSelected(
- parent: AdapterView<*>?,
- view: View?,
- position: Int,
- id: Long
- ) {
- popupManager.updateValue(
- codedValueDomain.codedValues[position].code,
- popupField
- )
- }
- }
- }
-
- /**
- * Updates the value of the specified PopupField to the appropriately cast string value of
- * the specified value
- */
- private fun updateValue(popupField: PopupField, newValue: String): ArcGISRuntimeException? {
- var error: ArcGISRuntimeException? = null
- when (popupManager.getFieldType(popupField)) {
- Field.Type.SHORT -> error =
- if (newValue.toShortOrNull() != null) {
- popupManager.updateValue(newValue.toShort(), popupField)
- } else {
- popupManager.updateValue(newValue, popupField)
- }
- Field.Type.INTEGER -> error =
- if (newValue.toIntOrNull() != null) {
- popupManager.updateValue(newValue.toInt(), popupField)
- } else {
- popupManager.updateValue(newValue, popupField)
- }
- Field.Type.FLOAT -> error =
- if (newValue.toFloatOrNull() != null) {
- popupManager.updateValue(newValue.toFloat(), popupField)
- } else {
- popupManager.updateValue(newValue, popupField)
- }
- Field.Type.DOUBLE -> error =
- if (newValue.toDoubleOrNull() != null) {
- popupManager.updateValue(newValue.toDouble(), popupField)
- } else {
- popupManager.updateValue(newValue, popupField)
- }
- Field.Type.TEXT -> error = popupManager.updateValue(newValue, popupField)
-
- else -> Log.i(
- TAG,
- "Unhandled field type: " + popupManager.getFieldType(popupField)
- )
- }
- return error
- }
-
- /**
- * Returns the int value representing the input type for EditText view.
- */
- private fun getInputType(fieldType: Field.Type): Int {
- return when (fieldType) {
- Field.Type.SHORT, Field.Type.INTEGER -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_SIGNED
- Field.Type.FLOAT, Field.Type.DOUBLE -> InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED
- else -> InputType.TYPE_CLASS_TEXT
- }
- }
- }
-}
diff --git a/app/src/main/res/drawable/ic_save_24.xml b/app/src/main/res/drawable/ic_save_24.xml
new file mode 100644
index 0000000..a73dcf2
--- /dev/null
+++ b/app/src/main/res/drawable/ic_save_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_trash_24.xml b/app/src/main/res/drawable/ic_trash_24.xml
new file mode 100644
index 0000000..db47363
--- /dev/null
+++ b/app/src/main/res/drawable/ic_trash_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_x_circle_24.xml b/app/src/main/res/drawable/ic_x_circle_24.xml
new file mode 100644
index 0000000..7720aa5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_x_circle_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-w600dp/fragment_data_collection.xml b/app/src/main/res/layout-w600dp/fragment_data_collection.xml
index ea91a83..43cc201 100644
--- a/app/src/main/res/layout-w600dp/fragment_data_collection.xml
+++ b/app/src/main/res/layout-w600dp/fragment_data_collection.xml
@@ -19,10 +19,14 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
-
+
+
+
-
-
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/toolbar"
+ app:map="@{dataCollectionViewModel.mapViewModel.map}" />
-
+
@@ -83,9 +93,100 @@
app:navGraph="@navigation/bottomsheet_navigation" />
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
-
-
-
-
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/toolbar"
+ app:map="@{dataCollectionViewModel.mapViewModel.map}" />
+
+
@@ -83,9 +93,97 @@
app:navGraph="@navigation/bottomsheet_navigation" />
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ type="com.esri.arcgisruntime.toolkit.popup.PopupViewModel"/>
@@ -45,6 +45,13 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="00.333" />
+
+
@@ -67,14 +74,27 @@
android:text="@{popupViewModel.popup}"
android:textAlignment="viewStart"
android:textSize="14sp"
- app:layout_constraintStart_toEndOf="@id/symbol"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.148"
- app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.65"/>
+
+
diff --git a/app/src/main/res/layout/fragment_navigation_drawer.xml b/app/src/main/res/layout/fragment_navigation_drawer.xml
index ef9cbbb..7a2c343 100644
--- a/app/src/main/res/layout/fragment_navigation_drawer.xml
+++ b/app/src/main/res/layout/fragment_navigation_drawer.xml
@@ -27,54 +27,89 @@
-
-
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+
-
+ app:layout_constraintTop_toBottomOf="@+id/profileStartGuideline"
+ app:layout_constraintVertical_bias="0.407">
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/profileStartGuideline"
+ app:layout_constraintVertical_bias="0.307" />
+ app:layout_constraintTop_toBottomOf="@+id/accessPortal"
+ app:layout_constraintVertical_bias="0.0" />
+ app:layout_constraintTop_toBottomOf="@+id/profileStartGuideline"
+ app:layout_constraintVertical_bias="0.333" />
-
+
-
-
-
-
-
+ app:layout_constraintTop_toTopOf="@id/profileEndGuideline" />
-
-
+
+
+
diff --git a/app/src/main/res/layout/fragment_popup.xml b/app/src/main/res/layout/fragment_popup.xml
index ef27ff2..36f49f7 100644
--- a/app/src/main/res/layout/fragment_popup.xml
+++ b/app/src/main/res/layout/fragment_popup.xml
@@ -21,7 +21,7 @@
+ type="com.esri.arcgisruntime.toolkit.popup.PopupViewModel"/>
-
-
-
-
+ app:layout_constraintVertical_bias="0.464"
+ app:srcCompat="@drawable/ic_x_circle_24" />
@@ -150,30 +119,37 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
- app:layout_constraintGuide_begin="64dp" />
+ app:layout_constraintGuide_begin="80dp" />
-
-
+
+
+
diff --git a/app/src/main/res/layout/item_popup_row.xml b/app/src/main/res/layout/item_popup_row.xml
index e8ea9a8..061a0c7 100644
--- a/app/src/main/res/layout/item_popup_row.xml
+++ b/app/src/main/res/layout/item_popup_row.xml
@@ -29,61 +29,105 @@
+ android:layout_height="105dp">
+ android:textSize="14sp"
+ android:textAlignment="viewStart"
+ app:layout_constraintBottom_toTopOf="@id/labelFieldValueFieldSeparator"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="@+id/verticalConstraintGuideline"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintVertical_bias="1.0" />
-
+
+
+
+
+ app:layout_constraintVertical_bias="0.407">
+
+
+
+
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintStart_toStartOf="@+id/verticalConstraintGuideline"
+ app:layout_constraintTop_toBottomOf="@+id/labelFieldValueFieldSeparator"
+ app:layout_constraintVertical_bias="0.407" />
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@id/guideline" />
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 21dbd5b..19c327e 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,7 +1,7 @@
- #008577
- #00574B
+ #60993D
+ #497C29#D81B60#DBEEDB
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index bcc8d60..1d31e96 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,6 +1,6 @@
-