diff --git a/app/src/main/java/com/geeksville/mesh/model/Channel.kt b/app/src/main/java/com/geeksville/mesh/model/Channel.kt new file mode 100644 index 000000000..ce3e1cc5f --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/model/Channel.kt @@ -0,0 +1,79 @@ +package com.geeksville.mesh.model + +import android.graphics.Bitmap +import android.net.Uri +import android.util.Base64 +import androidx.compose.Model +import com.geeksville.mesh.MeshProtos +import com.google.zxing.BarcodeFormat +import com.google.zxing.MultiFormatWriter +import com.journeyapps.barcodescanner.BarcodeEncoder +import java.net.MalformedURLException + + +@Model +data class Channel( + var name: String, + var modemConfig: MeshProtos.ChannelSettings.ModemConfig, + var settings: MeshProtos.ChannelSettings? = MeshProtos.ChannelSettings.getDefaultInstance() +) { + companion object { + // Placeholder when emulating + val emulated = Channel( + MeshProtos.ChannelSettings.newBuilder().setName("Default") + .setModemConfig(MeshProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128).build() + ) + + const val prefix = "https://www.meshtastic.org/c/" + + private const val base64Flags = Base64.URL_SAFE + Base64.NO_WRAP + + private fun urlToSettings(url: Uri): MeshProtos.ChannelSettings { + val urlStr = url.toString() + val pathRegex = Regex("$prefix(.*)") + val (base64) = pathRegex.find(urlStr)?.destructured + ?: throw MalformedURLException("Not a meshtastic URL") + val bytes = Base64.decode(base64, base64Flags) + + return MeshProtos.ChannelSettings.parseFrom(bytes) + } + } + + constructor(c: MeshProtos.ChannelSettings) : this(c.name, c.modemConfig, c) + + constructor(url: Uri) : this(urlToSettings(url)) + + /// Can this channel be changed right now? + var editable = false + + /// Return an URL that represents the current channel values + fun getChannelUrl(): Uri { + // If we have a valid radio config use it, othterwise use whatever we have saved in the prefs + + val channelBytes = settings?.toByteArray() ?: ByteArray(0) // if unset just use empty + val enc = Base64.encodeToString(channelBytes, base64Flags) + + return Uri.parse("$prefix$enc") + } + + fun getChannelQR(): Bitmap { + val multiFormatWriter = MultiFormatWriter() + + val bitMatrix = + multiFormatWriter.encode(getChannelUrl().toString(), BarcodeFormat.QR_CODE, 192, 192); + val barcodeEncoder = BarcodeEncoder() + return barcodeEncoder.createBitmap(bitMatrix) + } +} + + +/** + * a nice readable description of modem configs + */ +fun MeshProtos.ChannelSettings.ModemConfig.toHumanString(): String = when (this) { + MeshProtos.ChannelSettings.ModemConfig.Bw125Cr45Sf128 -> "Medium range (but fast)" + MeshProtos.ChannelSettings.ModemConfig.Bw500Cr45Sf128 -> "Short range (but fast)" + MeshProtos.ChannelSettings.ModemConfig.Bw31_25Cr48Sf512 -> "Long range (but slower)" + MeshProtos.ChannelSettings.ModemConfig.Bw125Cr48Sf4096 -> "Very long range (but slow)" + else -> this.toString() +} 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 36c6fc195..3cc5a120d 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -2,90 +2,15 @@ package com.geeksville.mesh.model import android.content.Context import android.content.SharedPreferences -import android.graphics.Bitmap import android.net.Uri import android.os.RemoteException -import android.util.Base64 -import androidx.compose.Model import androidx.compose.mutableStateOf import androidx.core.content.edit import com.geeksville.android.BuildUtils.isEmulator import com.geeksville.android.Logging import com.geeksville.mesh.IMeshService import com.geeksville.mesh.MeshProtos -import com.geeksville.mesh.MeshProtos.ChannelSettings.ModemConfig import com.geeksville.mesh.ui.getInitials -import com.google.zxing.BarcodeFormat -import com.google.zxing.MultiFormatWriter -import com.journeyapps.barcodescanner.BarcodeEncoder -import java.net.MalformedURLException - -@Model -data class Channel( - var name: String, - var modemConfig: ModemConfig, - var settings: MeshProtos.ChannelSettings? = MeshProtos.ChannelSettings.getDefaultInstance() -) { - companion object { - // Placeholder when emulating - val emulated = Channel( - MeshProtos.ChannelSettings.newBuilder().setName("Default") - .setModemConfig(ModemConfig.Bw125Cr45Sf128).build() - ) - - const val prefix = "https://www.meshtastic.org/c/" - - private const val base64Flags = Base64.URL_SAFE + Base64.NO_WRAP - - private fun urlToSettings(url: Uri): MeshProtos.ChannelSettings { - val urlStr = url.toString() - val pathRegex = Regex("$prefix(.*)") - val (base64) = pathRegex.find(urlStr)?.destructured - ?: throw MalformedURLException("Not a meshtastic URL") - val bytes = Base64.decode(base64, base64Flags) - - return MeshProtos.ChannelSettings.parseFrom(bytes) - } - } - - constructor(c: MeshProtos.ChannelSettings) : this(c.name, c.modemConfig, c) - - constructor(url: Uri) : this(urlToSettings(url)) - - /// Can this channel be changed right now? - var editable = false - - /// Return an URL that represents the current channel values - fun getChannelUrl(): Uri { - // If we have a valid radio config use it, othterwise use whatever we have saved in the prefs - - val channelBytes = settings?.toByteArray() ?: ByteArray(0) // if unset just use empty - val enc = Base64.encodeToString(channelBytes, base64Flags) - - return Uri.parse("$prefix$enc") - } - - fun getChannelQR(): Bitmap { - val multiFormatWriter = MultiFormatWriter() - - val bitMatrix = - multiFormatWriter.encode(getChannelUrl().toString(), BarcodeFormat.QR_CODE, 192, 192); - val barcodeEncoder = BarcodeEncoder() - return barcodeEncoder.createBitmap(bitMatrix) - } -} - - -/** - * a nice readable description of modem configs - */ -fun ModemConfig.toHumanString(): String = when (this) { - ModemConfig.Bw125Cr45Sf128 -> "Medium range (but fast)" - ModemConfig.Bw500Cr45Sf128 -> "Short range (but fast)" - ModemConfig.Bw31_25Cr48Sf512 -> "Long range (but slower)" - ModemConfig.Bw125Cr48Sf4096 -> "Very long range (but slow)" - else -> this.toString() -} /// FIXME - figure out how to merge this staate with the AppStatus Model object UIState : Logging {