sforkowany z mirror/meshtastic-android
Merge pull request #470 from ScriptTactics/feature/osmand-migration
Feature/osmdroid migrationmaster
commit
8b412961fa
|
@ -16,16 +16,6 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
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
|
- name: Mock files for CI
|
||||||
run: |
|
run: |
|
||||||
rm ./app/google-services.json
|
rm ./app/google-services.json
|
||||||
|
|
|
@ -29,8 +29,6 @@ jobs:
|
||||||
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
|
echo "$KEYSTORE_PROPERTIES" > ./keystore.properties
|
||||||
env:
|
env:
|
||||||
GSERVICES: ${{ secrets.GSERVICES }}
|
GSERVICES: ${{ secrets.GSERVICES }}
|
||||||
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
|
|
||||||
MAPBOX_DOWNLOADS_TOKEN: ${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
|
|
||||||
KEYSTORE: ${{ secrets.KEYSTORE }}
|
KEYSTORE: ${{ secrets.KEYSTORE }}
|
||||||
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
|
KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }}
|
||||||
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
|
KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }}
|
||||||
|
|
45
README.md
45
README.md
|
@ -6,51 +6,50 @@
|
||||||
[![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg?label=Fiscal%20Contributors&color=deeppink)](https://opencollective.com/meshtastic/)
|
[![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)
|
[![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:
|
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)
|
[![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.
|
To join the beta program for the app go to
|
||||||
If you encounter any problems or have questions, [post in the forum](https://meshtastic.discourse.group/) and we'll help.
|
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
|
## Build instructions
|
||||||
|
|
||||||
If you would like to develop this application we'd love your help! These build instructions are brief
|
If you would like to develop this application we'd love your help! These build instructions are
|
||||||
and should be improved, please send a PR if you can.
|
brief and should be improved, please send a PR if you can.
|
||||||
|
|
||||||
- Use Android Studio to build/debug
|
- Use Android Studio to build/debug
|
||||||
- Use "git submodule update --init --recursive" to pull in the various submodules we depend on
|
- 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.
|
- There are a few config files which you'll need to copy from templates included in the project. Run
|
||||||
Run the following commands to do so:
|
the following commands to do so:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rm ./app/google-services.json
|
rm ./app/google-services.json
|
||||||
cp ./app/google-services-example.json ./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
|
rm ./app/src/main/res/values/curfirmwareversion.xml
|
||||||
cp ./app/special/curfirmwareversion.xml ./app/src/main/res/values/
|
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.
|
- 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
|
||||||
```bash
|
used in that environment.
|
||||||
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.
|
|
||||||
|
|
||||||
## Analytics setup
|
## Analytics setup
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,6 @@ android {
|
||||||
// doesn't like and we need to strip them (gr)
|
// 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"
|
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 {
|
ndk {
|
||||||
// abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
// abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
|
||||||
}
|
}
|
||||||
|
@ -88,7 +87,7 @@ android {
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "1.8"
|
jvmTarget = "1.8"
|
||||||
freeCompilerArgs += [ '-opt-in=kotlin.RequiresOptIn' ]
|
freeCompilerArgs += ['-opt-in=kotlin.RequiresOptIn']
|
||||||
}
|
}
|
||||||
lint {
|
lint {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
|
@ -140,6 +139,9 @@ dependencies {
|
||||||
kapt "androidx.room:room-compiler:$room_version"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
kapt "com.google.dagger:hilt-compiler:$hilt_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
|
// optional - Kotlin Extensions and Coroutines support for Room
|
||||||
implementation "androidx.room:room-ktx:$room_version"
|
implementation "androidx.room:room-ktx:$room_version"
|
||||||
|
|
||||||
|
@ -166,9 +168,6 @@ dependencies {
|
||||||
// implementation 'com.google.android.things:androidthings:1.0'
|
// implementation 'com.google.android.things:androidthings:1.0'
|
||||||
implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
|
implementation 'com.github.mik3y:usb-serial-for-android:3.4.6'
|
||||||
|
|
||||||
// mapbox
|
|
||||||
implementation 'com.mapbox.maps:android:10.2.0'
|
|
||||||
|
|
||||||
// location services
|
// location services
|
||||||
implementation 'com.google.android.gms:play-services-location:19.0.1'
|
implementation 'com.google.android.gms:play-services-location:19.0.1'
|
||||||
// For Google Sign-In (owner name accesss)
|
// For Google Sign-In (owner name accesss)
|
||||||
|
|
|
@ -30,9 +30,6 @@
|
||||||
-keep class com.geeksville.mesh.**{*;}
|
-keep class com.geeksville.mesh.**{*;}
|
||||||
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite { <fields>; }
|
-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
|
# for kotlinx.serialization
|
||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
|
|
|
@ -126,6 +126,7 @@ class MainActivity : BaseActivity(), Logging,
|
||||||
// const val REQUEST_ENABLE_BT = 10
|
// const val REQUEST_ENABLE_BT = 10
|
||||||
const val DID_REQUEST_PERM = 11
|
const val DID_REQUEST_PERM = 11
|
||||||
const val RC_SIGN_IN = 12 // google signin completed
|
const val RC_SIGN_IN = 12 // google signin completed
|
||||||
|
|
||||||
// const val SELECT_DEVICE_REQUEST_CODE = 13
|
// const val SELECT_DEVICE_REQUEST_CODE = 13
|
||||||
const val CREATE_CSV_FILE = 14
|
const val CREATE_CSV_FILE = 14
|
||||||
}
|
}
|
||||||
|
@ -331,7 +332,7 @@ class MainActivity : BaseActivity(), Logging,
|
||||||
DID_REQUEST_PERM -> {
|
DID_REQUEST_PERM -> {
|
||||||
// If request is cancelled, the result arrays are empty.
|
// If request is cancelled, the result arrays are empty.
|
||||||
if ((grantResults.isNotEmpty() &&
|
if ((grantResults.isNotEmpty() &&
|
||||||
grantResults[0] == PackageManager.PERMISSION_GRANTED)
|
grantResults[0] == PackageManager.PERMISSION_GRANTED)
|
||||||
) {
|
) {
|
||||||
// Permission is granted. Continue the action or workflow
|
// Permission is granted. Continue the action or workflow
|
||||||
// in your app.
|
// in your app.
|
||||||
|
@ -664,7 +665,13 @@ class MainActivity : BaseActivity(), Logging,
|
||||||
|
|
||||||
// model.setLocalConfig(LocalOnlyProtos.LocalConfig.parseFrom(service.deviceConfig))
|
// 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()
|
model.updateNodesFromDevice()
|
||||||
|
|
||||||
|
@ -1072,14 +1079,10 @@ class MainActivity : BaseActivity(), Logging,
|
||||||
chooseLangDialog()
|
chooseLangDialog()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.preferences_map_style -> {
|
|
||||||
chooseMapStyle()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.show_intro -> {
|
R.id.show_intro -> {
|
||||||
startActivity(Intent(this, AppIntroduction::class.java))
|
startActivity(Intent(this, AppIntroduction::class.java))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.preferences_quick_chat -> {
|
R.id.preferences_quick_chat -> {
|
||||||
val fragmentManager: FragmentManager = supportFragmentManager
|
val fragmentManager: FragmentManager = supportFragmentManager
|
||||||
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
|
||||||
|
@ -1192,7 +1195,10 @@ class MainActivity : BaseActivity(), Logging,
|
||||||
val lang = prefs.getString("lang", "zz")
|
val lang = prefs.getString("lang", "zz")
|
||||||
debug("Lang from prefs: $lang")
|
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]
|
val selectedLang = languageValues[which]
|
||||||
debug("Set lang pref to $selectedLang")
|
debug("Set lang pref to $selectedLang")
|
||||||
editor.putString("lang", selectedLang)
|
editor.putString("lang", selectedLang)
|
||||||
|
@ -1202,27 +1208,4 @@ class MainActivity : BaseActivity(), Logging,
|
||||||
val dialog = builder.create()
|
val dialog = builder.create()
|
||||||
dialog.show()
|
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,680 +1,251 @@
|
||||||
package com.geeksville.mesh.ui
|
package com.geeksville.mesh.ui
|
||||||
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import com.geeksville.android.GeeksvilleApplication
|
|
||||||
import com.geeksville.android.Logging
|
import com.geeksville.android.Logging
|
||||||
|
import com.geeksville.mesh.BuildConfig
|
||||||
import com.geeksville.mesh.NodeInfo
|
import com.geeksville.mesh.NodeInfo
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.databinding.MapNotAllowedBinding
|
|
||||||
import com.geeksville.mesh.databinding.MapViewBinding
|
import com.geeksville.mesh.databinding.MapViewBinding
|
||||||
|
import com.geeksville.mesh.model.CustomTileSource
|
||||||
import com.geeksville.mesh.model.UIViewModel
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.geeksville.util.formatAgo
|
import com.geeksville.util.formatAgo
|
||||||
import com.mapbox.bindgen.Value
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
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 dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlin.math.cos
|
import org.osmdroid.api.IMapController
|
||||||
import kotlin.math.sin
|
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
|
@AndroidEntryPoint
|
||||||
class MapFragment : ScreenFragment("Map"), Logging {
|
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 binding: MapViewBinding
|
||||||
private lateinit var mapNotAllowedBinding: MapNotAllowedBinding
|
private lateinit var map: MapView
|
||||||
private var userStyleURI: String? = null
|
private lateinit var mapController: IMapController
|
||||||
|
private lateinit var mPrefs: SharedPreferences
|
||||||
private lateinit var geoJsonSource: GeoJsonSource
|
|
||||||
private lateinit var lineLayer: LineLayer
|
|
||||||
|
|
||||||
private var point: Point? = null
|
|
||||||
|
|
||||||
private val model: UIViewModel by activityViewModels()
|
private val model: UIViewModel by activityViewModels()
|
||||||
|
|
||||||
private val nodeSourceId = "node-positions"
|
private val defaultMinZoom = 1.5
|
||||||
private val nodeLayerId = "node-layer"
|
private val nodeZoomLevel = 8.5
|
||||||
private val labelLayerId = "label-layer"
|
private val defaultZoomSpeed = 3000L
|
||||||
private val markerImageId = "my-marker-image"
|
private val prefsName = "org.andnav.osm.prefs"
|
||||||
private val userPointImageId = "user-image"
|
private val mapStyleId = "map_style_id"
|
||||||
private val boundingBoxId = "bounding-box-id"
|
private val uiPrefs = "ui-prefs"
|
||||||
private val lineLayerId = "line-layer-id"
|
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(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
// We can't allow mapbox if user doesn't want analytics
|
binding = MapViewBinding.inflate(inflater)
|
||||||
return if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
|
return binding.root
|
||||||
// Mapbox Access token
|
|
||||||
binding = MapViewBinding.inflate(inflater, container, false)
|
|
||||||
binding.root
|
|
||||||
} else {
|
|
||||||
mapNotAllowedBinding = MapNotAllowedBinding.inflate(inflater, container, false)
|
|
||||||
mapNotAllowedBinding.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?) {
|
override fun onViewCreated(viewIn: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(viewIn, savedInstanceState)
|
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
|
setupMapProperties()
|
||||||
if ((requireContext().applicationContext as GeeksvilleApplication).isAnalyticsAllowed) {
|
loadOnlineTileSourceBase()
|
||||||
// binding.fabStyleToggle.setOnClickListener {
|
map.let {
|
||||||
// //TODO: Setup Style menu for satellite view, street view, & outdoor view
|
if (view != null) {
|
||||||
// }
|
mapController = map.controller
|
||||||
binding.downloadRegion.setOnClickListener {
|
binding.fabStyleToggle.setOnClickListener {
|
||||||
// Display menu for download region
|
chooseMapStyle()
|
||||||
this.downloadRegionDialogFragment()
|
}
|
||||||
}
|
model.nodeDB.nodes.value?.let { nodes ->
|
||||||
|
onNodesChanged(nodes.values)
|
||||||
val vIn = viewIn.findViewById<MapView>(R.id.mapView)
|
drawOverlays()
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 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 {
|
/// Load preferences and its value
|
||||||
mapView?.getMapboxMap()
|
val prefs = UIViewModel.getPreferences(context!!)
|
||||||
?.getStyle()?.styleURI.toString()
|
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())
|
||||||
}
|
}
|
||||||
|
val dialog = builder.create()
|
||||||
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()
|
|
||||||
dialog.show()
|
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!!)
|
private fun onNodesChanged(nodes: Collection<NodeInfo>) {
|
||||||
dialog.dismiss()
|
val nodesWithPosition = nodes.filter { it.validPosition != null }
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
/**
|
||||||
requireContext(),
|
* Using the latest nodedb, generate GeoPoint
|
||||||
R.string.style_uri_empty_alert,
|
*/
|
||||||
Toast.LENGTH_SHORT
|
// Find all nodes with valid locations
|
||||||
).show()
|
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 {
|
} else {
|
||||||
downloadOfflineRegion()
|
// Only one node, just zoom in on it
|
||||||
dialog.dismiss()
|
val it = nodesWithPosition[0].position!!
|
||||||
|
points.add(GeoPoint(it.latitude, it.longitude))
|
||||||
|
controller.animateTo(points[0], nodeZoomLevel, defaultZoomSpeed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadMapStyleFromPref():String {
|
private fun loadOnlineTileSourceBase(): ITileSource {
|
||||||
val prefs = context?.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
|
val prefs = context?.getSharedPreferences(uiPrefs, Context.MODE_PRIVATE)
|
||||||
val mapStyleId = prefs?.getInt("map_style_id", 1)
|
val mapSourceId = prefs?.getInt(mapStyleId, 1)
|
||||||
debug("mapStyleId from prefs: $mapStyleId")
|
debug("mapStyleId from prefs: $mapSourceId")
|
||||||
val mapStyle = when (mapStyleId) {
|
return CustomTileSource.mTileSources[mapSourceId!!]
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -5,93 +5,22 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<com.mapbox.maps.MapView
|
<org.osmdroid.views.MapView
|
||||||
android:id="@+id/mapView"
|
android:id="@+id/map"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent" />
|
||||||
app:mapbox_cameraZoom="0" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/fab_style_toggle"
|
||||||
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
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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:orientation="vertical"
|
||||||
|
android:src="@drawable/baseline_layers_white_24dp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
android:visibility="visible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
<!-- <com.google.android.material.floatingactionbutton.FloatingActionButton-->
|
tools:background="@color/design_default_color_secondary" />
|
||||||
<!-- 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>
|
|
||||||
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -32,10 +32,6 @@
|
||||||
android:id="@+id/preferences_language"
|
android:id="@+id/preferences_language"
|
||||||
android:title="@string/preferences_language"
|
android:title="@string/preferences_language"
|
||||||
app:showAsAction="withText" />
|
app:showAsAction="withText" />
|
||||||
<item
|
|
||||||
android:id="@+id/preferences_map_style"
|
|
||||||
android:title="@string/preferences_map_style"
|
|
||||||
app:showAsAction="withText" />
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/show_intro"
|
android:id="@+id/show_intro"
|
||||||
android:title="@string/show_intro"
|
android:title="@string/show_intro"
|
||||||
|
|
|
@ -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="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="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="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="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="radio_sleeping">Vysílač je uspaný, nepodařilo se změnit kanál.</string>
|
||||||
<string name="report_bug">Nahlášení chyby</string>
|
<string name="report_bug">Nahlášení chyby</string>
|
||||||
|
|
|
@ -28,9 +28,6 @@
|
||||||
<string name="are_you_sure_channel">Είστε βέβαιοι ότι θέλετε να αλλάξετε κανάλι? Η επικοινωνία με άλλες συσκευές θα σταματήσεις μέχρι να μοιραστείτε τις ρυθμίσεις του νέου καναλιού.</string>
|
<string name="are_you_sure_channel">Είστε βέβαιοι ότι θέλετε να αλλάξετε κανάλι? Η επικοινωνία με άλλες συσκευές θα σταματήσεις μέχρι να μοιραστείτε τις ρυθμίσεις του νέου καναλιού.</string>
|
||||||
<string name="new_channel_rcvd">Λήψη URL νέου καναλιού</string>
|
<string name="new_channel_rcvd">Λήψη URL νέου καναλιού</string>
|
||||||
<string name="do_you_want_switch">Θέλετε να αλλάξετε ‘%s’ κανάλι?</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="permission_missing">Λείπει μια απαιτούμενη άδεια, Meshtastic δεν θα λειτοργεί σωστά. Ενεργοποιήστε τις ρυθμίσεις εφαρμογής Android.</string>
|
||||||
<string name="radio_sleeping">Radio σε κατάσταση ύπνου, δεν γίνεται αλλαγή καναλιού</string>
|
<string name="radio_sleeping">Radio σε κατάσταση ύπνου, δεν γίνεται αλλαγή καναλιού</string>
|
||||||
<string name="report_bug">Αναφορά Bug</string>
|
<string name="report_bug">Αναφορά Bug</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Nouveau URL canal reçu</string>
|
||||||
<string name="do_you_want_switch">Voulez-vous changer vers canal \'%s\' ?</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="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="radio_sleeping">Radio en veille, change de Canal impossible</string>
|
||||||
<string name="report_bug">Rapporter Bogue</string>
|
<string name="report_bug">Rapporter Bogue</string>
|
||||||
|
|
|
@ -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="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="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="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, b’fhé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="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="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>
|
<string name="report_bug">Tuairiscigh fabht</string>
|
||||||
|
|
|
@ -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="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="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="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="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="radio_sleeping">Radyo t\'ap dòmi, pat ka chanje chènn</string>
|
||||||
<string name="report_bug">Report Bug</string>
|
<string name="report_bug">Report Bug</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Új csatorna URL érkezett</string>
|
||||||
<string name="do_you_want_switch">Átkapcsol a(z) \'%s\' csatornára?</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="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="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>
|
<string name="report_bug">Hiba jelentése</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Nuovo Canale URL ricevuto</string>
|
||||||
<string name="do_you_want_switch">Vuoi passare al canale \'%s\' ?</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="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="radio_sleeping">La radio dormiva, non poteva cambiare canale.</string>
|
||||||
<string name="report_bug">Segnala bug</string>
|
<string name="report_bug">Segnala bug</string>
|
||||||
|
|
|
@ -28,9 +28,6 @@
|
||||||
<string name="are_you_sure_channel">チャンネルを変更しますか?新しいチャンネル設定をシェアするまで他のノードとの通信はすべて停止します。</string>
|
<string name="are_you_sure_channel">チャンネルを変更しますか?新しいチャンネル設定をシェアするまで他のノードとの通信はすべて停止します。</string>
|
||||||
<string name="new_channel_rcvd">新しいチャンネルURLを受信しました</string>
|
<string name="new_channel_rcvd">新しいチャンネルURLを受信しました</string>
|
||||||
<string name="do_you_want_switch">\'%s\チャンネルに変更しますか?</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="permission_missing">必要なアクセス権限が拒否されているため、アプリが正常に動作しません。設定により権限を許可してください。</string>
|
||||||
<string name="radio_sleeping">Meshtasticデバイスはスリープ状態です。チャンネルを変更できませんでした。</string>
|
<string name="radio_sleeping">Meshtasticデバイスはスリープ状態です。チャンネルを変更できませんでした。</string>
|
||||||
<string name="report_bug">バグ報告</string>
|
<string name="report_bug">バグ報告</string>
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
<string name="are_you_sure_channel">채널 변경을 원하세요? 채널 설정이 공유되기 전까지 다른 노드와의 통신은 중단됩니다.</string>
|
<string name="are_you_sure_channel">채널 변경을 원하세요? 채널 설정이 공유되기 전까지 다른 노드와의 통신은 중단됩니다.</string>
|
||||||
<string name="new_channel_rcvd">새로운 채널 URL 수신</string>
|
<string name="new_channel_rcvd">새로운 채널 URL 수신</string>
|
||||||
<string name="do_you_want_switch">\'%s\' 채널로 변경할 까요?</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="permission_missing">필요로 하는 권한을 얻지 못했습니다. Meshtastic는 정상적으로 작동하지 않을 수도 있습니다. 스마트폰 설정에서 권한을 설정해주세요.</string>
|
||||||
<string name="radio_sleeping">메쉬태스틱기기가 절전모드 중 이므로 채널을 변경할 수 없습니다.</string>
|
<string name="radio_sleeping">메쉬태스틱기기가 절전모드 중 이므로 채널을 변경할 수 없습니다.</string>
|
||||||
<string name="report_bug">버그 보고</string>
|
<string name="report_bug">버그 보고</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Nieuw kanaal URL ontvangen</string>
|
||||||
<string name="do_you_want_switch">Wil je veranderen naar kanaal \'%s\' ?</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="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="radio_sleeping">Radio was in slaapmodus, kon het kanaal niet veranderen</string>
|
||||||
<string name="report_bug">Rapporteer Bug</string>
|
<string name="report_bug">Rapporteer Bug</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Ny kanal URL mottatt</string>
|
||||||
<string name="do_you_want_switch">Vil du bytte til \'%s\' kanal?</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="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="radio_sleeping">Radio sov.kunne ikke endre kanal</string>
|
||||||
<string name="report_bug">Rapporter Feil</string>
|
<string name="report_bug">Rapporter Feil</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Odebrano nowy URL kanału</string>
|
||||||
<string name="do_you_want_switch">Chcesz przełączyć na \'%s\' kanał?</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="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="radio_sleeping">Radio było w trybie uśpienia, nie mogło zmienić kanału</string>
|
||||||
<string name="report_bug">Zgłoś bug</string>
|
<string name="report_bug">Zgłoś bug</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Novo link de canal recebido</string>
|
||||||
<string name="do_you_want_switch">Deseja mudar para o canal \'%s\'?</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="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="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>
|
<string name="report_bug">Informar Bug</string>
|
||||||
|
|
|
@ -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="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="new_channel_rcvd">Novo Link Recebido do Canal</string>
|
||||||
<string name="do_you_want_switch">Pretende mudar para o canal \'%s\' ?</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="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="radio_sleeping">O rádio estava a dormir, não conseguia mudar de canal</string>
|
||||||
<string name="report_bug">Reportar Bug</string>
|
<string name="report_bug">Reportar Bug</string>
|
||||||
|
|
|
@ -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="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="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="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="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="radio_sleeping">Nu am putut să schimb canalul deoarece dispozitivul era în sleep mode</string>
|
||||||
<string name="report_bug">Raportează Bug</string>
|
<string name="report_bug">Raportează Bug</string>
|
||||||
|
|
|
@ -28,9 +28,6 @@
|
||||||
<string name="are_you_sure_channel">Вы уверены, что хотите изменить канал? Связь с другими устройствами будет прервана, пока вы не поделитесь новыми настройками канала.</string>
|
<string name="are_you_sure_channel">Вы уверены, что хотите изменить канал? Связь с другими устройствами будет прервана, пока вы не поделитесь новыми настройками канала.</string>
|
||||||
<string name="new_channel_rcvd">URL нового канала получен</string>
|
<string name="new_channel_rcvd">URL нового канала получен</string>
|
||||||
<string name="do_you_want_switch">Вы хотите переключиться на \'%s\' канал?</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="permission_missing">Требуемое разрешение отсутствует, Meshtastic не сможет работать должным образом. Пожалуйста, включите в настройках приложения.</string>
|
||||||
<string name="radio_sleeping">Радиомодуль в режиме сна, смена канала невозможна</string>
|
<string name="radio_sleeping">Радиомодуль в режиме сна, смена канала невозможна</string>
|
||||||
<string name="report_bug">Сообщить об ошибке</string>
|
<string name="report_bug">Сообщить об ошибке</string>
|
||||||
|
|
|
@ -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="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="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="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="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="radio_sleeping">Vysielač je uspatý, nepodarilo sa zmeniť kanál</string>
|
||||||
<string name="report_bug">Nahlásenie chyby</string>
|
<string name="report_bug">Nahlásenie chyby</string>
|
||||||
|
|
|
@ -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="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="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="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="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="radio_sleeping">Radio je "spal", sprememba kanala ni bila izvedena.</string>
|
||||||
<string name="report_bug">Prijavi napako</string>
|
<string name="report_bug">Prijavi napako</string>
|
||||||
|
|
|
@ -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="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="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="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="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="radio_sleeping">Telsiz uyku durumundaydı, kanal değiştirilemedi</string>
|
||||||
<string name="report_bug">Hata Bildir</string>
|
<string name="report_bug">Hata Bildir</string>
|
||||||
|
|
|
@ -28,9 +28,6 @@
|
||||||
<string name="are_you_sure_channel">您确定要修改通信频道吗?这将会使你与其他节点断开通信. </string>
|
<string name="are_you_sure_channel">您确定要修改通信频道吗?这将会使你与其他节点断开通信. </string>
|
||||||
<string name="new_channel_rcvd">收到新的频道URL</string>
|
<string name="new_channel_rcvd">收到新的频道URL</string>
|
||||||
<string name="do_you_want_switch">您是否要切换到\'%s\'频道?</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="permission_missing">缺少所需的权限,Mesh网络将无法正常工作.请在应用程序设置中启用.</string>
|
||||||
<string name="radio_sleeping">设备正在休眠,无法更改频道</string>
|
<string name="radio_sleeping">设备正在休眠,无法更改频道</string>
|
||||||
<string name="report_bug">报告BUG</string>
|
<string name="report_bug">报告BUG</string>
|
||||||
|
|
|
@ -1,36 +1,60 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<array name="MapMenuCheckbox">
|
|
||||||
<item>Use Custom URI?</item>
|
|
||||||
</array>
|
|
||||||
<string-array name="language_entries" translatable="false">
|
<string-array name="language_entries" translatable="false">
|
||||||
<!-- zz --><item>@string/preferences_system_default</item>
|
<!-- zz -->
|
||||||
<!-- en --><item>English</item>
|
<item>@string/preferences_system_default</item>
|
||||||
<!-- cs --><item>Čeština</item>
|
<!-- en -->
|
||||||
<!-- zh --><item>Chinese 中文 </item>
|
<item>English</item>
|
||||||
<!-- de --><item>Deutsch</item>
|
<!-- cs -->
|
||||||
<!-- es --><item>Español</item>
|
<item>Čeština</item>
|
||||||
<!-- fr --><item>Français</item>
|
<!-- zh -->
|
||||||
<!-- ga --><item>Gaeilge</item>
|
<item>Chinese 中文 </item>
|
||||||
<!-- el --><item>Greek ελληνικά</item>
|
<!-- de -->
|
||||||
<!-- ht --><item>Haiti</item>
|
<item>Deutsch</item>
|
||||||
<!-- it --><item>Italiano</item>
|
<!-- es -->
|
||||||
<!-- ja --><item>Japanese 日本語</item>
|
<item>Español</item>
|
||||||
<!-- ko-rKR --><item>Korean 한국어</item>
|
<!-- fr -->
|
||||||
<!-- hu --><item>Magyar</item>
|
<item>Français</item>
|
||||||
<!-- nl --><item>Nederlands</item>
|
<!-- ga -->
|
||||||
<!-- no --><item>Norge</item>
|
<item>Gaeilge</item>
|
||||||
<!-- pl --><item>Polski</item>
|
<!-- el -->
|
||||||
<!-- pt --><item>Português</item>
|
<item>Greek ελληνικά</item>
|
||||||
<!-- pt_BR --><item>Português do Brasil</item>
|
<!-- ht -->
|
||||||
<!-- ro --><item>Română</item>
|
<item>Haiti</item>
|
||||||
<!-- ru --><item>Russian Pусский</item>
|
<!-- it -->
|
||||||
<!-- sq --><item>Shqip</item>
|
<item>Italiano</item>
|
||||||
<!-- sk --><item>Slovenský</item>
|
<!-- ja -->
|
||||||
<!-- sl --><item>Slovenščina</item>
|
<item>Japanese 日本語</item>
|
||||||
<!-- fi --><item>Suomi</item>
|
<!-- ko-rKR -->
|
||||||
<!-- sv --><item>Svenska</item>
|
<item>Korean 한국어</item>
|
||||||
<!-- tr --><item>Türkçe</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>
|
||||||
|
|
||||||
<string-array name="language_values" translatable="false">
|
<string-array name="language_values" translatable="false">
|
||||||
|
@ -63,13 +87,9 @@
|
||||||
<item>tr</item>
|
<item>tr</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="map_styles">
|
<string-array name="map_styles">
|
||||||
<item>Streets</item>
|
<item>OpenStreetMap</item>
|
||||||
<item>Outdoors</item>
|
<item>USGS TOPO</item>
|
||||||
<item>Light</item>
|
<item>USGS Satellite</item>
|
||||||
<item>Dark</item>
|
<item>ESRI World Overview</item>
|
||||||
<item>Satellite</item>
|
|
||||||
<item>Satellite Streets</item>
|
|
||||||
<item>Navigation Day</item>
|
|
||||||
<item>Navigation Night</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
|
@ -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="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="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="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="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="radio_sleeping">Radio was sleeping, could not change channel</string>
|
||||||
<string name="report_bug">Report Bug</string>
|
<string name="report_bug">Report Bug</string>
|
||||||
|
@ -140,7 +137,7 @@
|
||||||
<string name="download_failed">Unable to download style pack</string>
|
<string name="download_failed">Unable to download style pack</string>
|
||||||
<string name="preferences_language">Language (restart needed)</string>
|
<string name="preferences_language">Language (restart needed)</string>
|
||||||
<string name="preferences_system_default">System default</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="resend">Resend</string>
|
||||||
<string name="shutdown">Shutdown</string>
|
<string name="shutdown">Shutdown</string>
|
||||||
<string name="reboot">Reboot</string>
|
<string name="reboot">Reboot</string>
|
||||||
|
|
14
build.gradle
14
build.gradle
|
@ -40,20 +40,6 @@ buildscript {
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
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()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
// jcenter()
|
// jcenter()
|
||||||
|
|
Ładowanie…
Reference in New Issue