Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ open class RadioConfigRepositoryImpl(
channel_url = channels.getChannelUrl().toString(),
config = localConfig,
module_config = localModuleConfig,
is_unmessagable = node?.user?.is_unmessagable,
is_licensed = node?.user?.is_licensed,
fixed_position =
if (node != null && localConfig.position?.fixed_position == true) {
node.position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,17 @@ open class InstallProfileUseCase constructor(private val radioController: RadioC
}
}

// is_licensed is deliberately not installed here: enabling ham mode is a dedicated onboarding flow
// (set_ham_mode — rewrites the owner, disables encryption, applies tx power/frequency) that a plain
// set_owner would bypass, leaving the radio flagged licensed without those required side effects.
private suspend fun AdminEditScope.installOwner(profile: DeviceProfile, currentUser: User?) {
if (profile.long_name != null || profile.short_name != null) {
if (profile.long_name != null || profile.short_name != null || profile.is_unmessagable != null) {
currentUser?.let {
setOwner(
it.copy(
long_name = profile.long_name ?: it.long_name,
short_name = profile.short_name ?: it.short_name,
is_unmessagable = profile.is_unmessagable ?: it.is_unmessagable,
),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import org.meshtastic.proto.ModuleConfig.TelemetryConfig
import org.meshtastic.proto.User
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class InstallProfileUseCaseTest {
Expand Down Expand Up @@ -107,4 +108,14 @@ class InstallProfileUseCaseTest {

assertTrue(radioController.editSettingsCalled)
}

@Test
fun `invoke installs is_unmessagable but never auto-installs is_licensed`() = runTest {
val profile = DeviceProfile(is_unmessagable = true, is_licensed = true)

useCase(1234, profile, User(long_name = "Old"))

assertEquals(true, radioController.lastSetOwnerUser?.is_unmessagable)
assertEquals(false, radioController.lastSetOwnerUser?.is_licensed)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class FakeRadioController :

var throwOnSend: Boolean = false
var lastSetDeviceAddress: String? = null
var lastSetOwnerUser: User? = null
var editSettingsCalled = false
var startProvideLocationCalled = false
var stopProvideLocationCalled = false
Expand All @@ -67,6 +68,7 @@ class FakeRadioController :
localConfigs.clear()
throwOnSend = false
lastSetDeviceAddress = null
lastSetOwnerUser = null
editSettingsCalled = false
startProvideLocationCalled = false
stopProvideLocationCalled = false
Expand Down Expand Up @@ -107,7 +109,9 @@ class FakeRadioController :

override suspend fun setLocalChannel(channel: Channel) {}

override suspend fun setOwner(destNum: Int, user: User, packetId: Int) {}
override suspend fun setOwner(destNum: Int, user: User, packetId: Int) {
lastSetOwnerUser = user
}

override suspend fun setHamMode(destNum: Int, hamParameters: HamParameters, packetId: Int) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,29 @@ import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.cancel
import org.meshtastic.core.resources.channel_url
import org.meshtastic.core.resources.fixed_position
import org.meshtastic.core.resources.licensed_amateur_radio
import org.meshtastic.core.resources.long_name
import org.meshtastic.core.resources.module_settings
import org.meshtastic.core.resources.radio_configuration
import org.meshtastic.core.resources.save
import org.meshtastic.core.resources.short_name
import org.meshtastic.core.resources.unmessageable
import org.meshtastic.core.ui.component.MeshtasticDialog
import org.meshtastic.core.ui.component.SwitchPreference
import org.meshtastic.proto.DeviceProfile

private const val UNMESSAGABLE_TAG = 9
private const val LICENSED_TAG = 10

private enum class ProfileField(val tag: Int, val labelRes: StringResource) {
LONG_NAME(1, Res.string.long_name),
SHORT_NAME(2, Res.string.short_name),
CHANNEL_URL(3, Res.string.channel_url),
CONFIG(4, Res.string.radio_configuration),
MODULE_CONFIG(5, Res.string.module_settings),
FIXED_POSITION(6, Res.string.fixed_position),
UNMESSAGABLE(UNMESSAGABLE_TAG, Res.string.unmessageable),
LICENSED(LICENSED_TAG, Res.string.licensed_amateur_radio),
}

@Suppress("LongMethod", "CyclomaticComplexMethod")
Expand All @@ -69,6 +76,8 @@ fun EditDeviceProfileDialog(
ProfileField.CONFIG -> deviceProfile.config != null
ProfileField.MODULE_CONFIG -> deviceProfile.module_config != null
ProfileField.FIXED_POSITION -> deviceProfile.fixed_position != null
ProfileField.UNMESSAGABLE -> deviceProfile.is_unmessagable != null
ProfileField.LICENSED -> deviceProfile.is_licensed != null
}
},
)
Expand Down Expand Up @@ -99,6 +108,9 @@ fun EditDeviceProfileDialog(
} else {
null
},
is_unmessagable =
if (state[ProfileField.UNMESSAGABLE] == true) deviceProfile.is_unmessagable else null,
is_licensed = if (state[ProfileField.LICENSED] == true) deviceProfile.is_licensed else null,
)
onConfirm(result)
},
Expand All @@ -115,6 +127,8 @@ fun EditDeviceProfileDialog(
ProfileField.CONFIG -> deviceProfile.config != null
ProfileField.MODULE_CONFIG -> deviceProfile.module_config != null
ProfileField.FIXED_POSITION -> deviceProfile.fixed_position != null
ProfileField.UNMESSAGABLE -> deviceProfile.is_unmessagable != null
ProfileField.LICENSED -> deviceProfile.is_licensed != null
}
SwitchPreference(
title = stringResource(field.labelRes),
Expand Down
Loading