diff --git a/app/build.gradle b/app/build.gradle index 4455c756e..47909d9b9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,7 +46,7 @@ android { defaultConfig { // We have to list all translated languages here, because some of our libs have bogus languages that google play // doesn't like and we need to strip them (gr) - resConfigs "cs", "de", "el", "en", "es", "fi", "fr", "ga", "ht", "it", "ja", "nl", "pt", "ru", "sk", "sl", "sq", "sv", "tr", "zh" + resConfigs "cs", "de", "el", "en", "es", "fi", "fr", "ga", "ht", "it", "ja", "nl", "no", "pt", "ro", "ru", "sk", "sl", "sq", "sv", "tr", "zh" // Needed to make mapbox work inside the firebase testlab - FIXME, alas, still doesn't work ndk { diff --git a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt new file mode 100644 index 000000000..970dcdd51 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt @@ -0,0 +1,12 @@ +package com.geeksville.mesh.android + +import android.bluetooth.BluetoothManager +import android.content.Context +import android.hardware.usb.UsbManager + +/** + * @return null on platforms without a BlueTooth driver (i.e. the emulator) + */ +val Context.bluetoothManager: BluetoothManager? get() = getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager? + +val Context.usbManager: UsbManager get() = requireNotNull(getSystemService(Context.USB_SERVICE) as? UsbManager?) { "USB_SERVICE is not available"} diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index eeb100c6b..a673dd6e8 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -17,12 +17,23 @@ import com.geeksville.mesh.MyNodeInfo import com.geeksville.mesh.service.MeshService /// Given a human name, strip out the first letter of the first three words and return that as the initials for -/// that user. -fun getInitials(name: String): String { - val words = name.split(Regex("\\s+")).filter { it.isNotEmpty() }.take(3).map { it.first() } - .joinToString("") +/// that user. If the original name is only one word, strip vowels from the original name and if the result is +/// 3 or more characters, use the first three characters. If not, just take the first 3 characters of the +/// original name. +fun getInitials(nameIn: String): String { + val nchars = 3 + val minchars = 2 + val name = nameIn.trim() + val words = name.split(Regex("\\s+")).filter { it.isNotEmpty() } - return words + val initials = when (words.size) { + in 0..minchars - 1 -> { + val nm = name.filterNot { c -> c.toLowerCase() in "aeiou" } + if (nm.length >= nchars) nm else name + } + else -> words.map { it.first() }.joinToString("") + } + return initials.take(nchars) } class UIViewModel(app: Application) : AndroidViewModel(app), Logging { diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 816025b7e..24a732b92 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -278,11 +278,18 @@ class MeshService : Service(), Logging { warnUserAboutLocation() } is ApiException -> - if (exception.statusCode == 17) { - // error: cancelled by user - errormsg("User cancelled location access", exception) - } else { - Exceptions.report(exception) + when (exception.statusCode) { + 17 -> + // error: cancelled by user + errormsg("User cancelled location access", exception) + 8502 -> + // error: settings change unavailable + errormsg( + "Settings-change-unavailable, user disabled location access (globally?)", + exception + ) + else -> + Exceptions.report(exception) } else -> Exceptions.report(exception) diff --git a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt index 69ff4de7e..9bdeda6ae 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SafeBluetooth.kt @@ -10,6 +10,7 @@ import com.geeksville.android.Logging import com.geeksville.concurrent.CallbackContinuation import com.geeksville.concurrent.Continuation import com.geeksville.concurrent.SyncContinuation +import com.geeksville.mesh.android.bluetoothManager import com.geeksville.util.exceptionReporter import kotlinx.coroutines.* import java.io.Closeable @@ -101,10 +102,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD fun restartBle() { GeeksvilleApplication.analytics.track("ble_restart") // record # of times we needed to use this nasty hack errormsg("Doing emergency BLE restart") - val mgr = - context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager - val adp = mgr.adapter - if (null != adp) { + context.bluetoothManager?.adapter?.let { adp -> if (adp.isEnabled) { adp.disable() // TODO: display some kind of UI about restarting BLE diff --git a/app/src/main/java/com/geeksville/mesh/service/SerialInterface.kt b/app/src/main/java/com/geeksville/mesh/service/SerialInterface.kt index 9637f8bf3..00b802527 100644 --- a/app/src/main/java/com/geeksville/mesh/service/SerialInterface.kt +++ b/app/src/main/java/com/geeksville/mesh/service/SerialInterface.kt @@ -7,6 +7,7 @@ import android.content.IntentFilter import android.hardware.usb.UsbDevice import android.hardware.usb.UsbManager import com.geeksville.android.Logging +import com.geeksville.mesh.android.usbManager import com.geeksville.util.exceptionReporter import com.geeksville.util.ignoreException import com.hoho.android.usbserial.driver.UsbSerialDriver @@ -32,8 +33,7 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S fun toInterfaceName(deviceName: String) = "s$deviceName" fun findDrivers(context: Context): List { - val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager - val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager) + val drivers = UsbSerialProber.getDefaultProber().findAllDrivers(context.usbManager) val devices = drivers.map { it.device } devices.forEach { d -> debug("Found serial port ${d.deviceName}") @@ -43,8 +43,7 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S fun addressValid(context: Context, rest: String): Boolean { findSerial(context, rest)?.let { d -> - val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager - return assumePermission || manager.hasPermission(d.device) + return assumePermission || context.usbManager.hasPermission(d.device) } return false } @@ -75,8 +74,7 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S if (UsbManager.ACTION_USB_DEVICE_ATTACHED == intent.action) { debug("attaching USB") val device: UsbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)!! - val manager = context.getSystemService(Context.USB_SERVICE) as UsbManager - if (assumePermission || manager.hasPermission(device)) { + if (assumePermission || context.usbManager.hasPermission(device)) { // reinit the port from scratch and reopen onDeviceDisconnect(true) connect() diff --git a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt index a1bd80e07..04e272ebf 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt @@ -33,6 +33,8 @@ import com.geeksville.android.isGooglePlayAvailable import com.geeksville.concurrent.handledLaunch import com.geeksville.mesh.MainActivity import com.geeksville.mesh.R +import com.geeksville.mesh.android.bluetoothManager +import com.geeksville.mesh.android.usbManager import com.geeksville.mesh.model.UIViewModel import com.geeksville.mesh.service.BluetoothInterface import com.geeksville.mesh.service.MeshService @@ -137,11 +139,8 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging { debug("BTScanModel cleared") } - /// Note: may be null on platforms without a bluetooth driver (ie. the emulator) - val bluetoothAdapter = - (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter - - private val usbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager + val bluetoothAdapter = context.bluetoothManager?.adapter + private val usbManager get() = context.usbManager var selectedAddress: String? = null val errorText = object : MutableLiveData(null) {} diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml new file mode 100644 index 000000000..4795d769e --- /dev/null +++ b/app/src/main/res/values-no/strings.xml @@ -0,0 +1,75 @@ + + + Innstillinger + Kanal Navn + Kanal valg + Delingsknapp + QR kode + Lås opp + Tilkoblingsstatus + applikasjon ikon + Ukjent Brukernavn + Bruker symbol + hei, jeg fant skatten, den er like ved den store tigeren. Jeg er litt redd. + Send Tekst + Du har ikke paret en Meshtastic kompatibel radio med denne telefonen. Vennligst parr en enhet, og sett ditt brukernavn.\n\nDenne åpen kildekode applikasjonen er i alfa-testing, Hvis du finner problemer, vennligst post på vårt forum: meshtastic.discourse.group.\n\nFor mer informasjon, se vår nettside - www.meshtastic.org. + Brukernavn ikke satt + Ditt Navn + Anonym brukerstatistikk og kræsjrapporter. + Ser etter Meshtastic enheter... + Meshtastic_ac23 + Meshtastic_1267 + Denne applikasjonen krever blåtann tilgang.Vennligst gi tilgang i android instillinger. + Feil - denne app krever blåtann + Starter paring + Paring feilet + En URL for å bli med i et Meshtastic nett + Godta + Avbrytl + Endre kanal + Er du sikker på at du vil endre kanalen? All kommunikasjon med andre noder vil stanse, intill du deler de nye kanalinstillingene. + Ny kanal URL mottatt + Vil du bytte til \'%s\' kanal? + 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 + En påkrevet tilgang mangler, Meshtastic vil ikke fungere korrekt. Vennligst slå på i Android appliksjonsinstillinger. + Radio sov.kunne ikke endre kanal + Rapporter Feil + Rapporter en feil + Er du sikker på at du vil rapportere en feil? Etter rapportering, vennligst posti meshtastic.discourse.group så vi kan matche rapporten med hva du fant + Rapport + Velg radio + Du er nå paret med radio %s + Du har ikke paret med en radio ennå + Endre radio + Venligst par enhet i Android instillinger. + Paring fullført, starter tjeneste + Paring feilet, vennligst velg igjen + Lokasjonstilgang er slått av,kan ikke gi posisjon til mesh. + Del + Frakoblet + Enhet sover + Tilkoblet: %s av %s på nett + En liste over noder i nettverket + Oppdater Firmware + Tilkoblet radio + Tilkoblet til radio (%s) + Ikke tilkoblet. velg radio nedenfor + Tilkoblet radio, men den sover + Oppdater til %s + Applikasjon for gammel + Du må oppdatere denne applikasjonen på Google Play store (eller Github). Den er for gammel til å snakke med denne radioen. + Ingen (slå av) + Kort rekkevidde (men rask) + Medium rekkevidde (men rask) + Lang rekkevidde (men langsommerer) + Veldig lang rekkevidde (men langsom) + IKKE GJENKJENT + Meshtastic Tjeneste meldinger + Du må slå på (høy presisjon) lokasjonstjenester i Android Innstillinger + Om + En liste over noder i nettet + Tekstmeldinger + Denne kanall URL er ugyldig og kan ikke benyttes + \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml new file mode 100644 index 000000000..ebd32f23e --- /dev/null +++ b/app/src/main/res/values-ro/strings.xml @@ -0,0 +1,73 @@ + + + Setări + Numele Canalului + Opțiunile Canalului + Buton de share + Cod QR + Nesetat + Statusul conexiunii + Iconița aplicației + Username Necunoscut + Avatar + hey am găsit cache-ul, este chiar aici lângă marele tigru. Mi-e cam frică. + Trimite textul + Nu conectat vre-un dispozitiv care suportă Meshtastic cu acest telefon. Te rog, conectează un dispozitiv cu acest telefon.\n\n Acesta aplicație open-source se află în alpha-testing, dacă întâmpini probleme contactează-ne: meshtastic.discourse.group.\n\nPentru mai multe informații: - www.meshtastic.org. + Username nesetat + Numele tău + Trimite în mod anonim statistici de utilizare și reporturi de crash. + Caut dispozitive Meshtastic... + Această aplicație necesită access la bluetooth. Te rog acordă accesul în setările telefonului. + Eroare - această aplicație necesită bluetooth + Încep să mă conectez + Conectare eșuată + Un URL pentru a intră în rețeaua Meshtastic + Accept + Renunta + Schimbă canalul + 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. + Am primit un nou URL de canal + Vrei să faci schimbul \'%s\' canalului? + 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 + O permisiune necesară lipsește, Meshtastic nu o să funcționeze corespunzător. Te rugăm activează-o în setările Android. + Nu am putut să schimb canalul deoarece dispozitivul era în sleep mode + Raportează Bug + Raportează un bug + Ești sigur că vrei să raportezi un bug? După ce ai raportat, te rog postează în meshtastic.discourse.group că să reușim să potrivim reportul tău cu ce ai găsit. + Raportare + Selectează un dispozitiv + Ești conectat la dispozitiv-ul %s + Nu ai conectat un dispozitiv încă. + Schimbă dispozitivul + Te rog connecteaza dispozitivul în setările Android. + Conectare reușită, începem serviciul + Conectare eșuată, te rog reselecteaza + Accesul locației este dezactivat, nu putem furniza locația ta la rețea. + Distribuie + Deconectat + Dispozitiv în sleep mode + Connectat: %s din %s online + O lista cu nodurile din rețea + Updateaza firmware-ul + Connectat la dispozitiv + Conectat la dispozitivul (%s) + Neconectat, selectează dispozitivul din lista de jos + Connectat la dispozitivi, dar e în modul de sleep + Updateaza către %s + Aplicație prea veche + Trebuie să updatezi această aplicație de pe Google Play (sau Github). Aplicația este prea veche pentru a comunica cu dispozitivul. + Niciunul (dezactivat) + Rază scurtă (comunicare rapidă) + Rază medie (comunicare rapidă) + Rază lungă (încet) + Rază foarte lungă (încet) + NERECUNOSCUT + Notificările Sistemului Meshtastic + Trepuie să pornești serviciile de locație (de high accuracy) în setările Android + Despre + O lista cu nodurile din rețea + Mesaje Text + Acest URL de canal este invalid și nu poate fi folosit + \ No newline at end of file diff --git a/app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt b/app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt index 24ab9800b..6d5f56317 100644 --- a/app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt +++ b/app/src/test/java/com/geeksville/mesh/ui/UIUnitTest.kt @@ -12,8 +12,11 @@ import org.junit.Test class UIUnitTest { @Test fun initialsGood() { - Assert.assertEquals(getInitials("Kevin Hester"), "KH") - Assert.assertEquals(getInitials(" Kevin Hester Lesser Cat "), "KHL") - Assert.assertEquals(getInitials(" "), "") + Assert.assertEquals("KH", getInitials("Kevin Hester")) + Assert.assertEquals("KHL", getInitials(" Kevin Hester Lesser Cat ")) + Assert.assertEquals("", getInitials(" ")) + Assert.assertEquals("gks", getInitials("geeksville")) + Assert.assertEquals("gee", getInitials("geek")) + Assert.assertEquals("gks", getInitials("geeks")) } }