showing real channel data works

pull/8/head
geeksville 2020-03-15 16:30:12 -07:00
rodzic 6ce859a952
commit 36b2da72e4
5 zmienionych plików z 171 dodań i 132 usunięć

Wyświetl plik

@ -10,11 +10,37 @@ import androidx.core.content.edit
import com.geeksville.android.Logging import com.geeksville.android.Logging
import com.geeksville.mesh.IMeshService import com.geeksville.mesh.IMeshService
import com.geeksville.mesh.MeshProtos import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.MeshProtos.ChannelSettings.ModemConfig
import com.geeksville.mesh.ui.getInitials import com.geeksville.mesh.ui.getInitials
import com.google.zxing.BarcodeFormat import com.google.zxing.BarcodeFormat
import com.google.zxing.MultiFormatWriter import com.google.zxing.MultiFormatWriter
import com.journeyapps.barcodescanner.BarcodeEncoder import com.journeyapps.barcodescanner.BarcodeEncoder
data class Channel(
val name: String,
val num: Int,
val modemConfig: ModemConfig = ModemConfig.Bw125Cr45Sf128
) {
companion object {
// Placeholder when emulating
val emulated = Channel("Default", 7)
}
constructor(c: MeshProtos.ChannelSettings) : this(c.name, c.channelNum, c.modemConfig) {
}
}
/**
* 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 /// FIXME - figure out how to merge this staate with the AppStatus Model
object UIState : Logging { object UIState : Logging {
@ -34,6 +60,8 @@ object UIState : Logging {
/// our activity will read this from prefs or set it to the empty string /// our activity will read this from prefs or set it to the empty string
var ownerName: String = "MrInIDE Ownername" var ownerName: String = "MrInIDE Ownername"
fun getChannel() = radioConfig.value?.channelSettings?.let { Channel(it) }
/// Return an URL that represents the current channel values /// Return an URL that represents the current channel values
fun getChannelUrl(context: Context): String { fun getChannelUrl(context: Context): String {
// If we have a valid radio config use it, othterwise use whatever we have saved in the prefs // If we have a valid radio config use it, othterwise use whatever we have saved in the prefs
@ -52,8 +80,7 @@ object UIState : Logging {
} }
} }
fun getChannelQR(context: Context): Bitmap fun getChannelQR(context: Context): Bitmap {
{
val multiFormatWriter = MultiFormatWriter() val multiFormatWriter = MultiFormatWriter()
val bitMatrix = val bitMatrix =

Wyświetl plik

@ -0,0 +1,89 @@
package com.geeksville.mesh.ui
import android.graphics.Bitmap
import androidx.compose.Composable
import androidx.ui.core.DensityAmbient
import androidx.ui.core.DrawModifier
import androidx.ui.core.Modifier
import androidx.ui.core.toModifier
import androidx.ui.foundation.Box
import androidx.ui.graphics.*
import androidx.ui.graphics.colorspace.ColorSpace
import androidx.ui.graphics.colorspace.ColorSpaces
import androidx.ui.graphics.painter.ImagePainter
import androidx.ui.unit.Density
import androidx.ui.unit.PxSize
import androidx.ui.unit.toRect
/// Stolen from the Compose SimpleImage, replace with their real Image component someday
// TODO(mount, malkov) : remove when RepaintBoundary is a modifier: b/149982905
// This is class and not val because if b/149985596
private object ClipModifier : DrawModifier {
override fun draw(density: Density, drawContent: () -> Unit, canvas: Canvas, size: PxSize) {
canvas.save()
canvas.clipRect(size.toRect())
drawContent()
canvas.restore()
}
}
/// Stolen from the Compose SimpleImage, replace with their real Image component someday
@Composable
fun ScaledImage(
image: Image,
modifier: Modifier = Modifier.None,
tint: Color? = null
) {
with(DensityAmbient.current) {
val imageModifier = ImagePainter(image).toModifier(
scaleFit = ScaleFit.FillMaxDimension,
colorFilter = tint?.let { ColorFilter(it, BlendMode.srcIn) }
)
Box(modifier + ClipModifier + imageModifier)
}
}
/// Borrowed from Compose
class AndroidImage(val bitmap: Bitmap) : Image {
/**
* @see Image.width
*/
override val width: Int
get() = bitmap.width
/**
* @see Image.height
*/
override val height: Int
get() = bitmap.height
override val config: ImageConfig get() = ImageConfig.Argb8888
/**
* @see Image.colorSpace
*/
override val colorSpace: ColorSpace
get() = ColorSpaces.Srgb
/**
* @see Image.hasAlpha
*/
override val hasAlpha: Boolean
get() = bitmap.hasAlpha()
/**
* @see Image.nativeImage
*/
override val nativeImage: NativeImage
get() = bitmap
/**
* @see
*/
override fun prepareToDraw() {
bitmap.prepareToDraw()
}
}

Wyświetl plik

@ -1,164 +1,87 @@
package com.geeksville.mesh.ui package com.geeksville.mesh.ui
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap
import androidx.compose.Composable import androidx.compose.Composable
import androidx.ui.core.* import androidx.ui.core.ContextAmbient
import androidx.ui.foundation.Box import androidx.ui.core.Text
import androidx.ui.graphics.*
import androidx.ui.graphics.colorspace.ColorSpace
import androidx.ui.graphics.colorspace.ColorSpaces
import androidx.ui.graphics.painter.ImagePainter
import androidx.ui.layout.* import androidx.ui.layout.*
import androidx.ui.material.MaterialTheme import androidx.ui.material.MaterialTheme
import androidx.ui.material.OutlinedButton import androidx.ui.material.OutlinedButton
import androidx.ui.material.ripple.Ripple import androidx.ui.material.ripple.Ripple
import androidx.ui.tooling.preview.Preview import androidx.ui.tooling.preview.Preview
import androidx.ui.unit.Density
import androidx.ui.unit.PxSize
import androidx.ui.unit.dp import androidx.ui.unit.dp
import androidx.ui.unit.toRect
import com.geeksville.analytics.DataPair import com.geeksville.analytics.DataPair
import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging import com.geeksville.android.Logging
import com.geeksville.mesh.R import com.geeksville.mesh.R
import com.geeksville.mesh.model.Channel
import com.geeksville.mesh.model.UIState import com.geeksville.mesh.model.UIState
import com.geeksville.mesh.model.toHumanString
/// The Compose IDE preview doesn't like the protobufs
data class Channel(val name: String, val num: Int)
object ChannelLog : Logging object ChannelLog : Logging
/// Borrowed from Compose
class AndroidImage(val bitmap: Bitmap) : Image {
/**
* @see Image.width
*/
override val width: Int
get() = bitmap.width
/**
* @see Image.height
*/
override val height: Int
get() = bitmap.height
override val config: ImageConfig get() = ImageConfig.Argb8888
/**
* @see Image.colorSpace
*/
override val colorSpace: ColorSpace
get() = ColorSpaces.Srgb
/**
* @see Image.hasAlpha
*/
override val hasAlpha: Boolean
get() = bitmap.hasAlpha()
/**
* @see Image.nativeImage
*/
override val nativeImage: NativeImage
get() = bitmap
/**
* @see
*/
override fun prepareToDraw() {
bitmap.prepareToDraw()
}
}
/// Stolen from the Compose SimpleImage, replace with their real Image component someday
// TODO(mount, malkov) : remove when RepaintBoundary is a modifier: b/149982905
// This is class and not val because if b/149985596
private object ClipModifier : DrawModifier {
override fun draw(density: Density, drawContent: () -> Unit, canvas: Canvas, size: PxSize) {
canvas.save()
canvas.clipRect(size.toRect())
drawContent()
canvas.restore()
}
}
/// Stolen from the Compose SimpleImage, replace with their real Image component someday
@Composable
fun ScaledImage(
image: Image,
modifier: Modifier = Modifier.None,
tint: Color? = null
) {
with(DensityAmbient.current) {
val imageModifier = ImagePainter(image).toModifier(
scaleFit = ScaleFit.FillMaxDimension,
colorFilter = tint?.let { ColorFilter(it, BlendMode.srcIn) }
)
Box(modifier + ClipModifier + imageModifier)
}
}
@Composable @Composable
fun ChannelContent(channel: Channel = Channel("Default", 7)) { fun ChannelContent(channel: Channel?) {
analyticsScreen(name = "channel") analyticsScreen(name = "channel")
val typography = MaterialTheme.typography() val typography = MaterialTheme.typography()
val context = ContextAmbient.current val context = ContextAmbient.current
Column(modifier = LayoutSize.Fill + LayoutPadding(16.dp)) { Column(modifier = LayoutSize.Fill + LayoutPadding(16.dp)) {
Text( if (channel != null) {
text = "Channel: ${channel.name}", Text(
modifier = LayoutGravity.Center, text = "Channel: ${channel.name}",
style = typography.h4 modifier = LayoutGravity.Center,
) style = typography.h4
Row(modifier = LayoutGravity.Center) {
// simulated qr code
// val image = imageResource(id = R.drawable.qrcode)
val image = AndroidImage(UIState.getChannelQR(context))
ScaledImage(
image = image,
modifier = LayoutGravity.Center + LayoutSize.Min(200.dp, 200.dp)
) )
Ripple(bounded = false) { Row(modifier = LayoutGravity.Center) {
OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(start = 24.dp), // simulated qr code
onClick = { // val image = imageResource(id = R.drawable.qrcode)
GeeksvilleApplication.analytics.track( val image = AndroidImage(UIState.getChannelQR(context))
"share",
DataPair("content_type", "channel")
) // track how many times users share channels
val sendIntent: Intent = Intent().apply { ScaledImage(
action = Intent.ACTION_SEND image = image,
putExtra(Intent.EXTRA_TEXT, UIState.getChannelUrl(context)) modifier = LayoutGravity.Center + LayoutSize.Min(200.dp, 200.dp)
putExtra(Intent.EXTRA_TITLE, "A URL for joining a Meshtastic mesh") )
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null) Ripple(bounded = false) {
context.startActivity(shareIntent) OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(start = 24.dp),
}) { onClick = {
VectorImage( GeeksvilleApplication.analytics.track(
id = R.drawable.ic_twotone_share_24, "share",
tint = palette.onBackground DataPair("content_type", "channel")
) ) // track how many times users share channels
val sendIntent: Intent = Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, UIState.getChannelUrl(context))
putExtra(Intent.EXTRA_TITLE, "A URL for joining a Meshtastic mesh")
type = "text/plain"
}
val shareIntent = Intent.createChooser(sendIntent, null)
context.startActivity(shareIntent)
}) {
VectorImage(
id = R.drawable.ic_twotone_share_24,
tint = palette.onBackground
)
}
} }
} }
}
Text( Text(
text = "Number: ${channel.num}", text = "Number: ${channel.num}",
modifier = LayoutGravity.Center modifier = LayoutGravity.Center
) )
Text( Text(
text = "Mode: Long range (but slow)", text = "Mode: ${channel.modemConfig.toHumanString()}",
modifier = LayoutGravity.Center modifier = LayoutGravity.Center
) )
}
} }
} }
@ -168,6 +91,6 @@ fun ChannelContent(channel: Channel = Channel("Default", 7)) {
fun previewChannel() { fun previewChannel() {
// another bug? It seems modaldrawerlayout not yet supported in preview // another bug? It seems modaldrawerlayout not yet supported in preview
MaterialTheme(colors = palette) { MaterialTheme(colors = palette) {
ChannelContent() ChannelContent(Channel.emulated)
} }
} }

Wyświetl plik

@ -145,7 +145,7 @@ private fun AppContent(openDrawer: () -> Unit) {
Screen.messages -> MessagesContent() Screen.messages -> MessagesContent()
Screen.settings -> SettingsContent() Screen.settings -> SettingsContent()
Screen.users -> HomeContent() Screen.users -> HomeContent()
Screen.channel -> ChannelContent() Screen.channel -> ChannelContent(UIState.getChannel())
Screen.map -> MapContent() Screen.map -> MapContent()
else -> TODO() else -> TODO()
} }

@ -1 +1 @@
Subproject commit f309ee8f9e9db37daabd7c76da683e052ef62f7a Subproject commit 66e926740acb30518d1fdcb901d1cc0b0d48122c