mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-02-05 13:22:59 -05:00
get ready for translations
This commit is contained in:
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Submodule geeksville-androidlib updated: 953a657184...ebc40c05fd
Reference in New Issue
Block a user