mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
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:
parent
7b49f57af6
commit
9ecae6c0e1
4 changed files with 104 additions and 28 deletions
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ import android.content.res.ColorStateList
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.text.style.StrikethroughSpan
|
import android.text.style.StrikethroughSpan
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
|
@ -14,8 +13,6 @@ import android.view.ViewGroup
|
||||||
import android.view.animation.LinearInterpolator
|
import android.view.animation.LinearInterpolator
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.animation.doOnEnd
|
import androidx.core.animation.doOnEnd
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.asLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
@ -23,6 +20,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.geeksville.mesh.NodeInfo
|
import com.geeksville.mesh.NodeInfo
|
||||||
|
import com.geeksville.mesh.Position
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.android.Logging
|
import com.geeksville.mesh.android.Logging
|
||||||
import com.geeksville.mesh.databinding.AdapterNodeLayoutBinding
|
import com.geeksville.mesh.databinding.AdapterNodeLayoutBinding
|
||||||
|
|
@ -35,7 +33,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.net.URLEncoder
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class UsersFragment : ScreenFragment("Users"), Logging {
|
class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
|
|
@ -58,11 +55,11 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
val chipNode = itemView.chipNode
|
val chipNode = itemView.chipNode
|
||||||
val nodeNameView = itemView.nodeNameView
|
val nodeNameView = itemView.nodeNameView
|
||||||
val distanceView = itemView.distanceView
|
val distanceView = itemView.distanceView
|
||||||
val coordsView = itemView.coordsView
|
|
||||||
val lastTime = itemView.lastConnectionView
|
val lastTime = itemView.lastConnectionView
|
||||||
val signalView = itemView.signalView
|
val signalView = itemView.signalView
|
||||||
val envMetrics = itemView.envMetrics
|
val envMetrics = itemView.envMetrics
|
||||||
val background = itemView.nodeCard
|
val background = itemView.nodeCard
|
||||||
|
val nodePosition = itemView.nodePosition
|
||||||
val batteryInfo = itemView.batteryInfo
|
val batteryInfo = itemView.batteryInfo
|
||||||
|
|
||||||
fun blink() {
|
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 {
|
batteryInfo.setContent {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
BatteryInfo(batteryLevel, voltage)
|
BatteryInfo(batteryLevel, voltage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nodePosition.setContent {
|
||||||
|
AppTheme {
|
||||||
|
LinkedCoordinates(position, gpsFormat, nodeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,29 +248,17 @@ class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
val user = n.user
|
val user = n.user
|
||||||
val (textColor, nodeColor) = n.colors
|
val (textColor, nodeColor) = n.colors
|
||||||
val isIgnored: Boolean = ignoreIncomingList.contains(n.num)
|
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) {
|
with(holder.chipNode) {
|
||||||
text = (user?.shortName ?: "UNK").strikeIf(isIgnored)
|
text = (user?.shortName ?: "UNK").strikeIf(isIgnored)
|
||||||
chipBackgroundColor = ColorStateList.valueOf(nodeColor)
|
chipBackgroundColor = ColorStateList.valueOf(nodeColor)
|
||||||
setTextColor(textColor)
|
setTextColor(textColor)
|
||||||
}
|
}
|
||||||
val name = user?.longName ?: getString(R.string.unknown_username)
|
|
||||||
holder.nodeNameView.text = name
|
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 ourNodeInfo = nodes[0]
|
||||||
val distance = ourNodeInfo.distanceStr(n, displayUnits)
|
val distance = ourNodeInfo.distanceStr(n, displayUnits)
|
||||||
if (distance != null) {
|
if (distance != null) {
|
||||||
|
|
|
||||||
|
|
@ -17,4 +17,6 @@ val LightRed = Color(0xFFFFB3B3)
|
||||||
|
|
||||||
val MeshtasticGreen = Color(0xFF67EA94)
|
val MeshtasticGreen = Color(0xFF67EA94)
|
||||||
val AlmostWhite = Color(0xB3FFFFFF)
|
val AlmostWhite = Color(0xB3FFFFFF)
|
||||||
val AlmostBlack = Color(0x8A000000)
|
val AlmostBlack = Color(0x8A000000)
|
||||||
|
|
||||||
|
val HyperlinkBlue = Color(0xFF43C3B0)
|
||||||
|
|
@ -53,17 +53,15 @@
|
||||||
tools:text="@string/sample_distance"
|
tools:text="@string/sample_distance"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextView
|
<androidx.compose.ui.platform.ComposeView
|
||||||
android:id="@+id/coords_view"
|
android:id="@+id/nodePosition"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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_constraintStart_toEndOf="@+id/chip_node"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/nodeNameView"
|
app:layout_constraintTop_toBottomOf="@+id/nodeNameView"
|
||||||
app:layout_constraintVertical_bias="0.0"
|
android:layout_marginStart="8dp"
|
||||||
tools:text="@string/sample_coords"
|
android:layout_marginTop="8dp"
|
||||||
|
tools:composableName="com.geeksville.mesh.ui.LinkedCoordinatesKt.LinkedCoordinatesPreview"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<androidx.compose.ui.platform.ComposeView
|
<androidx.compose.ui.platform.ComposeView
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue