diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9f8787afb..7ff5c0925 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -60,21 +60,26 @@ jobs:
rm ./app/src/main/res/values/curfirmwareversion.xml
echo -e "\n ${{ env.CUR_FIRMWARE_VERSION }}\n ${{ env.SHORT_FIRMWARE_VERSION }}\n" > ./app/src/main/res/values/curfirmwareversion.xml
- - name: Fetch firmware release
- uses: dsaltares/fetch-gh-release-asset@master
- with:
- repo: meshtastic/Meshtastic-device
- file: firmware-${{ env.CUR_FIRMWARE_VERSION }}.zip
- version: tags/${{ steps.firmware_version.outputs.tag }}
- target: firmware.zip
- token: ${{ secrets.GITHUB_TOKEN }}
+# - name: Fetch firmware release
+# uses: dsaltares/fetch-gh-release-asset@master
+# with:
+# repo: meshtastic/Meshtastic-device
+# file: firmware-${{ env.CUR_FIRMWARE_VERSION }}.zip
+# version: tags/${{ steps.firmware_version.outputs.tag }}
+# target: firmware.zip
+# token: ${{ secrets.GITHUB_TOKEN }}
- - name: Unzip firmware into assets/firmware
+# - name: Unzip firmware into assets/firmware
+# run: |
+# rm -rf ./app/src/main/assets/firmware
+# mkdir -p ./app/src/main/assets/firmware
+# unzip -qq ./firmware.zip 'littlefs-*.bin' 'firmware-heltec*.bin' 'firmware-tbeam*.bin' 'firmware-tlora*.bin' 'firmware-nano*.bin' -d ./app/src/main/assets/firmware
+# rm ./firmware.zip
+
+ - name: Mock firmware assets for release
run: |
rm -rf ./app/src/main/assets/firmware
mkdir -p ./app/src/main/assets/firmware
- unzip -qq ./firmware.zip 'littlefs-*.bin' 'firmware-heltec*.bin' 'firmware-tbeam*.bin' 'firmware-tlora*.bin' 'firmware-nano*.bin' -d ./app/src/main/assets/firmware
- rm ./firmware.zip
- name: Validate Gradle wrapper
uses: gradle/wrapper-validation-action@v1
diff --git a/app/build.gradle b/app/build.gradle
index b10034946..eac5e97d0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -43,8 +43,8 @@ android {
applicationId "com.geeksville.mesh"
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
targetSdkVersion 30 // 30 can't work until an explicit location permissions dialog is added
- versionCode 20338 // format is Mmmss (where M is 1+the numeric major number
- versionName "1.3.38"
+ versionCode 20339 // format is Mmmss (where M is 1+the numeric major number
+ versionName "1.3.39"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// per https://developer.android.com/studio/write/vector-asset-studio
diff --git a/app/src/main/java/com/geeksville/mesh/BaseActivity.kt b/app/src/main/java/com/geeksville/mesh/BaseActivity.kt
index 1556dbd05..205a20b25 100644
--- a/app/src/main/java/com/geeksville/mesh/BaseActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/BaseActivity.kt
@@ -37,12 +37,12 @@ open class BaseActivity: AppCompatActivity(), Logging {
}
}
- private fun createLocale(language: String):Locale {
- var langArray = language.split("_")
- if (langArray.size == 2) {
- return Locale(langArray[0], langArray[1]);
+ private fun createLocale(language: String): Locale {
+ val langArray = language.split("_")
+ return if (langArray.size == 2) {
+ Locale(langArray[0], langArray[1])
} else {
- return Locale(langArray[0]);
+ Locale(langArray[0])
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
index e38b7d5d7..8f0263508 100644
--- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt
+++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt
@@ -409,11 +409,10 @@ class MainActivity : BaseActivity(), Logging,
installSplashScreen()
super.onCreate(savedInstanceState)
- if (preferences.getBoolean("app_intro_completed", false) == false) {
+ if (!preferences.getBoolean("app_intro_completed", false)) {
startActivity(Intent(this, AppIntroduction::class.java))
}
-
binding = ActivityMainBinding.inflate(layoutInflater)
val prefs = UIViewModel.getPreferences(this)
diff --git a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
index 3a9ddf10f..6915ef4a5 100644
--- a/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
+++ b/app/src/main/java/com/geeksville/mesh/android/ContextServices.kt
@@ -1,7 +1,6 @@
package com.geeksville.mesh.android
import android.Manifest
-import android.annotation.SuppressLint
import android.app.NotificationManager
import android.bluetooth.BluetoothManager
import android.companion.CompanionDeviceManager
@@ -19,7 +18,6 @@ import com.geeksville.mesh.MainActivity
val Context.bluetoothManager: BluetoothManager? get() = getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager?
val Context.deviceManager: CompanionDeviceManager?
- @SuppressLint("InlinedApi")
get() {
if (GeeksvilleApplication.currentActivity is MainActivity) {
val activity = GeeksvilleApplication.currentActivity
@@ -47,7 +45,7 @@ fun Context.hasGps(): Boolean =
packageManager.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS)
/**
- * return app install source (sideload = null)
+ * return app install source (play store = com.android.vending)
*/
fun Context.installSource(): String? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
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 b183feaef..d2711e238 100644
--- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt
+++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt
@@ -71,13 +71,10 @@ class UIViewModel @Inject constructor(
private val _allPacketState = MutableStateFlow>(emptyList())
val allPackets: StateFlow> = _allPacketState
- private val _localConfig = MutableLiveData()
- val localConfig: LiveData get() = _localConfig
+ private val _localConfig = MutableLiveData()
+ val localConfig: LiveData get() = _localConfig
- private val _quickChatActions =
- MutableStateFlow>(
- emptyList()
- )
+ private val _quickChatActions = MutableStateFlow>(emptyList())
val quickChatActions: StateFlow> = _quickChatActions
init {
diff --git a/app/src/main/java/com/geeksville/mesh/repository/datastore/LocalConfigRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/datastore/LocalConfigRepository.kt
index 972e4dfc0..429125692 100644
--- a/app/src/main/java/com/geeksville/mesh/repository/datastore/LocalConfigRepository.kt
+++ b/app/src/main/java/com/geeksville/mesh/repository/datastore/LocalConfigRepository.kt
@@ -61,6 +61,7 @@ class LocalConfigRepository @Inject constructor(
if (config.hasWifi()) setWifiConfig(config.wifi)
if (config.hasDisplay()) setDisplayConfig(config.display)
if (config.hasLora()) setLoraConfig(config.lora)
+ if (config.hasBluetooth()) setBluetoothConfig(config.bluetooth)
}
private suspend fun setDeviceConfig(config: ConfigProtos.Config.DeviceConfig) {
@@ -99,6 +100,12 @@ class LocalConfigRepository @Inject constructor(
}
}
+ private suspend fun setBluetoothConfig(config: ConfigProtos.Config.BluetoothConfig) {
+ localConfigStore.updateData { preference ->
+ preference.toBuilder().setBluetooth(config).build()
+ }
+ }
+
suspend fun fetchInitialLocalConfig() = localConfigStore.data.first()
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt
index aaa9397ec..501bed195 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/AdvancedSettingsFragment.kt
@@ -116,6 +116,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
.setNeutralButton(R.string.cancel) { _, _ ->
}
.setPositiveButton(getString(R.string.okay)) { _, _ ->
+ debug("User clicked requestShutdown")
model.requestShutdown()
}
.show()
@@ -127,6 +128,7 @@ class AdvancedSettingsFragment : ScreenFragment("Advanced Settings"), Logging {
.setNeutralButton(R.string.cancel) { _, _ ->
}
.setPositiveButton(getString(R.string.okay)) { _, _ ->
+ debug("User clicked requestReboot")
model.requestReboot()
}
.show()
diff --git a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
index efebd43dd..bd4fe6a35 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/ChannelFragment.kt
@@ -304,49 +304,62 @@ class ChannelFragment : ScreenFragment("Channel"), Logging {
}
} else {
// User just locked it, we should warn and then apply changes to radio
- MaterialAlertDialogBuilder(requireContext())
- .setTitle(R.string.change_channel)
- .setMessage(R.string.are_you_sure_channel)
- .setNeutralButton(R.string.cancel) { _, _ ->
- setGUIfromModel()
+
+ model.channels.value?.primaryChannel?.let { oldPrimary ->
+ var newSettings = oldPrimary.settings.toBuilder()
+ val newName = binding.channelNameEdit.text.toString().trim()
+
+ // Find the new modem config
+ val selectedModemPresetString =
+ binding.filledExposedDropdown.editableText.toString()
+ var newModemPreset = getModemPreset(selectedModemPresetString)
+ if (newModemPreset == ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED) // Huh? didn't find it - keep same
+ newModemPreset = oldPrimary.loraConfig.modemPreset
+
+ // Generate a new AES256 key if the user changes channel name or the name is non-default and the settings changed
+ val shouldUseRandomKey =
+ newName != originalName || (newName.isNotEmpty() && newModemPreset != oldPrimary.loraConfig.modemPreset)
+ if (shouldUseRandomKey) {
+
+ // Install a new customized channel
+ debug("ASSIGNING NEW AES256 KEY")
+ val random = SecureRandom()
+ val bytes = ByteArray(32)
+ random.nextBytes(bytes)
+ newSettings.name = newName.take(11) // proto max_size:12
+ newSettings.psk = ByteString.copyFrom(bytes)
+ } else {
+ debug("Switching back to default channel")
+ newSettings = Channel.default.settings.toBuilder()
}
- .setPositiveButton(getString(R.string.accept)) { _, _ ->
- // Generate a new channel with only the changes the user can change in the GUI
- model.channels.value?.primaryChannel?.let { oldPrimary ->
- var newSettings = oldPrimary.settings.toBuilder()
- val newName = binding.channelNameEdit.text.toString().trim()
- // Find the new modem config
- val selectedModemPresetString =
- binding.filledExposedDropdown.editableText.toString()
- var newModemPreset = getModemPreset(selectedModemPresetString)
- if (newModemPreset == ConfigProtos.Config.LoRaConfig.ModemPreset.UNRECOGNIZED) // Huh? didn't find it - keep same
- newModemPreset = oldPrimary.loraConfig.modemPreset
+ // No matter what apply the speed selection from the user
+ val newLoRaConfig = ConfigProtos.Config.LoRaConfig.newBuilder()
+ .setRegion(model.region)
+ .setModemPreset(newModemPreset)
- // Generate a new AES256 key if the user changes channel name or the name is non-default and the settings changed
- if (newName != originalName || (newName.isNotEmpty() && newModemPreset != oldPrimary.loraConfig.modemPreset)) {
+ val humanName = Channel(newSettings.build(), newLoRaConfig.build()).humanName
+ binding.channelNameEdit.setText(humanName)
- // Install a new customized channel
- debug("ASSIGNING NEW AES256 KEY")
- val random = SecureRandom()
- val bytes = ByteArray(32)
- random.nextBytes(bytes)
- newSettings.name = newName.take(11) // proto max_size:12
- newSettings.psk = ByteString.copyFrom(bytes)
- } else {
- debug("Switching back to default channel")
- newSettings = Channel.default.settings.toBuilder()
- }
+ val message = buildString {
+ append(getString(R.string.are_you_sure_channel))
+ if (!shouldUseRandomKey)
+ append("\n\n" + getString(R.string.warning_default_psk).format(humanName))
+ }
- // No matter what apply the speed selection from the user
- val newLoRaConfig = ConfigProtos.Config.LoRaConfig.newBuilder()
- .setRegion(model.region)
- .setModemPreset(newModemPreset)
-
- installSettings(newSettings.build(),newLoRaConfig.build())
+ MaterialAlertDialogBuilder(requireContext())
+ .setTitle(R.string.change_channel)
+ .setMessage(message)
+ .setNeutralButton(R.string.cancel) { _, _ ->
+ setGUIfromModel()
}
- }
- .show()
+ .setPositiveButton(getString(R.string.accept)) { _, _ ->
+ // Generate a new channel with only the changes the user can change in the GUI
+
+ installSettings(newSettings.build(), newLoRaConfig.build())
+ }
+ .show()
+ }
}
onEditingChanged() // update GUI on what user is allowed to edit/share
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 3326883c6..dff076719 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/SettingsFragment.kt
@@ -281,7 +281,17 @@ class SettingsFragment : ScreenFragment("Settings"), Logging {
}
model.localConfig.observe(viewLifecycleOwner) {
- updateNodeInfo()
+ if (!model.isConnected()) {
+ val configCount = it.allFields.size
+ binding.scanStatusText.text = "Device config ($configCount / 7)"
+ } else updateNodeInfo()
+ }
+
+ model.channels.observe(viewLifecycleOwner) {
+ if (!model.isConnected()) {
+ val channelCount = it?.protobuf?.settingsCount ?: 0
+ binding.scanStatusText.text = "Channels ($channelCount / 8)"
+ }
}
// Also watch myNodeInfo because it might change later
diff --git a/app/src/main/play/release-notes/en-US/alpha.txt b/app/src/main/play/release-notes/en-US/alpha.txt
index e7baf1655..e4da04080 100644
--- a/app/src/main/play/release-notes/en-US/alpha.txt
+++ b/app/src/main/play/release-notes/en-US/alpha.txt
@@ -1 +1 @@
-Lots of improvements - see meshtastic.discourse.group
+For more information visit meshtastic.org. This application is made by volunteers. We are friendly and actively respond to forum posts with any questions you have. Post at meshtastic.discourse.group and we'll help.
diff --git a/app/src/main/play/release-notes/en-US/beta.txt b/app/src/main/play/release-notes/en-US/beta.txt
index e7baf1655..e4da04080 100644
--- a/app/src/main/play/release-notes/en-US/beta.txt
+++ b/app/src/main/play/release-notes/en-US/beta.txt
@@ -1 +1 @@
-Lots of improvements - see meshtastic.discourse.group
+For more information visit meshtastic.org. This application is made by volunteers. We are friendly and actively respond to forum posts with any questions you have. Post at meshtastic.discourse.group and we'll help.
diff --git a/app/src/main/play/release-notes/en-US/production.txt b/app/src/main/play/release-notes/en-US/production.txt
index e7baf1655..e4da04080 100644
--- a/app/src/main/play/release-notes/en-US/production.txt
+++ b/app/src/main/play/release-notes/en-US/production.txt
@@ -1 +1 @@
-Lots of improvements - see meshtastic.discourse.group
+For more information visit meshtastic.org. This application is made by volunteers. We are friendly and actively respond to forum posts with any questions you have. Post at meshtastic.discourse.group and we'll help.
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 62de45bd5..69004fb23 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -155,6 +155,7 @@
Message
Append to message
Instantly send
+ Empty channel names use the default encryption key (any device on %s can read your messages).
Factory reset
Are you sure you want to factory reset?
This will clear all device configuration you have done.