fix: update channel URL to match channel selection

This commit is contained in:
andrekir 2024-11-25 19:46:34 -03:00 committed by Andre K
parent 65d832ef99
commit 4e9055c9b1
3 changed files with 107 additions and 91 deletions

View file

@ -48,7 +48,6 @@ import com.geeksville.mesh.databinding.ActivityMainBinding
import com.geeksville.mesh.model.BluetoothViewModel
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.model.toChannelSet
import com.geeksville.mesh.service.MeshService
import com.geeksville.mesh.service.MeshServiceNotifications
import com.geeksville.mesh.service.ServiceRepository
@ -303,12 +302,7 @@ class MainActivity : AppCompatActivity(), Logging {
when (appLinkAction) {
Intent.ACTION_VIEW -> {
debug("Asked to open a channel URL - ask user if they want to switch to that channel. If so send the config to the radio")
try {
appLinkData?.let { model.requestChannelSet(it.toChannelSet()) }
} catch (ex: Throwable) {
errormsg("Channel url error: ${ex.message}")
showSnackbar("${getString(R.string.channel_invalid)}: ${ex.message}")
}
appLinkData?.let(model::requestChannelUrl)
// We now wait for the device to connect, once connected, we ask the user if they want to switch to the new channel
}

View file

@ -461,8 +461,11 @@ class UIViewModel @Inject constructor(
private val _requestChannelSet = MutableStateFlow<AppOnlyProtos.ChannelSet?>(null)
val requestChannelSet: StateFlow<AppOnlyProtos.ChannelSet?> get() = _requestChannelSet
fun requestChannelSet(channelSet: AppOnlyProtos.ChannelSet) {
_requestChannelSet.value = channelSet
fun requestChannelUrl(url: Uri) = runCatching {
_requestChannelSet.value = url.toChannelSet()
}.onFailure { ex ->
errormsg("Channel url error: ${ex.message}")
showSnackbar(R.string.channel_invalid)
}
/**

View file

@ -125,7 +125,6 @@ fun ChannelScreen(
) {
val context = LocalContext.current
val focusManager = LocalFocusManager.current
val clipboardManager = LocalClipboardManager.current
val connectionState by viewModel.connectionState.collectAsStateWithLifecycle()
val enabled = connectionState == MeshService.ConnectionState.CONNECTED && !viewModel.isManaged
@ -138,22 +137,21 @@ fun ChannelScreen(
/* Holds selections made by the user for QR generation. */
val channelSelections = rememberSaveable(
saver = listSaver(
save = { stateList -> stateList.toList() },
save = { it.toList() },
restore = { it.toMutableStateList() }
)
) { mutableStateListOf(elements = Array(size = 8, init = { true })) }
val channelUrl = channelSet.getChannelUrl()
val selectedChannelSet = channelSet.copy {
val result = settings.filterIndexed { i, _ -> channelSelections.getOrNull(i) == true }
settings.clear()
settings.addAll(result)
}
val modemPresetName = Channel(loraConfig = channelSet.loraConfig).name
val barcodeLauncher = rememberLauncherForActivityResult(ScanContract()) { result ->
if (result.contents != null) {
try {
viewModel.requestChannelSet(Uri.parse(result.contents).toChannelSet())
} catch (ex: Throwable) {
errormsg("Channel url error: ${ex.message}")
viewModel.showSnackbar(R.string.channel_invalid)
}
viewModel.requestChannelUrl(Uri.parse(result.contents))
}
}
@ -340,64 +338,10 @@ fun ChannelScreen(
}
item {
var valueState by remember(channelUrl) { mutableStateOf(channelUrl) }
val isError = valueState != channelUrl
OutlinedTextField(
value = valueState.toString(),
onValueChange = {
try {
valueState = Uri.parse(it)
channelSet = valueState.toChannelSet()
} catch (ex: Throwable) {
// channelSet failed to update, isError true
}
},
modifier = Modifier.fillMaxWidth(),
EditChannelUrl(
enabled = enabled,
label = { Text("URL") },
isError = isError,
trailingIcon = {
val isUrlEqual = channelUrl == channels.getChannelUrl()
IconButton(onClick = {
when {
isError -> valueState = channelUrl
!isUrlEqual -> viewModel.requestChannelSet(channels)
else -> {
// track how many times users share channels
GeeksvilleApplication.analytics.track(
"share",
DataPair("content_type", "channel")
)
clipboardManager.setText(AnnotatedString(channelUrl.toString()))
}
}
}) {
Icon(
imageVector = when {
isError -> Icons.TwoTone.Close
!isUrlEqual -> Icons.TwoTone.Check
else -> Icons.TwoTone.ContentCopy
},
contentDescription = when {
isError -> "Error"
!isUrlEqual -> stringResource(R.string.send)
else -> "Copy"
},
tint = if (isError) {
MaterialTheme.colors.error
} else {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
)
}
},
maxLines = 1,
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
channelUrl = selectedChannelSet.getChannelUrl(),
onConfirm = viewModel::requestChannelUrl
)
}
@ -413,21 +357,20 @@ fun ChannelScreen(
})
}
if (isEditing) item {
PreferenceFooter(
enabled = enabled,
onCancelClicked = {
focusManager.clearFocus()
showChannelEditor = false
channelSet = channels
},
onSaveClicked = {
focusManager.clearFocus()
// viewModel.setRequestChannelUrl(channelUrl)
sendButton()
})
} else {
item {
item {
if (isEditing) {
PreferenceFooter(
enabled = enabled,
onCancelClicked = {
focusManager.clearFocus()
showChannelEditor = false
channelSet = channels
},
onSaveClicked = {
focusManager.clearFocus()
sendButton()
})
} else {
PreferenceFooter(
enabled = enabled,
negativeText = R.string.reset,
@ -438,7 +381,6 @@ fun ChannelScreen(
positiveText = R.string.scan,
onPositiveClicked = {
focusManager.clearFocus()
// viewModel.setRequestChannelUrl(channelUrl)
if (context.hasCameraPermission()) zxingScan() else requestPermissionAndScan()
})
}
@ -446,6 +388,83 @@ fun ChannelScreen(
}
}
@Suppress("LongMethod")
@Composable
private fun EditChannelUrl(
enabled: Boolean,
channelUrl: Uri,
modifier: Modifier = Modifier,
onConfirm: (Uri) -> Unit
) {
val focusManager = LocalFocusManager.current
val clipboardManager = LocalClipboardManager.current
var valueState by remember(channelUrl) { mutableStateOf(channelUrl) }
var isError by remember { mutableStateOf(false) }
OutlinedTextField(
value = valueState.toString(),
onValueChange = {
isError = runCatching {
valueState = Uri.parse(it)
valueState.toChannelSet()
}.isFailure
},
modifier = modifier.fillMaxWidth(),
enabled = enabled,
label = { Text("URL") },
isError = isError,
trailingIcon = {
val isUrlEqual = valueState == channelUrl
IconButton(onClick = {
when {
isError -> {
isError = false
valueState = channelUrl
}
!isUrlEqual -> {
onConfirm(valueState)
valueState = channelUrl
}
else -> {
// track how many times users share channels
GeeksvilleApplication.analytics.track(
"share", DataPair("content_type", "channel")
)
clipboardManager.setText(AnnotatedString(valueState.toString()))
}
}
}) {
Icon(
imageVector = when {
isError -> Icons.TwoTone.Close
!isUrlEqual -> Icons.TwoTone.Check
else -> Icons.TwoTone.ContentCopy
},
contentDescription = when {
isError -> stringResource(R.string.share)
!isUrlEqual -> stringResource(R.string.send)
else -> stringResource(R.string.share)
},
tint = if (isError) {
MaterialTheme.colors.error
} else {
LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
}
)
}
},
maxLines = 1,
singleLine = true,
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Uri, imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
)
}
@Composable
private fun QrCodeImage(
enabled: Boolean,