Skip to content

Commit

Permalink
[RFR-363] Add Alleycat tab (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
hb0 committed May 10, 2023
1 parent 8607dfe commit c569aea
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
*/
package de.cyface.app.ui.button;

import static de.cyface.app.utils.Constants.TAG;
import static de.cyface.app.utils.SharedConstants.ACCEPTED_REPORTING_KEY;
import static de.cyface.app.utils.SharedConstants.PREFERENCES_MODALITY_KEY;
import static de.cyface.app.utils.Constants.TAG;
import static de.cyface.camera_service.Constants.PREFERENCES_CAMERA_CAPTURING_ENABLED_KEY;
import static de.cyface.camera_service.Constants.PREFERENCES_CAMERA_DISTANCE_BASED_TRIGGERING_ENABLED_KEY;
import static de.cyface.camera_service.Constants.PREFERENCES_CAMERA_RAW_MODE_ENABLED_KEY;
Expand All @@ -45,9 +45,6 @@
import static de.cyface.persistence.model.MeasurementStatus.PAUSED;
import static de.cyface.utils.DiskConsumption.spaceAvailable;

import de.cyface.app.button.AbstractButton;
import de.cyface.app.button.ButtonListener;
import de.cyface.app.utils.Map;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
Expand All @@ -74,9 +71,12 @@
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;

import de.cyface.app.R;
import de.cyface.app.CapturingFragment;
import de.cyface.app.R;
import de.cyface.app.button.AbstractButton;
import de.cyface.app.button.ButtonListener;
import de.cyface.app.utils.CalibrationDialogListener;
import de.cyface.app.utils.Map;
import de.cyface.camera_service.CameraListener;
import de.cyface.camera_service.CameraService;
import de.cyface.camera_service.Constants;
Expand All @@ -91,7 +91,6 @@
import de.cyface.datacapturing.exception.MissingPermissionException;
import de.cyface.datacapturing.model.CapturedData;
import de.cyface.datacapturing.ui.Reason;
import de.cyface.persistence.strategy.DefaultLocationCleaning;
import de.cyface.persistence.DefaultPersistenceBehaviour;
import de.cyface.persistence.DefaultPersistenceLayer;
import de.cyface.persistence.exception.NoSuchMeasurementException;
Expand All @@ -101,6 +100,7 @@
import de.cyface.persistence.model.Modality;
import de.cyface.persistence.model.ParcelableGeoLocation;
import de.cyface.persistence.model.Track;
import de.cyface.persistence.strategy.DefaultLocationCleaning;
import de.cyface.utils.DiskConsumption;
import de.cyface.utils.Validate;
import io.sentry.Sentry;
Expand Down Expand Up @@ -608,7 +608,8 @@ private boolean isRestrictionActive() {
}

if (!spaceAvailable()) {
showToastOnMainThread(context.getString(de.cyface.app.utils.R.string.error_message_capturing_canceled_no_space), false);
showToastOnMainThread(
context.getString(de.cyface.app.utils.R.string.error_message_capturing_canceled_no_space), false);
setButtonEnabled(button);
return true;
}
Expand Down Expand Up @@ -729,7 +730,8 @@ public void startUpFinished(final long measurementIdentifier) {
* @return A reference to the ProgressDialog which can be used to dismiss it.
*/
private ProgressDialog createAndShowCalibrationDialog() {
return ProgressDialog.show(context, context.getString(de.cyface.app.utils.R.string.title_dialog_starting_data_capture),
return ProgressDialog.show(context,
context.getString(de.cyface.app.utils.R.string.title_dialog_starting_data_capture),
context.getString(de.cyface.app.utils.R.string.msg_calibrating), true, false, dialog -> {
try {
dataCapturingService
Expand Down Expand Up @@ -908,7 +910,8 @@ public void onNewGeoLocationAcquired(ParcelableGeoLocation geoLocation) {
final List<Event> currentMeasurementsEvents;
try {
currentMeasurementsEvents = loadCurrentMeasurementsEvents();
capturingFragment.getMap().renderMeasurement(currentMeasurementsTracks, currentMeasurementsEvents, false);
capturingFragment.getMap().render(currentMeasurementsTracks, currentMeasurementsEvents, false,
new ArrayList<>());
} catch (NoSuchMeasurementException e) {
Log.w(TAG, "onNewGeoLocationAcquired() failed to loadCurrentMeasurementsEvents(). "
+ "Thus, map.renderMeasurement() is ignored. This should only happen id "
Expand Down
11 changes: 8 additions & 3 deletions ui/cyface/src/main/kotlin/de/cyface/app/CapturingFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ import androidx.lifecycle.Lifecycle
import androidx.navigation.fragment.NavHostFragment
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import de.cyface.app.button.SynchronizationButton
import de.cyface.app.capturing.MenuProvider
import de.cyface.app.databinding.FragmentCapturingBinding
import de.cyface.app.ui.button.DataCapturingButton
import de.cyface.app.button.SynchronizationButton
import de.cyface.app.dialog.ModalityDialog
import de.cyface.app.ui.button.DataCapturingButton
import de.cyface.app.utils.Map
import de.cyface.app.utils.ServiceProvider
import de.cyface.app.utils.SharedConstants.ACCEPTED_REPORTING_KEY
Expand Down Expand Up @@ -148,7 +148,7 @@ class CapturingFragment : Fragment(), ConnectionStatusListener {
val currentMeasurementsEvents: List<Event>
try {
currentMeasurementsEvents = dataCapturingButton!!.loadCurrentMeasurementsEvents()
map!!.renderMeasurement(currentMeasurementsTracks, currentMeasurementsEvents, false)
map!!.render(currentMeasurementsTracks, currentMeasurementsEvents, false, ArrayList())
} catch (e: NoSuchMeasurementException) {
val isReportingEnabled = preferences!!.getBoolean(ACCEPTED_REPORTING_KEY, false)
if (isReportingEnabled) {
Expand Down Expand Up @@ -314,18 +314,23 @@ class CapturingFragment : Fragment(), ConnectionStatusListener {
Modality.CAR.name -> {
tabLayout.getTabAt(0)
}

Modality.BICYCLE.name -> {
tabLayout.getTabAt(1)
}

Modality.WALKING.name -> {
tabLayout.getTabAt(2)
}

Modality.BUS.name -> {
tabLayout.getTabAt(3)
}

Modality.TRAIN.name -> {
tabLayout.getTabAt(4)
}

else -> {
throw IllegalArgumentException("Unknown Modality id: $modality")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import de.cyface.app.r4r.R
import de.cyface.app.utils.ServiceProvider
import de.cyface.app.r4r.databinding.FragmentCapturingBinding
import de.cyface.app.r4r.capturing.map.MapFragment
import de.cyface.app.r4r.capturing.marker.MarkerFragment
import de.cyface.app.r4r.capturing.speed.SpeedFragment
import de.cyface.app.r4r.utils.Constants.TAG
import de.cyface.app.utils.CalibrationDialogListener
Expand Down Expand Up @@ -272,6 +273,14 @@ class CapturingFragment : Fragment(), DataCapturingListener {
Lifecycle.State.RESUMED
)

// Dynamically add tabs (dynamically show MarkerFragment)
val tabLayout = binding.tabLayout
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.ic_baseline_map_24))
if (!MarkerFragment.eventPassed()) {
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.baseline_pin_drop_24))
}
tabLayout.addTab(tabLayout.newTab().setIcon(R.drawable.ic_baseline_speed_24))

return root
}

Expand Down Expand Up @@ -863,12 +872,16 @@ class CapturingFragment : Fragment(), DataCapturingListener {
// Ordered list, make sure titles list match this order
return if (position == 0) {
MapFragment()
} else SpeedFragment()
} else if (position == 1 && !MarkerFragment.eventPassed()) {
MarkerFragment()
} else {
SpeedFragment()
}
}

override fun getItemCount(): Int {
// List size
return 2
return if (MarkerFragment.eventPassed()) 2 else 3
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import com.google.android.gms.maps.MapsInitializer
import de.cyface.app.r4r.databinding.FragmentMapBinding
import de.cyface.app.r4r.capturing.CapturingViewModel
import de.cyface.app.r4r.capturing.CapturingViewModelFactory
import de.cyface.app.r4r.capturing.marker.MarkerFragment
import de.cyface.app.r4r.databinding.FragmentMapBinding
import de.cyface.app.r4r.utils.Constants.TAG
import de.cyface.app.utils.Map
import de.cyface.app.utils.ServiceProvider
Expand Down Expand Up @@ -129,6 +130,7 @@ class MapFragment : Fragment() {
* The `Runnable` triggered when the `Map` is loaded and ready.
*/
private val onMapReadyRunnable = Runnable {
map!!.renderMarkers(MarkerFragment.markers)
observeTracks()

// Only load track if there is an ongoing measurement
Expand All @@ -150,9 +152,10 @@ class MapFragment : Fragment() {
val observer = Observer<ArrayList<Track>?> {
if (it != null) {
//val events: List<Event> = loadCurrentMeasurementsEvents()
map!!.renderMeasurement(it, ArrayList()/* events */, false)
map!!.render(it, ArrayList()/* events */, false, MarkerFragment.markers)
} else {
map!!.clearMap()
map!!.renderMarkers(MarkerFragment.markers)
}
}
capturingViewModel.tracks.observe(viewLifecycleOwner, observer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright 2023 Cyface GmbH
*
* This file is part of the Cyface App for Android.
*
* The Cyface App for Android 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.
*
* The Cyface App for Android 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 the Cyface App for Android. If not, see <http://www.gnu.org/licenses/>.
*/
package de.cyface.app.r4r.capturing.marker

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import com.google.android.gms.maps.MapsInitializer
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.MarkerOptions
import de.cyface.app.r4r.databinding.FragmentMarkerBinding
import de.cyface.app.utils.Map
import java.util.Calendar

/**
* The [Fragment] which shows a map with markers to the user.
*
* @author Armin Schnabel
* @version 1.0.0
* @since 3.2.1
*/
class MarkerFragment : Fragment() {

/**
* This property is only valid between onCreateView and onDestroyView.
*/
private var _binding: FragmentMarkerBinding? = null

/**
* The generated class which holds all bindings from the layout file.
*/
private val binding get() = _binding!!

/**
* The `Map` used to visualize data.
*/
private var map: Map? = null

/**
* Can be launched to request permissions.
*
* The launcher ensures `map.onMapReady` is called after permissions are newly granted.
*/
private var permissionLauncher: ActivityResultLauncher<Array<String>> =
registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
if (result.isNotEmpty()) {
val allGranted = result.values.none { !it }
if (allGranted) {
// Only if the map already called it's onMapReady
if (map!!.googleMap != null) {
map!!.onMapReady()
}
} else {
Toast.makeText(
context,
"Location permission repeatedly denied",
Toast.LENGTH_LONG
).show()
// Close Cyface if permission has not been granted.
// When the user repeatedly denies the location permission, the app won't start
// and only starts again if the permissions are granted manually.
// It was always like this, but if this is a problem we need to add a screen
// which explains the user that this can happen.
requireActivity().finish()
}
}
}

/**
* The `Runnable` triggered when the `Map` is loaded and ready.
*/
private val onMapReadyRunnable = Runnable {
map!!.renderMarkers(markers)
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Using the new map renderer, which must be called before MapView is initialized:
// https://developers.google.com/maps/documentation/android-sdk/renderer
MapsInitializer.initialize(requireContext(), MapsInitializer.Renderer.LATEST) {}

_binding = FragmentMarkerBinding.inflate(inflater, container, false)
val root: View = binding.root

map = Map(binding.mapView, savedInstanceState, onMapReadyRunnable, permissionLauncher)

return root
}

override fun onResume() {
super.onResume()
map!!.onResume()
}

override fun onPause() {
super.onPause()
map!!.onPause()
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}

companion object {
fun eventPassed(): Boolean {
val now = Calendar.getInstance()
val endDate = Calendar.getInstance()
endDate.set(2023, 5 /* June = 5! */, 26)
return now.after(endDate)
}

val markers = if (eventPassed()) emptyList() else arrayListOf(
// Schkeuditz
MarkerOptions()
.position(LatLng(51.39877, 12.20104)).title("Station: Nähe Globana"),
MarkerOptions()
.position(LatLng(51.38995, 12.22101)).title("Station: Stadtmuseum"),
MarkerOptions()
.position(LatLng(51.38848, 12.23337)).title("Station: Elsterbrücke"),
MarkerOptions()
.position(LatLng(51.40696, 12.22106)).title("Station: Fußballfeld")
// Köthen
)
}
}
5 changes: 5 additions & 0 deletions ui/r4r/src/main/res/drawable/baseline_pin_drop_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M18,8c0,-3.31 -2.69,-6 -6,-6S6,4.69 6,8c0,4.5 6,11 6,11s6,-6.5 6,-11zM10,8c0,-1.1 0.9,-2 2,-2s2,0.9 2,2 -0.89,2 -2,2c-1.1,0 -2,-0.9 -2,-2zM5,20v2h14v-2L5,20z"/>
</vector>
14 changes: 1 addition & 13 deletions ui/r4r/src/main/res/layout/fragment_capturing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/pager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">

<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/ic_baseline_map_24" />

<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/ic_baseline_speed_24" />

</com.google.android.material.tabs.TabLayout>
app:layout_constraintStart_toStartOf="parent" />

<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
Expand Down
Loading

0 comments on commit c569aea

Please sign in to comment.