feat: add option to show phone GPS location on map

This commit is contained in:
Andre K 2023-07-31 22:56:15 -03:00 committed by GitHub
parent a51e6afd4e
commit 377c6a18e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 192 additions and 55 deletions

View file

@ -7,7 +7,11 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
@ -25,8 +29,10 @@ import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.viewmodel.compose.viewModel
import com.geeksville.mesh.BuildConfig
@ -36,6 +42,10 @@ import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.R
import com.geeksville.mesh.android.BuildUtils.debug
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.android.getLocationPermissions
import com.geeksville.mesh.android.gpsDisabled
import com.geeksville.mesh.android.hasGps
import com.geeksville.mesh.android.hasLocationPermission
import com.geeksville.mesh.copy
import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.model.UIViewModel
@ -46,7 +56,7 @@ import com.geeksville.mesh.ui.ScreenFragment
import com.geeksville.mesh.ui.map.components.CacheLayout
import com.geeksville.mesh.ui.map.components.DownloadButton
import com.geeksville.mesh.ui.map.components.EditWaypointDialog
import com.geeksville.mesh.ui.map.components.MapStyleButton
import com.geeksville.mesh.ui.components.IconButton
import com.geeksville.mesh.util.EnableWakeLock
import com.geeksville.mesh.util.SqlTileWriterExt
import com.geeksville.mesh.util.requiredZoomLevel
@ -79,6 +89,7 @@ import org.osmdroid.views.overlay.Polygon
import org.osmdroid.views.overlay.TilesOverlay
import org.osmdroid.views.overlay.gridlines.LatLonGridlineOverlay2
import org.osmdroid.views.overlay.infowindow.InfoWindow
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
import java.io.File
import java.text.DateFormat
@ -126,6 +137,7 @@ fun MapView(model: UIViewModel = viewModel()) {
// Map Elements
var downloadRegionBoundingBox: BoundingBox? by remember { mutableStateOf(null) }
var myLocationOverlay: MyLocationNewOverlay? by remember { mutableStateOf(null) }
val context = LocalContext.current
val mPrefs = context.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
@ -133,6 +145,8 @@ fun MapView(model: UIViewModel = viewModel()) {
val haptic = LocalHapticFeedback.current
fun performHapticFeedback() = haptic.performHapticFeedback(HapticFeedbackType.LongPress)
val hasGps = context.hasGps()
EnableWakeLock(context)
val map = remember {
@ -140,6 +154,43 @@ fun MapView(model: UIViewModel = viewModel()) {
clipToOutline = true
}
}
fun toggleMyLocation() {
if (context.gpsDisabled()) {
debug("Telling user we need location turned on for MyLocationNewOverlay")
model.showSnackbar(R.string.location_disabled)
return
}
debug("user clicked MyLocationNewOverlay ${myLocationOverlay == null}")
if (myLocationOverlay == null) {
myLocationOverlay = MyLocationNewOverlay(map).apply {
enableMyLocation()
enableFollowLocation()
AppCompatResources.getDrawable(context, R.drawable.ic_location_dot_24)?.let {
setPersonIcon(it.toBitmap())
setPersonAnchor(0.5f, 0.5f)
}
}
map.overlays.add(myLocationOverlay)
} else {
myLocationOverlay?.apply {
disableMyLocation()
disableFollowLocation()
}
map.overlays.remove(myLocationOverlay)
myLocationOverlay = null
}
}
val requestPermissionAndToggleLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
if (permissions.entries.all { it.value }) toggleMyLocation()
}
fun requestPermissionAndToggle() {
requestPermissionAndToggleLauncher.launch(context.getLocationPermissions())
}
val nodes by model.nodeDB.nodes.observeAsState(emptyMap())
val waypoints by model.waypoints.observeAsState(emptyMap())
@ -388,6 +439,9 @@ fun MapView(model: UIViewModel = viewModel()) {
if (overlays.none { it is MapEventsOverlay }) {
overlays.add(0, MapEventsOverlay(mapEventsReceiver))
}
if (myLocationOverlay != null && overlays.none { it is MyLocationNewOverlay }) {
overlays.add(myLocationOverlay)
}
addCopyright() // Copyright is required for certain map sources
createLatLongGrid(false)
@ -601,10 +655,28 @@ fun MapView(model: UIViewModel = viewModel()) {
}
},
modifier = Modifier.align(Alignment.BottomCenter)
) else MapStyleButton(
onClick = { showMapStyleDialog() },
modifier = Modifier.align(Alignment.TopEnd),
)
) else Column(
modifier = Modifier
.padding(top = 16.dp, end = 16.dp)
.align(Alignment.TopEnd),
) {
IconButton(
onClick = { showMapStyleDialog() },
drawableRes = R.drawable.ic_twotone_layers_24,
contentDescription = R.string.map_style_selection,
)
IconButton(
onClick = {
if (context.hasLocationPermission()) toggleMyLocation()
else requestPermissionAndToggle()
},
enabled = hasGps,
drawableRes = if (myLocationOverlay == null) R.drawable.ic_twotone_my_location_24
else R.drawable.ic_twotone_location_disabled_24,
contentDescription = null,
modifier = Modifier.padding(top = 8.dp),
)
}
}
}
if (showEditWaypointDialog != null) {

View file

@ -1,44 +0,0 @@
package com.geeksville.mesh.ui.map.components
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import com.geeksville.mesh.R
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun MapStyleButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Button(
onClick = onClick,
modifier = modifier
.padding(16.dp)
.size(48.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary
),
) {
Icon(
painterResource(id = R.drawable.ic_twotone_layers_24),
stringResource(R.string.map_style_selection),
modifier = Modifier.scale(1.5f)
)
}
}
@Preview(showBackground = true)
@Composable
private fun MapStyleButtonPreview() {
MapStyleButton(onClick = {})
}