From 44ebac1758b3daaeb723a289456bb9eb1ae6fb7a Mon Sep 17 00:00:00 2001 From: Kevin Hester Date: Mon, 2 Mar 2020 08:41:16 -0800 Subject: [PATCH] channel qrs are now sharable and real --- TODO.md | 4 +- .../java/com/geeksville/mesh/MainActivity.kt | 6 +-- .../java/com/geeksville/mesh/model/UIState.kt | 46 ++++++++++++++----- .../java/com/geeksville/mesh/ui/Channel.kt | 28 ++++++----- 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/TODO.md b/TODO.md index c5f2c7727..58cf3616b 100644 --- a/TODO.md +++ b/TODO.md @@ -9,8 +9,6 @@ Work items for soon alpha builds * fix app icon in title bar * show direction on the nodeinfo cards * take video of the app -* make channel button look like a button -* generate real channel QR codes * register app link for our URLs https://developer.android.com/studio/write/app-link-indexing.html * let users change & share channels (but no saving them yet) * treat macaddrs as the unique id, not the app layer user id @@ -160,3 +158,5 @@ Don't leave device discoverable. Don't let unpaired users do things with device * have notification (individually maskable) notifications for received texts - use file:///home/kevinh/packages/android-sdk-linux/docs/reference/android/support/v4/app/NotificationCompat.BigTextStyle.html * startforegroundservice only if we have a valid radio * when we select a new radio, restart the service +* make channel button look like a button +* generate real channel QR codes \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/MainActivity.kt b/app/src/main/java/com/geeksville/mesh/MainActivity.kt index ed1d84fdb..f1012e7a9 100644 --- a/app/src/main/java/com/geeksville/mesh/MainActivity.kt +++ b/app/src/main/java/com/geeksville/mesh/MainActivity.kt @@ -179,7 +179,7 @@ class MainActivity : AppCompatActivity(), Logging, override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val prefs = getSharedPreferences("ui-prefs", Context.MODE_PRIVATE) + val prefs = UIState.getPreferences(this) UIState.ownerName = prefs.getString("owner", "")!! UIState.meshService = null @@ -276,9 +276,9 @@ class MainActivity : AppCompatActivity(), Logging, val bytes = UIState.meshService!!.radioConfig val config = MeshProtos.RadioConfig.parseFrom(bytes) - UIState.radioConfig.value = config + UIState.setRadioConfig(this, config) - debug("Read config from radio, channel URL ${UIState.channelUrl}") + debug("Read config from radio") } /// Called when we gain/lose a connection to our mesh radio 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 e1e899792..14c64d2e0 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -1,6 +1,7 @@ package com.geeksville.mesh.model import android.content.Context +import android.content.SharedPreferences import android.graphics.Bitmap import android.os.RemoteException import android.util.Base64 @@ -26,7 +27,7 @@ object UIState : Logging { val isConnected = mutableStateOf(false) /// various radio settings (including the channel) - val radioConfig = mutableStateOf(MeshProtos.RadioConfig.getDefaultInstance()) + private val radioConfig = mutableStateOf(null) /// our name in hte radio /// Note, we generate owner initials automatically for now @@ -34,22 +35,44 @@ object UIState : Logging { var ownerName: String = "MrInIDE Ownername" /// Return an URL that represents the current channel values - val channelUrl - get(): String { - val channelBytes = radioConfig.value.channelSettings.toByteArray() + fun getChannelUrl(context: Context): String { + // If we have a valid radio config use it, othterwise use whatever we have saved in the prefs + val radio = radioConfig.value + if (radio != null) { + val settings = radio.channelSettings + val channelBytes = settings.toByteArray() val enc = Base64.encodeToString(channelBytes, Base64.URL_SAFE + Base64.NO_WRAP) return "https://www.meshtastic.org/c/$enc" + } else { + return getPreferences(context).getString( + "owner", + "https://www.meshtastic.org/c/unset" + )!! } + } - val channelQR - get(): Bitmap { - val multiFormatWriter = MultiFormatWriter() + fun getChannelQR(context: Context): Bitmap + { + val multiFormatWriter = MultiFormatWriter() - val bitMatrix = multiFormatWriter.encode(channelUrl, BarcodeFormat.QR_CODE, 192, 192); - val barcodeEncoder = BarcodeEncoder() - return barcodeEncoder.createBitmap(bitMatrix) + val bitMatrix = + multiFormatWriter.encode(getChannelUrl(context), BarcodeFormat.QR_CODE, 192, 192); + val barcodeEncoder = BarcodeEncoder() + return barcodeEncoder.createBitmap(bitMatrix) + } + + fun getPreferences(context: Context): SharedPreferences = + context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE) + + /// Set the radio config (also updates our saved copy in preferences) + fun setRadioConfig(context: Context, c: MeshProtos.RadioConfig) { + radioConfig.value = c + + getPreferences(context).edit(commit = true) { + this.putString("channel-url", getChannelUrl(context)) } + } // clean up all this nasty owner state management FIXME fun setOwner(context: Context, s: String? = null) { @@ -58,8 +81,7 @@ object UIState : Logging { ownerName = s // note: we allow an empty userstring to be written to prefs - val prefs = context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE) - prefs.edit(commit = true) { + getPreferences(context).edit(commit = true) { putString("owner", s) } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/Channel.kt b/app/src/main/java/com/geeksville/mesh/ui/Channel.kt index 6047760da..81eac7423 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/Channel.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/Channel.kt @@ -1,27 +1,24 @@ package com.geeksville.mesh.ui +import android.content.Intent import android.graphics.Bitmap -import android.os.Build import androidx.compose.Composable -import androidx.compose.ambient import androidx.ui.core.ContextAmbient import androidx.ui.core.Text -import androidx.ui.foundation.Clickable import androidx.ui.foundation.DrawImage -import androidx.ui.graphics.* +import androidx.ui.graphics.Image +import androidx.ui.graphics.ImageConfig +import androidx.ui.graphics.NativeImage import androidx.ui.graphics.colorspace.ColorSpace import androidx.ui.graphics.colorspace.ColorSpaces import androidx.ui.layout.* -import androidx.ui.material.Button import androidx.ui.material.MaterialTheme import androidx.ui.material.OutlinedButton import androidx.ui.material.ripple.Ripple -import androidx.ui.res.imageResource import androidx.ui.tooling.preview.Preview import androidx.ui.unit.dp import com.geeksville.android.GeeksvilleApplication import com.geeksville.android.Logging -import com.geeksville.android.toast import com.geeksville.mesh.R import com.geeksville.mesh.model.UIState @@ -88,7 +85,7 @@ fun ChannelContent(channel: Channel = Channel("Default", 7)) { Row(modifier = LayoutGravity.Center) { // simulated qr code // val image = imageResource(id = R.drawable.qrcode) - val image = AndroidImage(UIState.channelQR) + val image = AndroidImage(UIState.getChannelQR(context)) Container(modifier = LayoutGravity.Center + LayoutSize.Min(200.dp, 200.dp)) { DrawImage(image = image) @@ -97,9 +94,18 @@ fun ChannelContent(channel: Channel = Channel("Default", 7)) { Ripple(bounded = false) { OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(left = 24.dp), onClick = { - GeeksvilleApplication.analytics.track("channel_share") // track how many times users share channels - context.toast("Channel sharing is not yet implemented") - }) { + GeeksvilleApplication.analytics.track("channel_share") // 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