sforkowany z mirror/meshtastic-android
channel qrs are now sharable and real
rodzic
90cee2f202
commit
44ebac1758
4
TODO.md
4
TODO.md
|
|
@ -9,8 +9,6 @@ Work items for soon alpha builds
|
||||||
* fix app icon in title bar
|
* fix app icon in title bar
|
||||||
* show direction on the nodeinfo cards
|
* show direction on the nodeinfo cards
|
||||||
* take video of the app
|
* 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
|
* 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)
|
* let users change & share channels (but no saving them yet)
|
||||||
* treat macaddrs as the unique id, not the app layer user id
|
* 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
|
* 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
|
* startforegroundservice only if we have a valid radio
|
||||||
* when we select a new radio, restart the service
|
* when we select a new radio, restart the service
|
||||||
|
* make channel button look like a button
|
||||||
|
* generate real channel QR codes
|
||||||
|
|
@ -179,7 +179,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
val prefs = getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
|
val prefs = UIState.getPreferences(this)
|
||||||
UIState.ownerName = prefs.getString("owner", "")!!
|
UIState.ownerName = prefs.getString("owner", "")!!
|
||||||
UIState.meshService = null
|
UIState.meshService = null
|
||||||
|
|
||||||
|
|
@ -276,9 +276,9 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
val bytes = UIState.meshService!!.radioConfig
|
val bytes = UIState.meshService!!.radioConfig
|
||||||
|
|
||||||
val config = MeshProtos.RadioConfig.parseFrom(bytes)
|
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
|
/// Called when we gain/lose a connection to our mesh radio
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.geeksville.mesh.model
|
package com.geeksville.mesh.model
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.RemoteException
|
import android.os.RemoteException
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
|
|
@ -26,7 +27,7 @@ object UIState : Logging {
|
||||||
val isConnected = mutableStateOf(false)
|
val isConnected = mutableStateOf(false)
|
||||||
|
|
||||||
/// various radio settings (including the channel)
|
/// various radio settings (including the channel)
|
||||||
val radioConfig = mutableStateOf(MeshProtos.RadioConfig.getDefaultInstance())
|
private val radioConfig = mutableStateOf<MeshProtos.RadioConfig?>(null)
|
||||||
|
|
||||||
/// our name in hte radio
|
/// our name in hte radio
|
||||||
/// Note, we generate owner initials automatically for now
|
/// Note, we generate owner initials automatically for now
|
||||||
|
|
@ -34,22 +35,44 @@ object UIState : Logging {
|
||||||
var ownerName: String = "MrInIDE Ownername"
|
var ownerName: String = "MrInIDE Ownername"
|
||||||
|
|
||||||
/// Return an URL that represents the current channel values
|
/// Return an URL that represents the current channel values
|
||||||
val channelUrl
|
fun getChannelUrl(context: Context): String {
|
||||||
get(): String {
|
// If we have a valid radio config use it, othterwise use whatever we have saved in the prefs
|
||||||
val channelBytes = radioConfig.value.channelSettings.toByteArray()
|
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)
|
val enc = Base64.encodeToString(channelBytes, Base64.URL_SAFE + Base64.NO_WRAP)
|
||||||
|
|
||||||
return "https://www.meshtastic.org/c/$enc"
|
return "https://www.meshtastic.org/c/$enc"
|
||||||
|
} else {
|
||||||
|
return getPreferences(context).getString(
|
||||||
|
"owner",
|
||||||
|
"https://www.meshtastic.org/c/unset"
|
||||||
|
)!!
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val channelQR
|
fun getChannelQR(context: Context): Bitmap
|
||||||
get(): Bitmap {
|
{
|
||||||
val multiFormatWriter = MultiFormatWriter()
|
val multiFormatWriter = MultiFormatWriter()
|
||||||
|
|
||||||
val bitMatrix = multiFormatWriter.encode(channelUrl, BarcodeFormat.QR_CODE, 192, 192);
|
val bitMatrix =
|
||||||
val barcodeEncoder = BarcodeEncoder()
|
multiFormatWriter.encode(getChannelUrl(context), BarcodeFormat.QR_CODE, 192, 192);
|
||||||
return barcodeEncoder.createBitmap(bitMatrix)
|
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
|
// clean up all this nasty owner state management FIXME
|
||||||
fun setOwner(context: Context, s: String? = null) {
|
fun setOwner(context: Context, s: String? = null) {
|
||||||
|
|
@ -58,8 +81,7 @@ object UIState : Logging {
|
||||||
ownerName = s
|
ownerName = s
|
||||||
|
|
||||||
// note: we allow an empty userstring to be written to prefs
|
// note: we allow an empty userstring to be written to prefs
|
||||||
val prefs = context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
|
getPreferences(context).edit(commit = true) {
|
||||||
prefs.edit(commit = true) {
|
|
||||||
putString("owner", s)
|
putString("owner", s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,24 @@
|
||||||
package com.geeksville.mesh.ui
|
package com.geeksville.mesh.ui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Build
|
|
||||||
import androidx.compose.Composable
|
import androidx.compose.Composable
|
||||||
import androidx.compose.ambient
|
|
||||||
import androidx.ui.core.ContextAmbient
|
import androidx.ui.core.ContextAmbient
|
||||||
import androidx.ui.core.Text
|
import androidx.ui.core.Text
|
||||||
import androidx.ui.foundation.Clickable
|
|
||||||
import androidx.ui.foundation.DrawImage
|
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.ColorSpace
|
||||||
import androidx.ui.graphics.colorspace.ColorSpaces
|
import androidx.ui.graphics.colorspace.ColorSpaces
|
||||||
import androidx.ui.layout.*
|
import androidx.ui.layout.*
|
||||||
import androidx.ui.material.Button
|
|
||||||
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.res.imageResource
|
|
||||||
import androidx.ui.tooling.preview.Preview
|
import androidx.ui.tooling.preview.Preview
|
||||||
import androidx.ui.unit.dp
|
import androidx.ui.unit.dp
|
||||||
import com.geeksville.android.GeeksvilleApplication
|
import com.geeksville.android.GeeksvilleApplication
|
||||||
import com.geeksville.android.Logging
|
import com.geeksville.android.Logging
|
||||||
import com.geeksville.android.toast
|
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.model.UIState
|
import com.geeksville.mesh.model.UIState
|
||||||
|
|
||||||
|
|
@ -88,7 +85,7 @@ fun ChannelContent(channel: Channel = Channel("Default", 7)) {
|
||||||
Row(modifier = LayoutGravity.Center) {
|
Row(modifier = LayoutGravity.Center) {
|
||||||
// simulated qr code
|
// simulated qr code
|
||||||
// val image = imageResource(id = R.drawable.qrcode)
|
// 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)) {
|
Container(modifier = LayoutGravity.Center + LayoutSize.Min(200.dp, 200.dp)) {
|
||||||
DrawImage(image = image)
|
DrawImage(image = image)
|
||||||
|
|
@ -97,9 +94,18 @@ fun ChannelContent(channel: Channel = Channel("Default", 7)) {
|
||||||
Ripple(bounded = false) {
|
Ripple(bounded = false) {
|
||||||
OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(left = 24.dp),
|
OutlinedButton(modifier = LayoutGravity.Center + LayoutPadding(left = 24.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
GeeksvilleApplication.analytics.track("channel_share") // track how many times users share channels
|
GeeksvilleApplication.analytics.track("channel_share") // track how many times users share channels
|
||||||
context.toast("Channel sharing is not yet implemented")
|
|
||||||
}) {
|
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(
|
VectorImage(
|
||||||
id = R.drawable.ic_twotone_share_24,
|
id = R.drawable.ic_twotone_share_24,
|
||||||
tint = palette.onBackground
|
tint = palette.onBackground
|
||||||
|
|
|
||||||
Ładowanie…
Reference in New Issue