get ready for translations

This commit is contained in:
geeksville
2020-04-09 11:03:17 -07:00
parent e4bbdf1d1e
commit 5a90a06580
7 changed files with 128 additions and 231 deletions

View File

@@ -34,7 +34,9 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- the xing library will try to bring this permission in but we don't want it -->
<uses-permission android:name="android.permission.CAMERA" tools:node="remove" />
<uses-permission
android:name="android.permission.CAMERA"
tools:node="remove" />
<uses-feature
android:name="android.hardware.bluetooth_le"
@@ -84,6 +86,7 @@
android:name="com.geeksville.mesh.MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -283,6 +283,7 @@ class MainActivity : AppCompatActivity(), Logging,
pager.adapter = tabsAdapter
pager.isUserInputEnabled =
false // Gestures for screen switching doesn't work so good with the map view
// pager.offscreenPageLimit = 0 // Don't keep any offscreen pages around, because we want to make sure our bluetooth scanning stops
TabLayoutMediator(tab_layout, pager) { tab, position ->
// tab.text = tabInfos[position].text // I think it looks better with icons only
tab.icon = getDrawable(tabInfos[position].icon)

View File

@@ -151,6 +151,8 @@ class MessagesFragment : ScreenFragment("Messages"), Logging {
val str = messageInputText.text.toString()
model.messagesState.sendMessage(str)
messageInputText.setText("") // blow away the string the user just entered
// requireActivity().hideKeyboard()
}
messageListView.adapter = messagesAdapter

View File

@@ -13,14 +13,16 @@ import android.os.ParcelUuid
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.RadioButton
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.geeksville.android.Logging
import com.geeksville.android.hideKeyboard
import com.geeksville.mesh.R
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.service.RadioInterfaceService
import com.geeksville.util.exceptionReporter
import kotlinx.android.synthetic.main.settings_fragment.*
@@ -50,97 +52,99 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
var selectedMacAddr: String? = null
val errorText = object : MutableLiveData<String?>(null) {}
val devices = object : LiveData<Map<String, BTScanEntry>>(mapOf()) {
private var scanner: BluetoothLeScanner? = null
private var scanner: BluetoothLeScanner? = null
private val scanCallback = object : ScanCallback() {
override fun onScanFailed(errorCode: Int) {
val msg = "Unexpected bluetooth scan failure: $errorCode"
// error code2 seeems to be indicate hung bluetooth stack
errorText.value = msg
}
// For each device that appears in our scan, ask for its GATT, when the gatt arrives,
// check if it is an eligable device and store it in our list of candidates
// if that device later disconnects remove it as a candidate
override fun onScanResult(callbackType: Int, result: ScanResult) {
val addr = result.device.address
// prevent logspam because weill get get lots of redundant scan results
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
val oldDevs = value!!
val oldEntry = oldDevs[addr]
if (oldEntry == null || oldEntry.bonded != isBonded) {
val entry = BTScanEntry(
result.device.name,
addr,
isBonded
)
debug("onScanResult ${entry}")
// If nothing was selected, by default select the first thing we see
if (selectedMacAddr == null && entry.bonded)
changeSelection(context, addr)
value = oldDevs + Pair(addr, entry) // trigger gui updates
}
}
private val scanCallback = object : ScanCallback() {
override fun onScanFailed(errorCode: Int) {
val msg = "Unexpected bluetooth scan failure: $errorCode"
// error code2 seeems to be indicate hung bluetooth stack
errorText.value = msg
}
private fun stopScan() {
if (scanner != null) {
debug("stopping scan")
try {
scanner?.stopScan(scanCallback)
} catch (ex: Throwable) {
warn("Ignoring error stopping scan, probably BT adapter was disabled suddenly: ${ex.message}")
}
scanner = null
}
}
// For each device that appears in our scan, ask for its GATT, when the gatt arrives,
// check if it is an eligable device and store it in our list of candidates
// if that device later disconnects remove it as a candidate
override fun onScanResult(callbackType: Int, result: ScanResult) {
private fun startScan() {
debug("BTScan component active")
selectedMacAddr = RadioInterfaceService.getBondedDeviceAddress(context)
if (bluetoothAdapter == null) {
warn("No bluetooth adapter. Running under emulation?")
val testnodes = listOf(
BTScanEntry("Meshtastic_ab12", "xx", false),
BTScanEntry("Meshtastic_32ac", "xb", true)
val addr = result.device.address
// prevent logspam because weill get get lots of redundant scan results
val isBonded = result.device.bondState == BluetoothDevice.BOND_BONDED
val oldDevs = devices.value!!
val oldEntry = oldDevs[addr]
if (oldEntry == null || oldEntry.bonded != isBonded) {
val entry = BTScanEntry(
result.device.name,
addr,
isBonded
)
value = (testnodes.map { it.macAddress to it }).toMap()
debug("onScanResult ${entry}")
// If nothing was selected, by default select the first thing we see
if (selectedMacAddr == null)
changeSelection(context, testnodes.first().macAddress)
} else {
/// The following call might return null if the user doesn't have bluetooth access permissions
val s: BluetoothLeScanner? = bluetoothAdapter.bluetoothLeScanner
if (selectedMacAddr == null && entry.bonded)
changeSelection(context, addr)
if (s == null) {
errorText.value =
"This application requires bluetooth access. Please grant access in android settings."
} else {
debug("starting scan")
// filter and only accept devices that have a sw update service
val filter =
ScanFilter.Builder()
.setServiceUuid(ParcelUuid(RadioInterfaceService.BTM_SERVICE_UUID))
.build()
val settings =
ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
s.startScan(listOf(filter), settings, scanCallback)
scanner = s
}
devices.value = oldDevs + Pair(addr, entry) // trigger gui updates
}
}
}
fun stopScan() {
if (scanner != null) {
debug("stopping scan")
try {
scanner?.stopScan(scanCallback)
} catch (ex: Throwable) {
warn("Ignoring error stopping scan, probably BT adapter was disabled suddenly: ${ex.message}")
}
scanner = null
}
}
fun startScan() {
debug("BTScan component active")
selectedMacAddr = RadioInterfaceService.getBondedDeviceAddress(context)
if (bluetoothAdapter == null) {
warn("No bluetooth adapter. Running under emulation?")
val testnodes = listOf(
BTScanEntry("Meshtastic_ab12", "xx", false),
BTScanEntry("Meshtastic_32ac", "xb", true)
)
devices.value = (testnodes.map { it.macAddress to it }).toMap()
// If nothing was selected, by default select the first thing we see
if (selectedMacAddr == null)
changeSelection(context, testnodes.first().macAddress)
} else {
/// The following call might return null if the user doesn't have bluetooth access permissions
val s: BluetoothLeScanner? = bluetoothAdapter.bluetoothLeScanner
if (s == null) {
errorText.value =
"This application requires bluetooth access. Please grant access in android settings."
} else {
debug("starting scan")
// filter and only accept devices that have a sw update service
val filter =
ScanFilter.Builder()
.setServiceUuid(ParcelUuid(RadioInterfaceService.BTM_SERVICE_UUID))
.build()
val settings =
ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
s.startScan(listOf(filter), settings, scanCallback)
scanner = s
}
}
}
val devices = object : MutableLiveData<Map<String, BTScanEntry>>(mapOf()) {
/**
* Called when the number of active observers change from 1 to 0.
@@ -157,18 +161,6 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
super.onInactive()
stopScan()
}
/**
* Called when the number of active observers change to 1 from 0.
*
*
* This callback can be used to know that this LiveData is being used thus should be kept
* up to date.
*/
override fun onActive() {
super.onActive()
startScan()
}
}
/// Called by the GUI when a new device has been selected by the user
@@ -230,6 +222,7 @@ class BTScanModel(app: Application) : AndroidViewModel(app), Logging {
class SettingsFragment : ScreenFragment("Settings"), Logging {
private val scanModel: BTScanModel by activityViewModels()
private val model: UIViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -241,6 +234,24 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.ownerName.observe(viewLifecycleOwner, Observer { name ->
usernameEditText.setText(name)
})
usernameEditText.on(EditorInfo.IME_ACTION_DONE) {
debug("did IME action")
val n = usernameEditText.text.toString().trim()
if (n.isNotEmpty())
model.setOwner(requireContext(), n)
requireActivity().hideKeyboard()
}
analyticsOkayCheckbox.setOnCheckedChangeListener { _, isChecked ->
// FIXME, preserve this in settings
analyticsOkayCheckbox.isChecked = true // so users will complain and I'll fix the bug
}
scanModel.errorText.observe(viewLifecycleOwner, Observer { errMsg ->
if (errMsg != null) {
scanStatusText.text = errMsg
@@ -251,10 +262,7 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
// Remove the old radio buttons and repopulate
deviceRadioGroup.removeAllViews()
var hasBonded = false // Have any of our devices been bonded
devices.values.forEach { device ->
hasBonded = hasBonded || device.bonded
val b = RadioButton(requireActivity())
b.text = device.name
b.id = View.generateViewId()
@@ -268,140 +276,21 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
}
}
val hasBonded = RadioInterfaceService.getBondedDeviceAddress(requireContext()) != null
// get rid of the warning text once at least one device is paired
warningNotPaired.visibility = if (hasBonded) View.GONE else View.VISIBLE
})
}
}
override fun onPause() {
super.onPause()
scanModel.stopScan()
}
/*
import androidx.compose.Composable
import androidx.compose.state
import androidx.ui.core.ContextAmbient
import androidx.ui.foundation.Text
import androidx.ui.input.ImeAction
import androidx.ui.layout.*
import androidx.ui.material.MaterialTheme
import androidx.ui.text.TextStyle
import androidx.ui.tooling.preview.Preview
import androidx.ui.unit.dp
import com.geeksville.android.Logging
import com.geeksville.mesh.model.MessagesState
import com.geeksville.mesh.model.UIState
import com.geeksville.mesh.service.RadioInterfaceService
object SettingsLog : Logging
@Composable
fun SettingsContent() {
//val typography = MaterialTheme.typography()
val context = ContextAmbient.current
Column(modifier = LayoutSize.Fill + LayoutPadding(16.dp)) {
Row {
Text("Your name ", modifier = LayoutGravity.Center)
val name = state { UIState.ownerName }
StyledTextField(
value = name.value,
onValueChange = { name.value = it },
textStyle = TextStyle(
color = palette.onSecondary.copy(alpha = 0.8f)
),
imeAction = ImeAction.Done,
onImeActionPerformed = {
MessagesState.info("did IME action")
val n = name.value.trim()
if (n.isNotEmpty())
UIState.setOwner(context, n)
},
hintText = "Type your name here...",
modifier = LayoutGravity.Center
)
}
BTScanScreen()
val bonded = RadioInterfaceService.getBondedDeviceAddress(context) != null
if (!bonded) {
val typography = MaterialTheme.typography
Text(
text =
"""
You haven't yet paired a Meshtastic compatible radio with this phone.
This application is an early alpha release, if you find problems please post on our website chat.
For more information see our web page - www.meshtastic.org.
""".trimIndent(), style = typography.body2
)
}
override fun onResume() {
super.onResume()
scanModel.startScan()
}
}
*/
/*
@Model
object ScanUIState {
}
/// FIXME, remove once compose has better lifecycle management
object ScanState : Logging {
}
@Composable
fun BTScanScreen() {
val context = ContextAmbient.current
// FIXME - remove onCommit now that we have a fragement to run in
}
Column {
if (ScanUIState.errorText != null) {
Text(text = ScanUIState.errorText!!)
} else {
if (ScanUIState.devices.isEmpty()) {
Text(
text = "Looking for Meshtastic devices... (zero found)",
modifier = LayoutGravity.Center
)
CircularProgressIndicator() // Show that we are searching still
} else {
// val allPaired = bluetoothAdapter?.bondedDevices.orEmpty().map { it.address }.toSet()
RadioGroup {
Column {
ScanUIState.devices.values.forEach {
// disabled pending https://issuetracker.google.com/issues/149528535
ProvideEmphasis(emphasis = if (it.bonded) MaterialTheme.emphasisLevels.high else MaterialTheme.emphasisLevels.disabled) {
RadioGroupTextItem(
selected = (it.isSelected),
onSelect = {
},
text = it.name
)
}
}
}
}
}
}
}
}
*/

View File

@@ -31,9 +31,11 @@
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/usernameEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/username_unset" />
android:singleLine="true"
android:imeOptions="actionDone" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textview.MaterialTextView

View File

@@ -1,5 +1,5 @@
<resources>
<string name="app_name">Meshtastic</string>
<string name="app_name" translatable="false">Meshtastic</string>
<string name="action_settings">Settings</string>
<string name="channel_name">Channel Name</string>
<string name="channel_options">Channel options</string>
@@ -10,7 +10,7 @@
<string name="application_icon">application icon</string>
<string name="unknown_username">Unknown Username</string>
<string name="user_avatar">User avatar</string>
<string name="sample_distance">2.13 km</string>
<string name="sample_distance" translatable="false">2.13 km</string>
<string name="sample_message">hey I found the cache, it is over here next to the big tiger. I\'m kinda scared.</string>
<string name="some_username">Some Username</string>
<string name="send_text">Send Text</string>
@@ -19,6 +19,6 @@
<string name="your_name">Your Name</string>
<string name="analytics_okay">Anonymous usage statistics and crash reports.</string>
<string name="looking_for_meshtastic_devices">Looking for Meshtastic devices...</string>
<string name="test__devname1">Meshtastic_ac23</string>
<string name="test_devname2">Meshtastic_1267</string>
<string name="test__devname1" translatable="false">Meshtastic_ac23</string>
<string name="test_devname2" translatable="false">Meshtastic_1267</string>
</resources>