Node position to compose (#877)

* Move battery info to compose - always show voltage level and icons to match battery percentage
Use tool text in preview, rather than actually set text value
Simplify node info layout to avoid defining margins on everything

* Move node position to Compose

* Update hyperlink color to match previous value

* Use compose preview in layout editor

* Use compose preview in layout editor

* Add simple preview for use in layout
This commit is contained in:
Davis
2024-02-27 14:43:47 -07:00
committed by GitHub
parent 7b49f57af6
commit 9ecae6c0e1
4 changed files with 104 additions and 28 deletions

View File

@@ -0,0 +1,80 @@
package com.geeksville.mesh.ui
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.geeksville.mesh.Position
import com.geeksville.mesh.R
import com.geeksville.mesh.android.BuildUtils.debug
import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.ui.theme.HyperlinkBlue
import java.net.URLEncoder
@Composable
fun LinkedCoordinates(
position: Position?,
format: Int,
nodeName: String?
) {
if (position != null) {
val uriHandler = LocalUriHandler.current
val style = SpanStyle(
color = HyperlinkBlue,
fontSize = MaterialTheme.typography.button.fontSize,
textDecoration = TextDecoration.Underline
)
val name = nodeName ?: stringResource(id = R.string.unknown_username)
val annotatedString = buildAnnotatedString {
pushStringAnnotation(
tag = "gps",
annotation = "geo:${position.latitude},${position.longitude}?z=17&label=${
URLEncoder.encode(name, "utf-8")
}"
)
withStyle(style = style) {
append(position.gpsString(format))
}
pop()
}
ClickableText(
text = annotatedString,
maxLines = 1,
onClick = { offset ->
debug("Clicked on link")
annotatedString.getStringAnnotations(tag = "gps", start = offset, end = offset)
.firstOrNull()?.let {
uriHandler.openUri(it.item)
}
}
)
}
}
@Composable
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = android.content.res.Configuration.UI_MODE_NIGHT_YES)
fun LinkedCoordinatesPreview(
@PreviewParameter(GPSFormatPreviewParameterProvider::class) format: Int
) {
AppTheme {
LinkedCoordinates(
position = Position(37.7749, -122.4194, 0),
format = format,
nodeName = "Test Node Name"
)
}
}
class GPSFormatPreviewParameterProvider: PreviewParameterProvider<Int> {
override val values: Sequence<Int>
get() = sequenceOf(0, 1, 2)
}

View File

@@ -5,7 +5,6 @@ import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.style.StrikethroughSpan
import android.view.LayoutInflater
import android.view.MenuItem
@@ -14,8 +13,6 @@ import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import androidx.appcompat.widget.PopupMenu
import androidx.core.animation.doOnEnd
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.asLiveData
import androidx.lifecycle.lifecycleScope
@@ -23,6 +20,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import com.geeksville.mesh.NodeInfo
import com.geeksville.mesh.Position
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.databinding.AdapterNodeLayoutBinding
@@ -35,7 +33,6 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URLEncoder
@AndroidEntryPoint
class UsersFragment : ScreenFragment("Users"), Logging {
@@ -58,11 +55,11 @@ class UsersFragment : ScreenFragment("Users"), Logging {
val chipNode = itemView.chipNode
val nodeNameView = itemView.nodeNameView
val distanceView = itemView.distanceView
val coordsView = itemView.coordsView
val lastTime = itemView.lastConnectionView
val signalView = itemView.signalView
val envMetrics = itemView.envMetrics
val background = itemView.nodeCard
val nodePosition = itemView.nodePosition
val batteryInfo = itemView.batteryInfo
fun blink() {
@@ -86,12 +83,23 @@ class UsersFragment : ScreenFragment("Users"), Logging {
}
}
fun bind(batteryLevel: Int?, voltage: Float?) {
fun bind(
batteryLevel: Int?,
voltage: Float?,
position: Position?,
gpsFormat: Int,
nodeName: String?
) {
batteryInfo.setContent {
AppTheme {
BatteryInfo(batteryLevel, voltage)
}
}
nodePosition.setContent {
AppTheme {
LinkedCoordinates(position, gpsFormat, nodeName)
}
}
}
}
@@ -240,29 +248,17 @@ class UsersFragment : ScreenFragment("Users"), Logging {
val user = n.user
val (textColor, nodeColor) = n.colors
val isIgnored: Boolean = ignoreIncomingList.contains(n.num)
val name = user?.longName
holder.bind(n.batteryLevel, n.voltage)
holder.bind(n.batteryLevel, n.voltage, n.validPosition, gpsFormat, name)
with(holder.chipNode) {
text = (user?.shortName ?: "UNK").strikeIf(isIgnored)
chipBackgroundColor = ColorStateList.valueOf(nodeColor)
setTextColor(textColor)
}
val name = user?.longName ?: getString(R.string.unknown_username)
holder.nodeNameView.text = name
val pos = n.validPosition
if (pos != null) {
val html = "<a href='geo:${pos.latitude},${pos.longitude}?z=17&label=${
URLEncoder.encode(name, "utf-8")
}'>${pos.gpsString(gpsFormat)}</a>"
holder.coordsView.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
holder.coordsView.movementMethod = LinkMovementMethod.getInstance()
holder.coordsView.visibility = View.VISIBLE
} else {
holder.coordsView.visibility = View.INVISIBLE
}
val ourNodeInfo = nodes[0]
val distance = ourNodeInfo.distanceStr(n, displayUnits)
if (distance != null) {

View File

@@ -17,4 +17,6 @@ val LightRed = Color(0xFFFFB3B3)
val MeshtasticGreen = Color(0xFF67EA94)
val AlmostWhite = Color(0xB3FFFFFF)
val AlmostBlack = Color(0x8A000000)
val AlmostBlack = Color(0x8A000000)
val HyperlinkBlue = Color(0xFF43C3B0)

View File

@@ -53,17 +53,15 @@
tools:text="@string/sample_distance"
/>
<TextView
android:id="@+id/coords_view"
<androidx.compose.ui.platform.ComposeView
android:id="@+id/nodePosition"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
app:layout_constraintStart_toEndOf="@+id/chip_node"
app:layout_constraintTop_toBottomOf="@+id/nodeNameView"
app:layout_constraintVertical_bias="0.0"
tools:text="@string/sample_coords"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
tools:composableName="com.geeksville.mesh.ui.LinkedCoordinatesKt.LinkedCoordinatesPreview"
/>
<androidx.compose.ui.platform.ComposeView