Merge pull request #470 from ScriptTactics/feature/osmand-migration

Feature/osmdroid migration
master
Andre K 2022-08-29 11:01:56 -03:00 zatwierdzone przez GitHub
commit 8b412961fa
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
40 zmienionych plików z 361 dodań i 994 usunięć

Wyświetl plik

@ -16,16 +16,6 @@ jobs:
with:
submodules: 'recursive'
- name: Load secrets
run: |
rm ./app/src/main/res/values/mapbox-token.xml
echo -e "<resources>\n <string name=\"mapbox_access_token\">$MAPBOX_ACCESS_TOKEN</string>\n</resources>" > ./app/src/main/res/values/mapbox-token.xml
mkdir -p ~/.gradle
echo "MAPBOX_DOWNLOADS_TOKEN=$MAPBOX_DOWNLOADS_TOKEN" >>~/.gradle/gradle.properties
env:
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
MAPBOX_DOWNLOADS_TOKEN: ${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
- name: Mock files for CI
run: |
rm ./app/google-services.json

Wyświetl plik

@ -29,8 +29,6 @@ jobs:
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
env:
GSERVICES: ${{ secrets.GSERVICES }}
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
MAPBOX_DOWNLOADS_TOKEN: ${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
KEYSTORE: ${{ secrets.KEYSTORE }}
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}

Wyświetl plik

@ -6,51 +6,50 @@
[![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/)
[![Vercel](https://img.shields.io/static/v1?label=Powered%20by&message=Vercel&style=flat&logo=vercel&color=000000)](https://vercel.com?utm_source=meshtastic&utm_campaign=oss)
This is a tool for using Android with open-source mesh radios. For more information see our webpage: [meshtastic.org](https://www.meshtastic.org). If you are looking for the the device side code, see [here](https://github.com/meshtastic/Meshtastic-esp32).
This is a tool for using Android with open-source mesh radios. For more information see our
webpage: [meshtastic.org](https://www.meshtastic.org). If you are looking for the the device side
code, see [here](https://github.com/meshtastic/Meshtastic-esp32).
This project is currently beta testing, if you have questions or feedback please [Join our discussion forum](https://meshtastic.discourse.group/). We would love to hear from you!
This project is currently beta testing, if you have questions or feedback
please [Join our discussion forum](https://meshtastic.discourse.group/). We would love to hear from
you!
The production version of our application is here:
[![Download at https://play.google.com/store/apps/details?id=com.geeksville.mesh](https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png)](https://play.google.com/store/apps/details?id=com.geeksville.mesh&referrer=utm_source%3Dgithub-android-readme)
To join the beta program for the app go to this [URL](https://play.google.com/apps/testing/com.geeksville.mesh) to opt-in to the alpha/beta test.
If you encounter any problems or have questions, [post in the forum](https://meshtastic.discourse.group/) and we'll help.
To join the beta program for the app go to
this [URL](https://play.google.com/apps/testing/com.geeksville.mesh) to opt-in to the alpha/beta
test. If you encounter any problems or have
questions, [post in the forum](https://meshtastic.discourse.group/) and we'll help.
The app is also distributed via F-Droid repo: [https://mesh.tastic.app/fdroid/repo](https://mesh.tastic.app/fdroid/repo)
The app is also distributed via F-Droid
repo: [https://mesh.tastic.app/fdroid/repo](https://mesh.tastic.app/fdroid/repo)
However, if you must use 'raw' APKs you can get them from our [github releases](https://github.com/meshtastic/Meshtastic-Android/releases). This is not recommended because if you manually install an APK it will not automatically update.
However, if you must use 'raw' APKs you can get them from
our [github releases](https://github.com/meshtastic/Meshtastic-Android/releases). This is not
recommended because if you manually install an APK it will not automatically update.
## Build instructions
If you would like to develop this application we'd love your help! These build instructions are brief
and should be improved, please send a PR if you can.
If you would like to develop this application we'd love your help! These build instructions are
brief and should be improved, please send a PR if you can.
- Use Android Studio to build/debug
- Use "git submodule update --init --recursive" to pull in the various submodules we depend on
- There are a few config files which you'll need to copy from templates included in the project.
Run the following commands to do so:
- There are a few config files which you'll need to copy from templates included in the project. Run
the following commands to do so:
```bash
rm ./app/google-services.json
cp ./app/google-services-example.json ./app/google-services.json
rm ./app/src/main/res/values/mapbox-token.xml
cp ./app/special/mapbox-token.xml ./app/src/main/res/values/
rm ./app/src/main/res/values/curfirmwareversion.xml
cp ./app/special/curfirmwareversion.xml ./app/src/main/res/values/
```
- (unfortunately) you need to get a (free) mapbox developer token [here](https://docs.mapbox.com/android/maps/guides/install/) and put that token in your user gradle.properties.
```bash
cat ~/.gradle/gradle.properties
MAPBOX_DOWNLOADS_TOKEN=sk.yourtokenherexxx
```
- (optional) to run CI tests on your fork: 1) allow GitHub Actions; 2) add your token at: Settings > Secrets > Actions > New repository secret: Name: MAPBOXTOKEN Value: sk.yourtokenherexxx
- Now you should be able to select "Run / Run" in the IDE and it will happily start running on your phone
or the emulator. Note: The emulators don't support bluetooth, so some features can not be used in
that environment.
- Now you should be able to select "Run / Run" in the IDE and it will happily start running on your
phone or the emulator. Note: The emulators don't support bluetooth, so some features can not be
used in that environment.
## Analytics setup

Wyświetl plik

@ -69,7 +69,6 @@ android {
// doesn't like and we need to strip them (gr)
resConfigs "cs", "de", "el", "en", "es", "fi", "fr", "ga", "ht", "it", "ja", "ko-rKR", "nl", "no", "pl", "pt", "pt-rBR", "ro", "ru", "sk", "sl", "sq", "sv", "tr", "zh"
// Needed to make mapbox work inside the firebase testlab - FIXME, alas, still doesn't work
ndk {
// abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
@ -88,7 +87,7 @@ android {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += [ '-opt-in=kotlin.RequiresOptIn' ]
freeCompilerArgs += ['-opt-in=kotlin.RequiresOptIn']
}
lint {
abortOnError false
@ -140,6 +139,9 @@ dependencies {
kapt "androidx.room:room-compiler:$room_version"
kapt "com.google.dagger:hilt-compiler:$hilt_version"
//OSMAND
implementation 'org.osmdroid:osmdroid-android:6.1.14'
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
@ -166,9 +168,6 @@ dependencies {
// implementation 'com.google.android.things:androidthings:1.0'
implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
// mapbox
implementation 'com.mapbox.maps:android:10.2.0'
// location services
implementation 'com.google.android.gms:play-services-location:19.0.1'
// For Google Sign-In (owner name accesss)

Wyświetl plik

@ -30,9 +30,6 @@
-keep class com.geeksville.mesh.**{*;}
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite { <fields>; }
# Recommended by mapbox to prevent native crashes
-dontwarn com.mapbox.**
-keep class com.mapbox.** { *; }
# for kotlinx.serialization
-keepattributes *Annotation*, InnerClasses

Wyświetl plik

@ -126,6 +126,7 @@ class MainActivity : BaseActivity(), Logging,
// const val REQUEST_ENABLE_BT = 10
const val DID_REQUEST_PERM = 11
const val RC_SIGN_IN = 12 // google signin completed
// const val SELECT_DEVICE_REQUEST_CODE = 13
const val CREATE_CSV_FILE = 14
}
@ -331,7 +332,7 @@ class MainActivity : BaseActivity(), Logging,
DID_REQUEST_PERM -> {
// If request is cancelled, the result arrays are empty.
if ((grantResults.isNotEmpty() &&
grantResults[0] == PackageManager.PERMISSION_GRANTED)
grantResults[0] == PackageManager.PERMISSION_GRANTED)
) {
// Permission is granted. Continue the action or workflow
// in your app.
@ -664,7 +665,13 @@ class MainActivity : BaseActivity(), Logging,
// model.setLocalConfig(LocalOnlyProtos.LocalConfig.parseFrom(service.deviceConfig))
model.setChannels(ChannelSet(AppOnlyProtos.ChannelSet.parseFrom(service.channels)))
model.setChannels(
ChannelSet(
AppOnlyProtos.ChannelSet.parseFrom(
service.channels
)
)
)
model.updateNodesFromDevice()
@ -1072,14 +1079,10 @@ class MainActivity : BaseActivity(), Logging,
chooseLangDialog()
return true
}
R.id.preferences_map_style -> {
chooseMapStyle()
return true
}
R.id.show_intro -> {
startActivity(Intent(this, AppIntroduction::class.java))
return true
}
}
R.id.preferences_quick_chat -> {
val fragmentManager: FragmentManager = supportFragmentManager
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
@ -1192,7 +1195,10 @@ class MainActivity : BaseActivity(), Logging,
val lang = prefs.getString("lang", "zz")
debug("Lang from prefs: $lang")
builder.setSingleChoiceItems(languageLabels, languageValues.indexOf(lang)) { dialog, which ->
builder.setSingleChoiceItems(
languageLabels,
languageValues.indexOf(lang)
) { dialog, which ->
val selectedLang = languageValues[which]
debug("Set lang pref to $selectedLang")
editor.putString("lang", selectedLang)
@ -1202,27 +1208,4 @@ class MainActivity : BaseActivity(), Logging,
val dialog = builder.create()
dialog.show()
}
private fun chooseMapStyle() {
/// Prepare dialog and its items
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(getString(R.string.preferences_map_style))
val mapStyles by lazy { resources.getStringArray(R.array.map_styles) }
/// Load preferences and its value
val prefs = UIViewModel.getPreferences(this)
val editor: SharedPreferences.Editor = prefs.edit()
val mapStyleId = prefs.getInt("map_style_id", 1)
debug("mapStyleId from prefs: $mapStyleId")
builder.setSingleChoiceItems(mapStyles, mapStyleId) { dialog, which ->
debug("Set mapStyleId pref to $which")
editor.putInt("map_style_id", which)
editor.apply()
dialog.dismiss()
}
val dialog = builder.create()
dialog.show()
}
}

Wyświetl plik

@ -0,0 +1,55 @@
package com.geeksville.mesh.model
import org.osmdroid.tileprovider.tilesource.ITileSource
import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.tileprovider.tilesource.TileSourcePolicy
import org.osmdroid.util.MapTileIndex
class CustomTileSource {
companion object {
val ESRI_IMAGERY = object : OnlineTileSourceBase(
"ESRI World Overview", 0, 18, 256, "", arrayOf(
"https://wayback.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/WMTS/1.0.0/default028mm/MapServer/tile/"
), "Esri, Maxar, Earthstar Geographics, and the GIS User Community" +
"URL\n" +
"View\n",
TileSourcePolicy(
2, TileSourcePolicy.FLAG_NO_BULK
or TileSourcePolicy.FLAG_NO_PREVENTIVE
or TileSourcePolicy.FLAG_USER_AGENT_MEANINGFUL
or TileSourcePolicy.FLAG_USER_AGENT_NORMALIZED
)
) {
override fun getTileURLString(pMapTileIndex: Long): String {
return baseUrl + (MapTileIndex.getZoom(pMapTileIndex)
.toString() + "/" + MapTileIndex.getY(pMapTileIndex)
+ "/" + MapTileIndex.getX(pMapTileIndex)
+ mImageFilenameEnding)
}
}
val MAPNIK: OnlineTileSourceBase = TileSourceFactory.MAPNIK
val USGS_TOPO: OnlineTileSourceBase = TileSourceFactory.USGS_TOPO
val USGS_SAT: OnlineTileSourceBase = TileSourceFactory.USGS_SAT
val DEFAULT_TILE_SOURCE: OnlineTileSourceBase = TileSourceFactory.DEFAULT_TILE_SOURCE
/**
* The order in this list must match that in the arrays.xml under map_styles
*/
val mTileSources: List<ITileSource> =
listOf(MAPNIK, USGS_TOPO, USGS_SAT, ESRI_IMAGERY)
fun getTileSource(aName: String): ITileSource {
for (tileSource: ITileSource in mTileSources) {
if (tileSource.name().equals(aName)) {
return tileSource;
}
}
throw IllegalArgumentException("No such tile source: $aName")
}
}
}

Wyświetl plik

@ -1,680 +1,251 @@
package com.geeksville.mesh.ui
import android.app.AlertDialog
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.mesh.BuildConfig
import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.R
import com.geeksville.mesh.databinding.MapNotAllowedBinding
import com.geeksville.mesh.databinding.MapViewBinding
import com.geeksville.mesh.model.CustomTileSource
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.util.formatAgo
import com.mapbox.bindgen.Value
import com.mapbox.common.*
import com.mapbox.geojson.*
import com.mapbox.maps.*
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.maps.extension.style.expressions.generated.Expression
import com.mapbox.maps.extension.style.layers.addLayer
import com.mapbox.maps.extension.style.layers.addPersistentLayer
import com.mapbox.maps.extension.style.layers.generated.LineLayer
import com.mapbox.maps.extension.style.layers.generated.SymbolLayer
import com.mapbox.maps.extension.style.layers.generated.lineLayer
import com.mapbox.maps.extension.style.layers.properties.generated.*
import com.mapbox.maps.extension.style.sources.addSource
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.mapbox.maps.extension.style.sources.generated.geoJsonSource
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.maps.plugin.animation.flyTo
import com.mapbox.maps.plugin.gestures.OnMapLongClickListener
import com.mapbox.maps.plugin.gestures.gestures
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import kotlin.math.cos
import kotlin.math.sin
import org.osmdroid.api.IMapController
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.ITileSource
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.CustomZoomButtonsController
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.CopyrightOverlay
import org.osmdroid.views.overlay.Marker
@AndroidEntryPoint
class MapFragment : ScreenFragment("Map"), Logging {
private val tileStore: TileStore by lazy {
TileStore.create().also {
// Set default access token for the created tile store instance
it.setOption(
TileStoreOptions.MAPBOX_ACCESS_TOKEN,
TileDataDomain.MAPS,
Value(getString(R.string.mapbox_access_token))
)
}
}
/**
* DEVELOPER OPTION TO ENABLE OFFLINE MAPS
* Set this variable to true to enable offline maps
*/
//___________________________________________________
private val offlineEnabled = false
//___________________________________________________
private val resourceOptions: ResourceOptions by lazy {
ResourceOptions.Builder().applyDefaultParams(requireContext()).tileStore(tileStore).build()
}
private val offlineManager: OfflineManager by lazy {
OfflineManager(resourceOptions)
}
private lateinit var binding: MapViewBinding
private lateinit var mapNotAllowedBinding: MapNotAllowedBinding
private var userStyleURI: String? = null
private lateinit var geoJsonSource: GeoJsonSource
private lateinit var lineLayer: LineLayer
private var point: Point? = null
private lateinit var map: MapView
private lateinit var mapController: IMapController
private lateinit var mPrefs: SharedPreferences
private val model: UIViewModel by activityViewModels()
private val nodeSourceId = "node-positions"
private val nodeLayerId = "node-layer"
private val labelLayerId = "label-layer"
private val markerImageId = "my-marker-image"
private val userPointImageId = "user-image"
private val boundingBoxId = "bounding-box-id"
private val lineLayerId = "line-layer-id"
private val defaultMinZoom = 1.5
private val nodeZoomLevel = 8.5
private val defaultZoomSpeed = 3000L
private val prefsName = "org.andnav.osm.prefs"
private val mapStyleId = "map_style_id"
private val uiPrefs = "ui-prefs"
private var nodePositions = listOf<Marker>()
private val nodeLayer = 1
private var stylePackCancelable: Cancelable? = null
private var tilePackCancelable: Cancelable? = null
private lateinit var squareRegion: Geometry
private val userTouchPositionId = "user-touch-position"
private val userTouchLayerId = "user-touch-layer"
private var nodePositions = GeoJsonSource(GeoJsonSource.Builder(nodeSourceId))
private var tileRegionDownloadSuccess = false
private var stylePackDownloadSuccess = false
private val userTouchPosition = GeoJsonSource(GeoJsonSource.Builder(userTouchPositionId))
private val nodeLayer = SymbolLayer(nodeLayerId, nodeSourceId)
.iconImage(markerImageId)
.iconAnchor(IconAnchor.BOTTOM)
.iconAllowOverlap(true)
private val userTouchLayer = SymbolLayer(userTouchLayerId, userTouchPositionId)
.iconImage(userPointImageId)
.iconAnchor(IconAnchor.BOTTOM)
private val labelLayer = SymbolLayer(labelLayerId, nodeSourceId)
.textField(Expression.get("name"))
.textSize(12.0)
.textColor(Color.RED)
.textAnchor(TextAnchor.TOP)
//.textVariableAnchor(TextAnchor.TOP) //TODO investigate need for variable anchor vs normal anchor
.textJustify(TextJustify.AUTO)
.textAllowOverlap(true)
private fun onNodesChanged(nodes: Collection<NodeInfo>) {
val nodesWithPosition = nodes.filter { it.validPosition != null }
/**
* Using the latest nodedb, generate geojson features
*/
fun getCurrentNodes(): FeatureCollection {
// Find all nodes with valid locations
val locations = nodesWithPosition.map { node ->
val p = node.position!!
debug("Showing on map: $node")
val f = Feature.fromGeometry(
Point.fromLngLat(
p.longitude,
p.latitude
)
)
node.user?.let {
f.addStringProperty("name", it.longName + " " + formatAgo(p.time))
}
f
}
return FeatureCollection.fromFeatures(locations)
}
nodePositions.featureCollection(getCurrentNodes())
}
private fun zoomToNodes(map: MapboxMap) {
val points: MutableList<Point> = mutableListOf()
val nodesWithPosition =
model.nodeDB.nodes.value?.values?.filter { it.validPosition != null }
if (nodesWithPosition != null && nodesWithPosition.isNotEmpty()) {
val unit = if (nodesWithPosition.size >= 2) {
// Multiple nodes, make them all fit on the map view
nodesWithPosition.forEach {
points.add(
Point.fromLngLat(
it.position!!.longitude,
it.position!!.latitude
)
)
}
map.cameraForCoordinates(points)
} else {
// Only one node, just zoom in on it
val it = nodesWithPosition[0].position!!
points.add(Point.fromLngLat(it.longitude, it.latitude))
map.cameraForCoordinates(points)
cameraOptions {
this.zoom(9.0)
this.center(points[0])
}
}
map.flyTo(
unit,
MapAnimationOptions.mapAnimationOptions { duration(1000) })
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// We can't allow mapbox if user doesn't want analytics
return if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
// Mapbox Access token
binding = MapViewBinding.inflate(inflater, container, false)
binding.root
} else {
mapNotAllowedBinding = MapNotAllowedBinding.inflate(inflater, container, false)
mapNotAllowedBinding.root
}
binding = MapViewBinding.inflate(inflater)
return binding.root
}
var mapView: MapView? = null
private fun removeOfflineRegions() {
// Remove the tile region with the tile region ID.
// Note this will not remove the downloaded tile packs, instead, it will just mark the tileset
// not a part of a tile region. The tiles still exists as a predictive cache in TileStore.
tileStore.removeTileRegion(TILE_REGION_ID)
// Set the disk quota to zero, so that tile regions are fully evicted
// when removed. The TileStore is also used when `ResourceOptions.isLoadTilePacksFromNetwork`
// is `true`, and also by the Navigation SDK.
// This removes the tiles that do not belong to any tile regions.
tileStore.setOption(TileStoreOptions.DISK_QUOTA, Value(0))
// Remove the style pack with the style url.
// Note this will not remove the downloaded style pack, instead, it will just mark the resources
// not a part of the existing style pack. The resources still exists as disk cache.
if (userStyleURI != null) {
offlineManager.removeStylePack(userStyleURI!!)
mapView?.getMapboxMap()?.loadStyleUri(loadMapStyleFromPref())
} else {
offlineManager.removeStylePack(mapView?.getMapboxMap()?.getStyle()?.styleURI.toString())
mapView?.getMapboxMap()?.loadStyleUri(loadMapStyleFromPref())
}
MapboxMap.clearData(resourceOptions) {
it.error?.let { error ->
debug(error)
}
}
updateStylePackDownloadProgress(0, 0)
updateTileRegionDownloadProgress(0, 0)
}
/**
* Mapbox native code can crash painfully if you ever call a mapbox view function while the view is not actively being show
*/
private val isViewVisible: Boolean
get() = mapView?.isVisible == true
override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) {
super.onViewCreated(viewIn, savedInstanceState)
Configuration.getInstance().userAgentValue =
BuildConfig.APPLICATION_ID // Required to get online tiles
map = viewIn.findViewById(R.id.map)
mPrefs = context!!.getSharedPreferences(prefsName, Context.MODE_PRIVATE)
// We might not have a real mapview if running with analytics
if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
// binding.fabStyleToggle.setOnClickListener {
// //TODO: Setup Style menu for satellite view, street view, & outdoor view
// }
binding.downloadRegion.setOnClickListener {
// Display menu for download region
this.downloadRegionDialogFragment()
}
val vIn = viewIn.findViewById<MapView>(R.id.mapView)
mapView = vIn
mapView?.let { v ->
// Each time the pane is shown start fetching new map info (we do this here instead of
// onCreate because getMapAsync can die in native code if the view goes away)
val map = v.getMapboxMap()
if (view != null) { // it might have gone away by now
val markerIcon =
ContextCompat.getDrawable(
requireActivity(),
R.drawable.ic_twotone_person_pin_24
)!!.toBitmap()
map.loadStyleUri(loadMapStyleFromPref()) {
if (it.isStyleLoaded) {
it.addSource(nodePositions)
it.addImage(markerImageId, markerIcon)
it.addPersistentLayer(nodeLayer)
it.addPersistentLayer(labelLayer)
}
}
v.gestures.rotateEnabled = false
if (offlineEnabled) {
v.gestures.addOnMapLongClickListener(this.longClick)
}
// Provide initial positions
model.nodeDB.nodes.value?.let { nodes ->
onNodesChanged(nodes.values)
}
setupMapProperties()
loadOnlineTileSourceBase()
map.let {
if (view != null) {
mapController = map.controller
binding.fabStyleToggle.setOnClickListener {
chooseMapStyle()
}
model.nodeDB.nodes.value?.let { nodes ->
onNodesChanged(nodes.values)
drawOverlays()
}
// Any times nodes change update our map
model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { nodes ->
if (isViewVisible)
onNodesChanged(nodes.values)
})
//viewAnnotationManager = v.viewAnnotationManager
zoomToNodes(map)
}
// Any times nodes change update our map
model.nodeDB.nodes.observe(viewLifecycleOwner) { nodes ->
onNodesChanged(nodes.values)
drawOverlays()
}
zoomToNodes(mapController)
}
}
private fun downloadOfflineRegion(styleURI: String = "") {
private fun chooseMapStyle() {
/// Prepare dialog and its items
val builder = MaterialAlertDialogBuilder(context!!)
builder.setTitle(getString(R.string.preferences_map_style))
val mapStyles by lazy { resources.getStringArray(R.array.map_styles) }
val style = styleURI.ifEmpty {
mapView?.getMapboxMap()
?.getStyle()?.styleURI.toString()
/// Load preferences and its value
val prefs = UIViewModel.getPreferences(context!!)
val editor: SharedPreferences.Editor = prefs.edit()
val mapStyleInt = prefs.getInt(mapStyleId, 1)
debug("mapStyleId from prefs: $mapStyleInt")
builder.setSingleChoiceItems(mapStyles, mapStyleInt) { dialog, which ->
debug("Set mapStyleId pref to $which")
editor.putInt(mapStyleId, which)
editor.apply()
dialog.dismiss()
map.setTileSource(loadOnlineTileSourceBase())
}
if (OfflineSwitch.getInstance().isMapboxStackConnected) {
// By default, users may download up to 250MB of data for offline use without incurring
// additional charges. This limit is subject to change during the beta.
// - - - - - - - -
// 1. Create style package with loadStylePack() call.
// A style pack (a Style offline package) contains the loaded style and its resources: loaded
// sources, fonts, sprites. Style packs are identified with their style URI.
// Style packs are stored in the disk cache database, but their resources are not subject to
// the data eviction algorithm and are not considered when calculating the disk cache size.
binding.stylePackDownloadProgress.visibility = View.VISIBLE
binding.stylePackText.visibility = View.VISIBLE
stylePackCancelable = offlineManager.loadStylePack(
style,
// Build Style pack load options
StylePackLoadOptions.Builder()
.glyphsRasterizationMode(GlyphsRasterizationMode.IDEOGRAPHS_RASTERIZED_LOCALLY)
.metadata(Value(STYLE_PACK_METADATA))
.build(),
{ progress ->
updateStylePackDownloadProgress(
progress.completedResourceCount,
progress.requiredResourceCount,
)
},
{ expected ->
if (expected.isValue) {
expected.value?.let { stylePack ->
// Style pack download finishes successfully
debug("StylePack downloaded: $stylePack")
if (binding.stylePackDownloadProgress.progress == binding.stylePackDownloadProgress.max) {
debug("Style pack download complete")
binding.stylePackText.visibility = View.INVISIBLE
binding.stylePackDownloadProgress.visibility = View.INVISIBLE
stylePackDownloadSuccess = true
} else {
debug("Waiting for tile region download to be finished.")
}
}
}
expected.error?.let {
stylePackDownloadSuccess = false
// Handle error occurred during the style pack download.
binding.stylePackText.visibility = View.INVISIBLE
binding.stylePackDownloadProgress.visibility = View.INVISIBLE
debug("StylePackError: $it")
}
}
)
// - - - - - - - -
// 2. Create a tile region with tiles for the outdoors style
// A Tile Region represents an identifiable geographic tile region with metadata, consisting of
// a set of tiles packs that cover a given area (a polygon). Tile Regions allow caching tiles
// packs in an explicit way: By creating a Tile Region, developers can ensure that all tiles in
// that region will be downloaded and remain cached until explicitly deleted.
// Creating a Tile Region requires supplying a description of the area geometry, the tilesets
// and zoom ranges of the tiles within the region.
// The tileset descriptor encapsulates the tile-specific data, such as which tilesets, zoom ranges,
// pixel ratio etc. the cached tile packs should have. It is passed to the Tile Store along with
// the region area geometry to load a new Tile Region.
// The OfflineManager is responsible for creating tileset descriptors for the given style and zoom range.
val tilesetDescriptor = offlineManager.createTilesetDescriptor(
TilesetDescriptorOptions.Builder()
.styleURI(style)
.minZoom(0)
.maxZoom(10)
.build()
)
// Use the the default TileStore to load this region. You can create custom TileStores are are
// unique for a particular file path, i.e. there is only ever one TileStore per unique path.
// Note that the TileStore path must be the same with the TileStore used when initialise the MapView.
binding.tilePackText.visibility = View.VISIBLE
binding.tilePackDownloadProgress.visibility = View.VISIBLE
tilePackCancelable = tileStore.loadTileRegion(
TILE_REGION_ID, // Make this dynamic
TileRegionLoadOptions.Builder()
.geometry(squareRegion)
.descriptors(listOf(tilesetDescriptor))
.metadata(Value(TILE_REGION_METADATA))
.acceptExpired(true)
.networkRestriction(NetworkRestriction.NONE)
.build(),
{ progress ->
updateTileRegionDownloadProgress(
progress.completedResourceCount,
progress.requiredResourceCount,
)
}
) { expected ->
if (expected.isValue) {
// Tile pack download finishes successfully
expected.value?.let { region ->
debug("TileRegion downloaded: $region")
if (binding.tilePackDownloadProgress.progress == binding.tilePackDownloadProgress.max) {
debug("Finished tilepack download")
binding.tilePackDownloadProgress.visibility = View.INVISIBLE
binding.tilePackText.visibility = View.INVISIBLE
tileRegionDownloadSuccess = true
} else {
debug("Waiting for style pack download to be finished.")
}
}
}
expected.error?.let {
tileRegionDownloadSuccess = false
// Handle error occurred during the tile region download.
binding.tilePackDownloadProgress.visibility = View.INVISIBLE
binding.tilePackText.visibility = View.INVISIBLE
debug("TileRegionError: $it")
}
}
} else {
Toast.makeText(
requireContext(),
R.string.download_region_connection_alert,
Toast.LENGTH_LONG
).show()
}
}
/**
* OnLongClick of the map set a position marker.
* If a user long-clicks again, the position of the first marker will be updated
*/
private val longClick = OnMapLongClickListener {
val userDefinedPointImg =
ContextCompat.getDrawable(
requireActivity(),
R.drawable.baseline_location_on_white_24dp
)!!
.toBitmap()
point = Point.fromLngLat(it.longitude(), it.latitude())
/*
Calculate region from user specified position.
5 miles NE,NW,SE,SW from user center point.
25 Sq Mile Region
*/
//____________________________________________________________________________________________
val topRight = calculateCoordinate(45.0, point?.latitude()!!, point?.longitude()!!)
val topLeft = calculateCoordinate(135.0, point?.latitude()!!, point?.longitude()!!)
val bottomLeft = calculateCoordinate(225.0, point?.latitude()!!, point?.longitude()!!)
val bottomRight = calculateCoordinate(315.0, point?.latitude()!!, point?.longitude()!!)
//____________________________________________________________________________________________
val pointList = listOf(topRight, topLeft, bottomLeft, bottomRight, topRight)
squareRegion = LineString.fromLngLats(pointList)
geoJsonSource = geoJsonSource(boundingBoxId) {
geometry(squareRegion)
}
lineLayer = lineLayer(lineLayerId, boundingBoxId) {
lineCap(LineCap.ROUND)
lineJoin(LineJoin.MITER)
lineOpacity(0.7)
lineWidth(1.5)
lineColor("#888")
}
if (point != null) {
binding.downloadRegion.visibility = View.VISIBLE
}
mapView?.getMapboxMap()?.getStyle()?.let { style ->
userTouchPosition.geometry(point!!)
if (!style.styleLayerExists(userTouchLayerId)) {
style.addImage(userPointImageId, userDefinedPointImg)
style.addSource(userTouchPosition)
style.addSource(geoJsonSource)
style.addPersistentLayer(lineLayer)
style.addLayer(userTouchLayer)
} else {
style.removeStyleLayer(lineLayerId)
style.removeStyleSource(boundingBoxId)
style.addSource(geoJsonSource)
style.addLayer(lineLayer)
}
}
mapView?.getMapboxMap().also { mapboxMap ->
mapboxMap?.flyTo(
CameraOptions.Builder()
.zoom(ZOOM)
.center(point)
.build(), MapAnimationOptions.mapAnimationOptions { duration(1000) })
}
return@OnMapLongClickListener true
}
/**
* Find's coordinates (Lat,Lon) a specified distance from given (lat,lon) using degrees to determine direction
* @param degrees degree of desired position from current position. (center point is 0,0 and desired point, top right corner, is 45 degrees from 0,0)
* @param lat latitude position (current position lat)
* @param lon longitude position (current position lon)
* @return Point
*/
private fun calculateCoordinate(degrees: Double, lat: Double, lon: Double): Point {
val deg = Math.toRadians(degrees)
val distancesInMeters =
1609.344 * 2.5 // 1609.344 is 1 mile in meters -> multiplier will be user specified up to a max of 10
val radiusOfEarthInMeters = 6378137
val x =
lon + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * cos(
deg
)
val y =
lat + (180 / Math.PI) * (distancesInMeters / radiusOfEarthInMeters) * sin(deg)
return Point.fromLngLat(x, y)
}
private fun updateStylePackDownloadProgress(
progress: Long,
max: Long,
) {
binding.stylePackDownloadProgress.max = max.toInt()
binding.stylePackDownloadProgress.progress = progress.toInt()
}
private fun updateTileRegionDownloadProgress(
progress: Long,
max: Long,
) {
binding.tilePackDownloadProgress.max = max.toInt()
binding.tilePackDownloadProgress.progress = progress.toInt()
}
companion object {
private const val ZOOM = 12.5
private const val TILE_REGION_ID = "tile-region"
private const val STYLE_PACK_METADATA = "outdoor-style-pack"
private const val TILE_REGION_METADATA = "outdoor-tile-region"
}
private fun downloadRegionDialogFragment() {
val mapDownloadView = layoutInflater.inflate(R.layout.dialog_map_download, null)
val uri = mapDownloadView.findViewById<EditText>(R.id.uri)
val downloadRegionDialogFragment = AlertDialog.Builder(context)
downloadRegionDialogFragment.setView(mapDownloadView)
.setTitle(R.string.download_region_dialog_title)
.setMultiChoiceItems(
R.array.MapMenuCheckbox,
null,
) { _, _, isChecked ->
if (isChecked) {
if (!uri.isVisible) {
uri.visibility =
View.VISIBLE
}
} else {
if (uri.isVisible) {
uri.visibility =
View.GONE
}
}
}
.setPositiveButton(
R.string.save_btn, null
)
.setNeutralButton(R.string.view_region_btn) { _, _ ->
if (tileRegionDownloadSuccess && stylePackDownloadSuccess) {
mapView?.getMapboxMap().also {
it?.flyTo(
CameraOptions.Builder()
.zoom(ZOOM)
.center(point)
.build(),
MapAnimationOptions.mapAnimationOptions { duration(1000) })
if (userStyleURI != null) {
it?.loadStyleUri(userStyleURI.toString())
} else {
it?.getStyle().also { style ->
style?.removeStyleImage(userPointImageId)
}
}
}
} else {
Toast.makeText(
requireContext(),
R.string.no_download_region_alert,
Toast.LENGTH_SHORT
).show()
}
}
.setNegativeButton(
R.string.cancel
) { dialog, _ ->
mapView?.getMapboxMap()?.getStyle { style ->
point = null
userStyleURI = null
style.removeStyleLayer(lineLayerId)
style.removeStyleSource(boundingBoxId)
style.removeStyleLayer(userTouchLayerId)
style.removeStyleSource(userTouchPositionId)
style.removeStyleImage(userPointImageId)
}
binding.downloadRegion.visibility = View.INVISIBLE
removeOfflineRegions() //TODO: Add to offline manager window
dialog.cancel()
}
val dialog = downloadRegionDialogFragment.create()
val dialog = builder.create()
dialog.show()
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (uri.isVisible) {
if (uri.text.isNotEmpty()) {
// Save URI
userStyleURI = uri.text.toString()
uri.setText("") // clear text
}
downloadOfflineRegion(userStyleURI!!)
dialog.dismiss()
} else {
Toast.makeText(
requireContext(),
R.string.style_uri_empty_alert,
Toast.LENGTH_SHORT
).show()
private fun onNodesChanged(nodes: Collection<NodeInfo>) {
val nodesWithPosition = nodes.filter { it.validPosition != null }
/**
* Using the latest nodedb, generate GeoPoint
*/
// Find all nodes with valid locations
fun getCurrentNodes(): List<Marker> {
val mrkr = nodesWithPosition.map { node ->
val p = node.position!!
debug("Showing on map: $node")
val f = GeoPoint(p.latitude, p.longitude)
lateinit var marker: MarkerWithLabel
node.user?.let {
val label = it.longName + " " + formatAgo(p.time)
marker = MarkerWithLabel(map, label)
marker.title = label
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
marker.position = f
marker.icon = ContextCompat.getDrawable(
requireActivity(),
R.drawable.ic_twotone_person_pin_24
)
}
marker
}
return mrkr
}
nodePositions = getCurrentNodes()
}
private fun drawOverlays() {
map.overlayManager.overlays().clear()
addCopyright() // Copyright is required for certain map sources
map.overlayManager.addAll(nodeLayer, nodePositions)
}
/**
* Adds copyright to map depending on what source is showing
*/
private fun addCopyright() {
val copyrightNotice: String =
map.tileProvider.tileSource.copyrightNotice
val copyrightOverlay = CopyrightOverlay(context)
copyrightOverlay.setCopyrightNotice(copyrightNotice)
map.overlays.add(copyrightOverlay)
}
private fun setupMapProperties() {
if (this::map.isInitialized) {
map.setDestroyMode(false) // keeps map instance alive when in the background.
map.isVerticalMapRepetitionEnabled = false // disables map repetition
map.setScrollableAreaLimitLatitude(
map.overlayManager.tilesOverlay.bounds.actualNorth,
map.overlayManager.tilesOverlay.bounds.actualSouth,
0
) // bounds scrollable map
map.isTilesScaledToDpi =
true // scales the map tiles to the display density of the screen
map.minZoomLevel =
defaultMinZoom // sets the minimum zoom level (the furthest out you can zoom)
map.setMultiTouchControls(true) // Sets gesture controls to true.
map.zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) // Disables default +/- button for zooming
}
}
private fun zoomToNodes(controller: IMapController) {
val points: MutableList<GeoPoint> = mutableListOf()
val nodesWithPosition =
model.nodeDB.nodes.value?.values?.filter { it.validPosition != null }
if ((nodesWithPosition != null) && nodesWithPosition.isNotEmpty()) {
if (nodesWithPosition.size >= 2) {
// Multiple nodes, make them all fit on the map view
nodesWithPosition.forEach {
points.add(
GeoPoint(
it.position!!.latitude,
it.position!!.longitude
)
)
}
val box = BoundingBox.fromGeoPoints(points)
val point = GeoPoint(box.centerLatitude, box.centerLongitude)
controller.animateTo(point, nodeZoomLevel, defaultZoomSpeed)
} else {
downloadOfflineRegion()
dialog.dismiss()
// Only one node, just zoom in on it
val it = nodesWithPosition[0].position!!
points.add(GeoPoint(it.latitude, it.longitude))
controller.animateTo(points[0], nodeZoomLevel, defaultZoomSpeed)
}
}
}
private fun loadMapStyleFromPref():String {
val prefs = context?.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
val mapStyleId = prefs?.getInt("map_style_id", 1)
debug("mapStyleId from prefs: $mapStyleId")
val mapStyle = when (mapStyleId) {
0 -> Style.MAPBOX_STREETS
1 -> Style.OUTDOORS
2 -> Style.LIGHT
3 -> Style.DARK
4 -> Style.SATELLITE
5 -> Style.SATELLITE_STREETS
6 -> Style.TRAFFIC_DAY
7 -> Style.TRAFFIC_NIGHT
else -> Style.OUTDOORS
}
return mapStyle
private fun loadOnlineTileSourceBase(): ITileSource {
val prefs = context?.getSharedPreferences(uiPrefs, Context.MODE_PRIVATE)
val mapSourceId = prefs?.getInt(mapStyleId, 1)
debug("mapStyleId from prefs: $mapSourceId")
return CustomTileSource.mTileSources[mapSourceId!!]
}
override fun onPause() {
map.onPause()
super.onPause()
}
override fun onResume() {
super.onResume()
map.onResume()
}
override fun onDestroy() {
super.onDestroyView()
map.onDetach()
}
private inner class MarkerWithLabel(mapView: MapView?, label: String) : Marker(mapView) {
val mLabel = label
override fun draw(c: Canvas, osmv: MapView?, shadow: Boolean) {
draw(c, osmv)
}
fun draw(c: Canvas, osmv: MapView?) {
super.draw(c, osmv, false)
val p = mPositionPixels
val textPaint = Paint()
textPaint.textSize = 50f
textPaint.color = Color.RED
textPaint.isAntiAlias = true
textPaint.textAlign = Paint.Align.CENTER
c.drawText(mLabel, (p.x - 0).toFloat(), (p.y - 60).toFloat(), textPaint)
}
}
}

Wyświetl plik

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
</vector>

Wyświetl plik

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#FFFFFF" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,2C8.13,2 5,5.13 5,9c0,5.25 7,13 7,13s7,-7.75 7,-13c0,-3.87 -3.13,-7 -7,-7zM12,11.5c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5z"/>
</vector>

Wyświetl plik

@ -1,19 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/uri"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginTop="16dp"
android:layout_marginRight="4dp"
android:layout_marginBottom="4dp"
android:hint="URI"
android:inputType="textUri"
android:visibility="gone" />
</LinearLayout>

Wyświetl plik

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/alert_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="2dp"
android:paddingTop="2dp"
android:textSize="12sp" />
</FrameLayout>

Wyświetl plik

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:autoLink="web"
android:text="@string/map_not_allowed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -5,93 +5,22 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mapbox.maps.MapView
android:id="@+id/mapView"
<org.osmdroid.views.MapView
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mapbox_cameraZoom="0" />
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="@+id/mapView">
<TextView
android:id="@+id/style_pack_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Style Pack"
android:textColor="@color/colorPrimaryDark"
android:visibility="invisible" />
<ProgressBar
android:id="@+id/style_pack_download_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="8"
android:clickable="false"
android:indeterminate="false"
android:max="100"
android:progressTint="@color/colorPrimary"
android:visibility="invisible" />
<TextView
android:id="@+id/tile_pack_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="Tile Pack"
android:textColor="@color/colorPrimaryDark"
android:visibility="invisible" />
<ProgressBar
android:id="@+id/tile_pack_download_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="8"
android:clickable="false"
android:indeterminate="false"
android:max="100"
android:progressTint="@color/colorPrimary"
android:visibility="invisible" />
</LinearLayout>
<LinearLayout
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_style_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:backgroundTint="@color/design_default_color_secondary"
android:contentDescription="@string/style_selection"
android:orientation="vertical"
android:src="@drawable/baseline_layers_white_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<!-- <com.google.android.material.floatingactionbutton.FloatingActionButton-->
<!-- android:id="@+id/fab_style_toggle"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_margin="8dp"-->
<!-- android:backgroundTint="@color/buttonColor"-->
<!-- android:contentDescription="@string/style_selection"-->
<!-- android:src="@drawadownload_regionble/baseline_layers_white_24dp"-->
<!-- tools:background="@color/buttonColor" />-->
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/download_region"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:backgroundTint="@color/buttonColor"
android:contentDescription="@string/download_region"
android:src="@drawable/baseline_download_white_24dp"
android:visibility="invisible"
tools:background="@color/buttonColor" />
</LinearLayout>
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
tools:background="@color/design_default_color_secondary" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="60dp"
android:background="@android:color/white"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/annotation"
android:text="@string/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"
android:padding="3dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Wyświetl plik

@ -32,10 +32,6 @@
android:id="@+id/preferences_language"
android:title="@string/preferences_language"
app:showAsAction="withText" />
<item
android:id="@+id/preferences_map_style"
android:title="@string/preferences_map_style"
app:showAsAction="withText" />
<item
android:id="@+id/show_intro"
android:title="@string/show_intro"

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Jste si jistý, že chcete změnit kanál? Veškerá komunikace s ostatními vysílači přestane fungovat až do momentu distribuce stejného nastavení na ostatní vysílače.</string>
<string name="new_channel_rcvd">Nová URL kanálu přijata.</string>
<string name="do_you_want_switch">Chcete se připojit ke kanálu \'%s\' ?</string>
<string name="map_not_allowed">You have analytics disabled. Unfortunately our map provider (mapbox) requires analytics to be allowed for their \'free\' plan. So we have turned off the map view.\n\n
If you would like to see the map, you\'ll need to turn on analytics in the Settings pane (also, for the time being you might need to force restart the application).\n\n
If you are interested in us paying for mapbox (or switching to a different map provider), please post in meshtastic.discourse.group</string>
<string name="permission_missing">Chybí požadovaná oprávnění, Meshtastic nebude fungovat správně. Prosím změntě oprávnění pro aplikaci.</string>
<string name="radio_sleeping">Vysílač je uspaný, nepodařilo se změnit kanál.</string>
<string name="report_bug">Nahlášení chyby</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Είστε βέβαιοι ότι θέλετε να αλλάξετε κανάλι? Η επικοινωνία με άλλες συσκευές θα σταματήσεις μέχρι να μοιραστείτε τις ρυθμίσεις του νέου καναλιού.</string>
<string name="new_channel_rcvd">Λήψη URL νέου καναλιού</string>
<string name="do_you_want_switch">Θέλετε να αλλάξετε %s κανάλι?</string>
<string name="map_not_allowed">Έχετε απενεργοποιήσει την ανάλυση δεδομένων. Δυστυχώς ο πάροχος χαρτών μας (mapbox) απαιτεί η ανάλυση δεδομένων να επιτρέπεται στο ‘δωρεάν’ πακέτο. Επομένως έχουμε απενεργοποιήσει το χάρτη.\n\n
Αν επιθυμείτε να δείτε το χάρτη, θα πρέπει να ενεργοποιήσετε την ανάλυση δεδομένων στις Ρυθμίσεις (επίσης - προσωρινά - θα πρέπει να επανεκκινήσετε την εφαρμογή).\n\n
Αν θεωρείτε ότι πρέπει να πληρώνουμε το mapbox (η να αλλάξουμε πάροχο χαρτών), παρακαλώ δημοσιεύστε στο meshtastic.discourse.group</string>
<string name="permission_missing">Λείπει μια απαιτούμενη άδεια, Meshtastic δεν θα λειτοργεί σωστά. Ενεργοποιήστε τις ρυθμίσεις εφαρμογής Android.</string>
<string name="radio_sleeping">Radio σε κατάσταση ύπνου, δεν γίνεται αλλαγή καναλιού</string>
<string name="report_bug">Αναφορά Bug</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Vous êtes sur de vouloir changer de canal? Toute communication aux autres nœuds va s\'arrêter jusqu\'à ce que vous partagez les nouveaux réglages.</string>
<string name="new_channel_rcvd">Nouveau URL canal reçu</string>
<string name="do_you_want_switch">Voulez-vous changer vers canal \'%s\' ?</string>
<string name="map_not_allowed">Vous avez désactivé les analytiques. Malheureusement notre fournisseur de carte (mapbox) a besoin d\'analytiques activés pour le plan \'gratuit\'. Désormais nous avons désactivé l\'apercu carte.\n\n
Si vous désirez voir la carte, vous avez besoin d\'activer les analytiques dans les Réglages (également, provisoirement, il peut être nécessaire de redémarrer l\'application).\n\n
Si vous trouvez que nous devons payer pour mapbox (ou changer de fournisseur de carte), veuillez poster dans meshtastic.discourse.group</string>
<string name="permission_missing">Une permission indispensable manque, Meshtastic ne peut pas fonctionner. Veuillez modifier dans Réglages.</string>
<string name="radio_sleeping">Radio en veille, change de Canal impossible</string>
<string name="report_bug">Rapporter Bogue</string>

Wyświetl plik

@ -28,10 +28,6 @@
<string name="are_you_sure_channel">An bhfuil tú cinnte gur mhaith leat an cainéal a athrú? Stopfaidh gach cumarsáid le nóid eile go dtí go roinnfidh tú na socruithe nua cainéil.</string>
<string name="new_channel_rcvd">URL Cainéal nua faighte</string>
<string name="do_you_want_switch">Ar mhaith leat aistriú go dtí an cainéal \'%s\'?</string>
<string name="map_not_allowed">Tá anailísíocht díchumasaithe agat. Ar an drochuair, éilíonn ár soláthraí léarscáileanna (bosca léarscáileanna) go gceadófar anailísíocht dá phlean \'saor in aisce\'.
Mar sin, táimid tar éis an radharc léarscáile a mhúchadh. Más mian leat an léarscáil a fheiceáil, beidh ort anailísíocht a chur ar siúl sa phána Socruithe (freisin, faoi
láthair, bfhéidir go mbeidh ort iallach a chur ar an bhfeidhmchlár atosú). Má tá suim agat muid a íoc as bosca léarscáileanna (nó aistriú chuig soláthraí léarscáileanna eile),
abair é le do thoil i meshtastic.discourse.group.</string>
<string name="permission_missing">Tá cead riachtanach ar iarraidh, ní bheidh Meshtastic in ann oibriú i gceart. Cumasaigh i socruithe feidhmchláir le do thoil.</string>
<string name="radio_sleeping">Bhí an raidió ina chodladh, ní raibh sé in ann an cainéal a athrú</string>
<string name="report_bug">Tuairiscigh fabht</string>

Wyświetl plik

@ -28,7 +28,6 @@
<string name="are_you_sure_channel">Èske ou sèten ke ou vle chanje chènn nan? Tout kominikasyon ak lòt ne elektwonik yo ap kanpe jiskaske ou pataje nouvo paramèt chènn yo.</string>
<string name="new_channel_rcvd">Te resevwa nouvo Chènn URL</string>
<string name="do_you_want_switch">Eke ou vle chanje a chènn \'%s\'?</string>
<string name="map_not_allowed">Ou gen analitik ki enfim. Malerezman founisè kat jeyografik nou an (mapbox) mande pou analitik yo pèmèt plan gratis. Se konsa, nou te etènn kat la. Si ou ta renmen wè kat jeyografik la ou pral bezwen ouvri analytics nan Paramèt (si ou enterese pou nou peye pou mapbox (oswa pou nou chanje founisè kat jeyografik la) tanpri poste nan meshtastic.discourse.group</string>
<string name="permission_missing">Manke pèmisyon obligatwa, Meshtastic pap ka fonksyone byen. Tanpri ale bay pèmisyon an nan paramèt aplikasyon android.</string>
<string name="radio_sleeping">Radyo t\'ap dòmi, pat ka chanje chènn</string>
<string name="report_bug">Report Bug</string>

Wyświetl plik

@ -27,9 +27,6 @@
<string name="are_you_sure_channel">Biztosan csatornát akar váltani? Minden kommunikáció a többi állomással megszakad amíg nem osztja meg velük az új csatorna beállításokat.</string>
<string name="new_channel_rcvd">Új csatorna URL érkezett</string>
<string name="do_you_want_switch">Átkapcsol a(z) \'%s\' csatornára?</string>
<string name="map_not_allowed">Az analitikák küldése ki van kapcsolva. Sajnos a térkép szolgáltatónk (mapbox) megköveteli az analitikák küldését számukra az \'ingyenes\' előfizetéshez. Ezért a térkép nézetet ki kellett kapcsolnunk.\n\n
Ha szeretné látni a térképet, ahhoz be kell kapcsolja az analitikák küldését a program beállítások oldalán (és lehet, hogy az alkalmazást is újra kell indítani hozzá).\n\n
Ha érdeklődne az általunk történő fizetéses megoldás iránt a mapbox felé (vagy egy másik térkép szolgáltatóra váltásról), kérem írjon bejegyzést a meshtastic.discourse.group fórumba.</string>
<string name="permission_missing">Egy szükséges engedély hiányzik, ezért a Meshtastic nem fog tudni rendesen működni. Kérem engedélyezze az Android alkalmazások beállításai között.</string>
<string name="radio_sleeping">A rádió alvó üzemmódban volt, ezért nem lehetett csatornát váltani.</string>
<string name="report_bug">Hiba jelentése</string>

Wyświetl plik

@ -28,10 +28,6 @@
<string name="are_you_sure_channel">Sei sicuro di voler cambiare canale ? Tutte le comunicazioni con gli altri nodi termineranno fino a quando non condividi le impostazioni del nuovo canale.</string>
<string name="new_channel_rcvd">Nuovo Canale URL ricevuto</string>
<string name="do_you_want_switch">Vuoi passare al canale \'%s\' ?</string>
<string name="map_not_allowed">YL\'analisi è disabilitata. Sfortunatamente il nostro fornitore di mappe (mapbox) richiede che le analisi siano consentite per il loro piano \'gratuito \'.
Quindi abbiamo disattivato la visualizzazione della mappa. \n\n
Se desideri visualizzare la mappa, dovrai attivare l\'analisi nel riquadro Impostazioni (inoltre, per il momento potresti dover forzare il riavvio dell\'applicazione). \n\n
Se sei interessato a pagare per mapbox (o passare ad un altro fornitore di mappe), scrivi in meshtastic.discourse.group</string>
<string name="permission_missing">Manca un\'autorizzazione richiesta, Meshtastic non sarà in grado di funzionare correttamente. Si prega di abilitarlo nelle impostazioni dell\'applicazione.</string>
<string name="radio_sleeping">La radio dormiva, non poteva cambiare canale.</string>
<string name="report_bug">Segnala bug</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">チャンネルを変更しますか?新しいチャンネル設定をシェアするまで他のノードとの通信はすべて停止します。</string>
<string name="new_channel_rcvd">新しいチャンネルURLを受信しました</string>
<string name="do_you_want_switch">\'%s\チャンネルに変更しますか?</string>
<string name="map_not_allowed">解析が無効になっています。残念ながら地図プロバイダmapboxは無料プランの場合、解析を有効にする必要があります。マップビューをオフにしました。\n\n
マップを表示したい場合は設定パネルで匿名の診断情報と不具合報告にチェックする必要があります(また当分の間アプリを強制停止して再起動する必要があります)。\n\n
mapboxの有償プランまたは代替地図プロバイダを検討される方は meshtastic.discourse.group に書き込んでください。</string>
<string name="permission_missing">必要なアクセス権限が拒否されているため、アプリが正常に動作しません。設定により権限を許可してください。</string>
<string name="radio_sleeping">Meshtasticデバイスはスリープ状態です。チャンネルを変更できませんでした。</string>
<string name="report_bug">バグ報告</string>

Wyświetl plik

@ -28,7 +28,6 @@
<string name="are_you_sure_channel">채널 변경을 원하세요? 채널 설정이 공유되기 전까지 다른 노드와의 통신은 중단됩니다.</string>
<string name="new_channel_rcvd">새로운 채널 URL 수신</string>
<string name="do_you_want_switch">\'%s\' 채널로 변경할 까요?</string>
<string name="map_not_allowed">분석이 비활성화 되었습니다. 현재 앱에서 사용하는 지도 제공자(mapbox)의 \'무료\' 계정을 사용하기 위해서는 분석을 활성화 해야합니다. 따라서 기본적으로 지도를 비활성화 시켰습니다. 만약 지도를 보길 원한다면 설정 창에서 분석 설정을 켜야합니다(또한, 앱을 강제 재부팅해야 할 수도 있습니다). 만약 mapbox 사용에 대해서 도움주길 원하거나 다른 지도 제공자로 바꾸길 원한다면 포럼에 글을 게시해주세요.</string>
<string name="permission_missing">필요로 하는 권한을 얻지 못했습니다. Meshtastic는 정상적으로 작동하지 않을 수도 있습니다. 스마트폰 설정에서 권한을 설정해주세요.</string>
<string name="radio_sleeping">메쉬태스틱기기가 절전모드 중 이므로 채널을 변경할 수 없습니다.</string>
<string name="report_bug">버그 보고</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Ben je zeker van kanaal te willen veranderen? Alle communicatie met andere nodes wordt gestopt tot je de nieuwe instellingen deelt.</string>
<string name="new_channel_rcvd">Nieuw kanaal URL ontvangen</string>
<string name="do_you_want_switch">Wil je veranderen naar kanaal \'%s\' ?</string>
<string name="map_not_allowed">Je hebt analytics uitgeschakeld. Spijtig want onze kaartprovider (mapbox) vereist ingeschakelde analytics voor het \'gratis\' plan. Daarom hebben we de kaartmodus uitgeschakeld.\n\n
Indien je de kaart wil zien, is het nodig analytics aan te zetten in de Instellingen (ook is het mogelijk, voorlopig, dat een herstart van de applicatie nodig is).\n\n
Indien je het nodig acht dat we betalen voor mapbox (of veranderen naar een andere kaartprovider), post dan een berichtje in de meshtastic.discourse.group</string>
<string name="permission_missing">Een vereiste toelating ontbreekt, Meshtastic kan niet goed werken. Graag aanzetten in Instellingen.</string>
<string name="radio_sleeping">Radio was in slaapmodus, kon het kanaal niet veranderen</string>
<string name="report_bug">Rapporteer Bug</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Er du sikker på at du vil endre kanalen? All kommunikasjon med andre noder vil stanse, intill du deler de nye kanalinstillingene.</string>
<string name="new_channel_rcvd">Ny kanal URL mottatt</string>
<string name="do_you_want_switch">Vil du bytte til \'%s\' kanal?</string>
<string name="map_not_allowed">Du har slått av analytics. Desverre krever kartleverandøren vår (mapbox) at analytics er slått på for deres \'grtis\' plan. Så vi har slått av kartvisning.\n\n
Hvis du vil se kartet, må du slå på analytics i instillingspanelet (også, for øyeblikket, må du kanskje tvangsstoppe og restarte applikasjonen).\n\n
Hvis du er interessert i at vi betaler for mapbox(eller bytter kartleveradøt), vennligst post i meshtastic.discourse.group</string>
<string name="permission_missing">En påkrevet tilgang mangler, Meshtastic vil ikke fungere korrekt. Vennligst slå på i Android appliksjonsinstillinger.</string>
<string name="radio_sleeping">Radio sov.kunne ikke endre kanal</string>
<string name="report_bug">Rapporter Feil</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Are you sure you want to change the channel? All communication with other nodes will stop until you share the new channel settings.</string>
<string name="new_channel_rcvd">Odebrano nowy URL kanału</string>
<string name="do_you_want_switch">Chcesz przełączyć na \'%s\' kanał?</string>
<string name="map_not_allowed">Masz wyłączone analizy. Niestety nasz dostawca map (mapbox) wymaga, aby analizy były dozwolone w ich planie \'free\'Dlatego wyłączyliśmy widok mapy.\n\n
Jeśli chcesz zobaczyć mapę, musisz włączyć analitykę w panelu Ustawienia (również na razie może być konieczne wymuszenie ponownego uruchomienia aplikacji).\n\n
Jeśli jesteś zainteresowany opłaceniem przez nas mapboxa (lub przejściem do innego dostawcy map), napisz na meshtastic.discourse.group</string>
<string name="permission_missing">Meshtastic potrzebuje %s zezwolenie i lokalizacja muszą być włączone, aby można było znaleźć nowe urządzenia przez Bluetooth. Możesz go później wyłączyć.</string>
<string name="radio_sleeping">Radio było w trybie uśpienia, nie mogło zmienić kanału</string>
<string name="report_bug">Zgłoś bug</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Tem certeza que deseja mudar de canal? Toda comunicação com os outros dispositivos será interrompida até serem compartilhadas as novas configurações do canal.</string>
<string name="new_channel_rcvd">Novo link de canal recebido</string>
<string name="do_you_want_switch">Deseja mudar para o canal \'%s\'?</string>
<string name="map_not_allowed">O Google Analytics está desativado. Infelizmente a plataforma de mapas utilizada (Mapbox) requer este recurso ativado para uso do plano \gratuito\ . Tivemos que desativar a visualização do mapa.\n\n
Para poder visualizar o mapa será necessário ativar o Google Analytics na configuração do Android (pode ser necessário forçar o reinício do aplicativo).\n\n
Se tiver interesse em que o Meshtastic use um plano pago do Mapbox (ou mude para uma plataforma de mapas diferente), por favor envie uma mensagem em meshtastic.discourse.group</string>
<string name="permission_missing">Meshtastic precisa da permissão de %s e da localização ativada para encontrar novos dispositivos via bluetooth. Você pode desativar novamente depois.</string>
<string name="radio_sleeping">Rádio estava em suspensão (sleep), não foi possível mudar de canal</string>
<string name="report_bug">Informar Bug</string>

Wyświetl plik

@ -27,9 +27,6 @@
<string name="are_you_sure_channel">Tem certeza que deseja mudar de canal? Todas as comunicações com outros nós serão interrompidas até que partilhe as novas configurações do canal.</string>
<string name="new_channel_rcvd">Novo Link Recebido do Canal</string>
<string name="do_you_want_switch">Pretende mudar para o canal \'%s\' ?</string>
<string name="map_not_allowed">Tem os dados analícos desativados. Infelizmente o fornecedor do mapa (mapbox) requer dos dados analíticos estejam activados para o seu plano \'gratuito\' . Por isso a visualização do mapa esta desativado.\n\n
Se pretender visualizar o mapa, vai necessitar de ativar os dados anaíticos no painel Configurações (também pode ser necessário forçar o reinício do aplicativo). \n\n
Se estiver interessado em pagarmos pelo mapbox (ou mudar para um provedor de mapas diferente), contacte através de meshtastic.discourse.group</string>
<string name="permission_missing">Meshtastic precisa da permissão de %s e da localização ativada para encontrar novos dispositivos via bluetooth. Você pode desativar novamente depois.</string>
<string name="radio_sleeping">O rádio estava a dormir, não conseguia mudar de canal</string>
<string name="report_bug">Reportar Bug</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Ești sigur că vrei să schimbi canalul? Toate comunicațiile cu alte noduri vor fi oprite până când setezi aceleași detalii pe alte noduri.</string>
<string name="new_channel_rcvd">Am primit un nou URL de canal</string>
<string name="do_you_want_switch">Vrei să faci schimbul \'%s\' canalului?</string>
<string name="map_not_allowed">Ai analiticele dezactivate. Din nefericire providerul hărților (mapbox) necesită analitice în planul \'gratuit\'. Așadar am dezactivat harta.\n\n
Dacă vrei să vezi harta, trebuie să pornești analiticele în panoul de setări (s-ar putea să fii nevoit să restartezi aplicația).\n\n
Dacă sugerezi să plătim pentru mapbox (sau să schimbăm providerul hărții), te rog, postează în meshtastic.discourse.group</string>
<string name="permission_missing">O permisiune necesară lipsește, Meshtastic nu o să funcționeze corespunzător. Te rugăm activează-o în setările Android.</string>
<string name="radio_sleeping">Nu am putut să schimb canalul deoarece dispozitivul era în sleep mode</string>
<string name="report_bug">Raportează Bug</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Вы уверены, что хотите изменить канал? Связь с другими устройствами будет прервана, пока вы не поделитесь новыми настройками канала.</string>
<string name="new_channel_rcvd">URL нового канала получен</string>
<string name="do_you_want_switch">Вы хотите переключиться на \'%s\' канал?</string>
<string name="map_not_allowed">У вас отключена аналитика. К сожалению, наш поставщик карт (mapbox) требует, чтобы аналитика была разрешена для «бесплатного» использования. Мы вынуждены отключить карту.\n\n
Если вы хотите использовать карту, вам нужно включить аналитику на панели настроек (также, на данный момент вам может потребоваться принудительный перезапуск приложения).\n\n
Если вы заинтересованы в том, чтобы мы платили за карту (или переходили на другого поставщика карт), отправьте сообщение по адресу meshtastic.discourse.group.</string>
<string name="permission_missing">Требуемое разрешение отсутствует, Meshtastic не сможет работать должным образом. Пожалуйста, включите в настройках приложения.</string>
<string name="radio_sleeping">Радиомодуль в режиме сна, смена канала невозможна</string>
<string name="report_bug">Сообщить об ошибке</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Ste si istý, že chcete zmeniť kanál? Všetka komunikácia s ostatnými vysielačmi prestane fungovať až do momentu distribúcie rovnakého nastavenia na ostatné vysielače.</string>
<string name="new_channel_rcvd">Obdržaná nová URL kanálu.</string>
<string name="do_you_want_switch">Chcete sa prpnúť na kanál \'%s\' ?</string>
<string name="map_not_allowed">You have analytics disabled. Unfortunately our map provider (mapbox) requires analytics to be allowed for their \'free\' plan. So we have turned off the map view.\n\n
If you would like to see the map, you\'ll need to turn on analytics in the Settings pane (also, for the time being you might need to force restart the application).\n\n
If you are interested in us paying for mapbox (or switching to a different map provider), please post in meshtastic.discourse.group</string>
<string name="permission_missing">Aplikácia Meshtastic nemá pridelené požadované oprávnenie a pravdepodobne nebude fungovať správne. Prosím povoľte tieto oprávnenia v nastaveniach aplikácie.</string>
<string name="radio_sleeping">Vysielač je uspatý, nepodarilo sa zmeniť kanál</string>
<string name="report_bug">Nahlásenie chyby</string>

Wyświetl plik

@ -28,7 +28,6 @@
<string name="are_you_sure_channel">Ali ste prepričani, da želite spremeniti kanal? Vsa komunikacija z drugimi vozlišči se ustavi, dokler ne delite novih nastavitev kanala.</string>
<string name="new_channel_rcvd">Prejet je bil novi URL kanala</string>
<string name="do_you_want_switch">Ali želite preklopiti na kanal \'%s\' ?</string>
<string name="map_not_allowed">Vaša analitika je onemogočena. Na žalost naš ponudnik zemljevidov (mapbox) zahteva analitiko, ki je dovoljena za njihov "brezplačen" načrt. Torej smo izključili prikaz zemljevida. \n\nČe želite videti zemljevid, boste morali v podoknu z nastavitvami vklopiti analitiko (morda boste morali znova zagnati aplikacijo). \n\nČe vas zanima, kako plačujemo za karto (ali preklopiti na drugega ponudnika zemljevidov), pošljite sporočilo v meshtastic.discourse.group</string>
<string name="permission_missing">Manjka zahtevano dovoljenje: Meshtastic ne bo mogel pravilno delovati. Omogočite v nastavitvah aplikacije za Android.</string>
<string name="radio_sleeping">Radio je "spal", sprememba kanala ni bila izvedena.</string>
<string name="report_bug">Prijavi napako</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">Kanalı değiştirmek istediğinize emin misiniz? Yeni kanal ayarlarını paylaşana dek tüm node\'lar ile iletişim sonlanacak.</string>
<string name="new_channel_rcvd">Yeni Kanal Adresi alındı</string>
<string name="do_you_want_switch">\'%s\' kanalına geçmek istiyor musunuz?</string>
<string name="map_not_allowed">Analitik raporları kapattınız. Harita sağlayıcımız (mapbox) ücretsiz kullanım paketi için analitik raporları gerektiriyor maalesef. Bu yüzden harita görünümü pasif hale getirdik.\n\n
Haritayı görmek istiyorsanız, Ayarlar bölümünden analitik raporları aktif hale getirmeniz gerekiyor. (ayrıca, şu an bunun için uygulamayı tümüyle kapatıp yeniden açmanız gerekebilir).\n\n
Eğer mapbox için ödeme yapmamız (ya da başka bir harita sağlayıcıya geçme) konusunda ilgiliyseniz, lütfen meshtastic.discourse.group sayfasında paylaşınız.</string>
<string name="permission_missing">Gerekli bir izin eksik, Meshtastic düzgün çalışamayacak. Lütfen Android ayarlarından izni aktif hale getiriniz.</string>
<string name="radio_sleeping">Telsiz uyku durumundaydı, kanal değiştirilemedi</string>
<string name="report_bug">Hata Bildir</string>

Wyświetl plik

@ -28,9 +28,6 @@
<string name="are_you_sure_channel">您确定要修改通信频道吗?这将会使你与其他节点断开通信. </string>
<string name="new_channel_rcvd">收到新的频道URL</string>
<string name="do_you_want_switch">您是否要切换到\'%s\'频道?</string>
<string name="map_not_allowed">您已禁用分析。 不幸的是,我们的地图提供商 (mapbox) 要求允许分析 \“免费”\计划。 所以我们关闭了地图视图.\n\n
如果您想查看地图,您将需要在“设置”窗格中打开分析(此外,您可能暂时需要强制重新启动应用程序).\n\n
如果您对我们为 mapbox付费或切换到不同的地图提供商感兴趣请在 meshtastic.discourse.group 中发帖</string>
<string name="permission_missing">缺少所需的权限Mesh网络将无法正常工作.请在应用程序设置中启用.</string>
<string name="radio_sleeping">设备正在休眠,无法更改频道</string>
<string name="report_bug">报告BUG</string>

Wyświetl plik

@ -1,36 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="MapMenuCheckbox">
<item>Use Custom URI?</item>
</array>
<string-array name="language_entries" translatable="false">
<!-- zz --><item>@string/preferences_system_default</item>
<!-- en --><item>English</item>
<!-- cs --><item>Čeština</item>
<!-- zh --><item>Chinese 中文 </item>
<!-- de --><item>Deutsch</item>
<!-- es --><item>Español</item>
<!-- fr --><item>Français</item>
<!-- ga --><item>Gaeilge</item>
<!-- el --><item>Greek ελληνικά</item>
<!-- ht --><item>Haiti</item>
<!-- it --><item>Italiano</item>
<!-- ja --><item>Japanese 日本語</item>
<!-- ko-rKR --><item>Korean 한국어</item>
<!-- hu --><item>Magyar</item>
<!-- nl --><item>Nederlands</item>
<!-- no --><item>Norge</item>
<!-- pl --><item>Polski</item>
<!-- pt --><item>Português</item>
<!-- pt_BR --><item>Português do Brasil</item>
<!-- ro --><item>Română</item>
<!-- ru --><item>Russian Pусский</item>
<!-- sq --><item>Shqip</item>
<!-- sk --><item>Slovenský</item>
<!-- sl --><item>Slovenščina</item>
<!-- fi --><item>Suomi</item>
<!-- sv --><item>Svenska</item>
<!-- tr --><item>Türkçe</item>
<!-- zz -->
<item>@string/preferences_system_default</item>
<!-- en -->
<item>English</item>
<!-- cs -->
<item>Čeština</item>
<!-- zh -->
<item>Chinese 中文 </item>
<!-- de -->
<item>Deutsch</item>
<!-- es -->
<item>Español</item>
<!-- fr -->
<item>Français</item>
<!-- ga -->
<item>Gaeilge</item>
<!-- el -->
<item>Greek ελληνικά</item>
<!-- ht -->
<item>Haiti</item>
<!-- it -->
<item>Italiano</item>
<!-- ja -->
<item>Japanese 日本語</item>
<!-- ko-rKR -->
<item>Korean 한국어</item>
<!-- hu -->
<item>Magyar</item>
<!-- nl -->
<item>Nederlands</item>
<!-- no -->
<item>Norge</item>
<!-- pl -->
<item>Polski</item>
<!-- pt -->
<item>Português</item>
<!-- pt_BR -->
<item>Português do Brasil</item>
<!-- ro -->
<item>Română</item>
<!-- ru -->
<item>Russian Pусский</item>
<!-- sq -->
<item>Shqip</item>
<!-- sk -->
<item>Slovenský</item>
<!-- sl -->
<item>Slovenščina</item>
<!-- fi -->
<item>Suomi</item>
<!-- sv -->
<item>Svenska</item>
<!-- tr -->
<item>Türkçe</item>
</string-array>
<string-array name="language_values" translatable="false">
@ -63,13 +87,9 @@
<item>tr</item>
</string-array>
<string-array name="map_styles">
<item>Streets</item>
<item>Outdoors</item>
<item>Light</item>
<item>Dark</item>
<item>Satellite</item>
<item>Satellite Streets</item>
<item>Navigation Day</item>
<item>Navigation Night</item>
<item>OpenStreetMap</item>
<item>USGS TOPO</item>
<item>USGS Satellite</item>
<item>ESRI World Overview</item>
</string-array>
</resources>

Wyświetl plik

@ -32,9 +32,6 @@
<string name="are_you_sure_channel">Are you sure you want to change the channel? All communication with other nodes will stop until you share the new channel settings.</string>
<string name="new_channel_rcvd">New Channel URL received</string>
<string name="do_you_want_switch">Do you want to switch to the \'%s\' channel?</string>
<string name="map_not_allowed">You have analytics disabled. Unfortunately our map provider (mapbox) requires analytics to be allowed for their \'free\' plan. So we have turned off the map view.\n\n
If you would like to see the map, you\'ll need to turn on analytics in the Settings pane (also, for the time being you might need to force restart the application).\n\n
If you are interested in us paying for mapbox (or switching to a different map provider), please post in meshtastic.discourse.group</string>
<string name="permission_missing">Meshtastic needs %s permission and location must be turned on to find new devices via bluetooth. You can turn it off again afterwards.</string>
<string name="radio_sleeping">Radio was sleeping, could not change channel</string>
<string name="report_bug">Report Bug</string>
@ -140,7 +137,7 @@
<string name="download_failed">Unable to download style pack</string>
<string name="preferences_language">Language (restart needed)</string>
<string name="preferences_system_default">System default</string>
<string name="preferences_map_style">Map style</string>
<string name="preferences_map_style">Map Source</string>
<string name="resend">Resend</string>
<string name="shutdown">Shutdown</string>
<string name="reboot">Reboot</string>

Wyświetl plik

@ -40,20 +40,6 @@ buildscript {
allprojects {
repositories {
maven {
// Per https://docs.mapbox.com/android/maps/guides/install/ we now need to signin to download mapbox lib
url 'https://api.mapbox.com/downloads/v2/releases/maven'
authentication {
basic(BasicAuthentication)
}
credentials {
// Do not change the username below.
// This should always be `mapbox` (not your username).
username = 'mapbox'
// Use the secret token you stored in gradle.properties as the password
password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: ""
}
}
google()
mavenCentral()
// jcenter()