2021-02-27 12:04:44 +08:00
|
|
|
package com.geeksville.mesh.model
|
|
|
|
|
|
|
|
|
|
import android.graphics.Bitmap
|
|
|
|
|
import android.net.Uri
|
|
|
|
|
import android.util.Base64
|
2023-10-07 08:22:12 -03:00
|
|
|
import com.geeksville.mesh.AppOnlyProtos.ChannelSet
|
|
|
|
|
import com.geeksville.mesh.android.BuildUtils.errormsg
|
2021-02-27 12:04:44 +08:00
|
|
|
import com.google.zxing.BarcodeFormat
|
|
|
|
|
import com.google.zxing.MultiFormatWriter
|
|
|
|
|
import com.journeyapps.barcodescanner.BarcodeEncoder
|
|
|
|
|
import java.net.MalformedURLException
|
2023-10-07 08:22:12 -03:00
|
|
|
import kotlin.jvm.Throws
|
2021-02-27 12:04:44 +08:00
|
|
|
|
2023-10-07 08:22:12 -03:00
|
|
|
internal const val URL_PREFIX = "https://meshtastic.org/e/#"
|
|
|
|
|
private const val BASE64FLAGS = Base64.URL_SAFE + Base64.NO_WRAP + Base64.NO_PADDING
|
2021-02-27 12:04:44 +08:00
|
|
|
|
2023-10-07 08:22:12 -03:00
|
|
|
/**
|
|
|
|
|
* Return a [ChannelSet] that represents the URL
|
|
|
|
|
* @throws MalformedURLException when not recognized as a valid Meshtastic URL
|
|
|
|
|
*/
|
|
|
|
|
@Throws(MalformedURLException::class)
|
|
|
|
|
fun Uri.toChannelSet(): ChannelSet {
|
|
|
|
|
val urlStr = this.toString()
|
2021-02-27 12:04:44 +08:00
|
|
|
|
2023-10-07 08:22:12 -03:00
|
|
|
val pathRegex = Regex("$URL_PREFIX(.*)", RegexOption.IGNORE_CASE)
|
|
|
|
|
val (base64) = pathRegex.find(urlStr)?.destructured
|
|
|
|
|
?: throw MalformedURLException("Not a Meshtastic URL: ${urlStr.take(40)}")
|
|
|
|
|
val bytes = Base64.decode(base64, BASE64FLAGS)
|
2021-02-27 12:04:44 +08:00
|
|
|
|
2023-10-07 08:22:12 -03:00
|
|
|
return ChannelSet.parseFrom(bytes)
|
|
|
|
|
}
|
2021-04-05 15:52:26 +08:00
|
|
|
|
2023-10-07 08:22:12 -03:00
|
|
|
/**
|
|
|
|
|
* Return the primary channel info
|
|
|
|
|
*/
|
|
|
|
|
val ChannelSet.primaryChannel: Channel?
|
|
|
|
|
get() = if (settingsCount > 0) Channel(getSettings(0), loraConfig) else null
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return a URL that represents the [ChannelSet]
|
|
|
|
|
* @param upperCasePrefix portions of the URL can be upper case to make for more efficient QR codes
|
|
|
|
|
*/
|
|
|
|
|
fun ChannelSet.getChannelUrl(upperCasePrefix: Boolean = false): Uri {
|
|
|
|
|
val channelBytes = this.toByteArray() ?: ByteArray(0) // if unset just use empty
|
|
|
|
|
val enc = Base64.encodeToString(channelBytes, BASE64FLAGS)
|
|
|
|
|
val p = if (upperCasePrefix) URL_PREFIX.uppercase() else URL_PREFIX
|
|
|
|
|
return Uri.parse("$p$enc")
|
2021-02-27 12:04:44 +08:00
|
|
|
}
|
2021-04-05 15:52:26 +08:00
|
|
|
|
2023-10-07 08:22:12 -03:00
|
|
|
val ChannelSet.qrCode: Bitmap?
|
|
|
|
|
get() = try {
|
|
|
|
|
val multiFormatWriter = MultiFormatWriter()
|
|
|
|
|
|
|
|
|
|
val bitMatrix =
|
|
|
|
|
multiFormatWriter.encode(
|
|
|
|
|
getChannelUrl(false).toString(),
|
|
|
|
|
BarcodeFormat.QR_CODE,
|
|
|
|
|
960,
|
|
|
|
|
960
|
|
|
|
|
)
|
|
|
|
|
val barcodeEncoder = BarcodeEncoder()
|
|
|
|
|
barcodeEncoder.createBitmap(bitMatrix)
|
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
|
errormsg("URL was too complex to render as barcode")
|
|
|
|
|
null
|
|
|
|
|
}
|