Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add more details screen for nodes #1176

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion app/src/main/java/com/geeksville/mesh/model/UIState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
Expand Down Expand Up @@ -194,10 +195,20 @@ class UIViewModel @Inject constructor(
nodeDB.getNodes(state.sort, state.filter, state.includeUnknown)
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5_000),
started = WhileSubscribed(STOP_TIMEOUT_MILLIS),
initialValue = emptyList(),
)

@OptIn(ExperimentalCoroutinesApi::class)
fun getNodeByNum(nodeNum: Int): StateFlow<NodeInfo?> = nodeDB.nodeDBbyNum.mapLatest { nodes ->
nodes[nodeNum]
}.stateIn(
scope = viewModelScope,
started = WhileSubscribed(STOP_TIMEOUT_MILLIS),
initialValue = null,
)


// hardware info about our local device (can be null)
val myNodeInfo: StateFlow<MyNodeInfo?> get() = nodeDB.myNodeInfo
val ourNodeInfo: StateFlow<NodeInfo?> get() = nodeDB.ourNodeInfo
Expand Down Expand Up @@ -315,6 +326,7 @@ class UIViewModel @Inject constructor(
}

companion object {
private const val STOP_TIMEOUT_MILLIS = 5_000L
fun getPreferences(context: Context): SharedPreferences =
context.getSharedPreferences("ui-prefs", Context.MODE_PRIVATE)
}
Expand Down
118 changes: 118 additions & 0 deletions app/src/main/java/com/geeksville/mesh/ui/NodeDetailsFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.geeksville.mesh.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.activityViewModels
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.geeksville.mesh.R
import com.geeksville.mesh.android.Logging
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.mesh.ui.theme.AppTheme
import dagger.hilt.android.AndroidEntryPoint

internal fun FragmentManager.navigateToNodeDetails(nodeNum: Int? = null) {
val nodeDetailsFragment = NodeDetailsFragment().apply {
arguments = bundleOf("nodeNum" to nodeNum)
}
beginTransaction()
.replace(R.id.mainActivityLayout, nodeDetailsFragment)
.addToBackStack(null)
.commit()
}

@AndroidEntryPoint
class NodeDetailsFragment : ScreenFragment("NodeDetails"), Logging {

private val model: UIViewModel by activityViewModels()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val nodeNum = arguments?.getInt("nodeNum")

return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
AppTheme {
NodeDetailsScreen(
model = model,
nodeNum = nodeNum,
navigateBack = {
parentFragmentManager.popBackStack()
}
)
}
}
}
}
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NodeDetailsScreen(
model: UIViewModel = hiltViewModel(),
nodeNum: Int? = null,
navigateBack: () -> Unit,
) {
val ourNodeInfo by model.ourNodeInfo.collectAsStateWithLifecycle()
val detailsNodeInfo by model.getNodeByNum(nodeNum ?: ourNodeInfo?.num ?: 0)
.collectAsStateWithLifecycle()
Scaffold(
topBar = {
TopAppBar(
backgroundColor = colorResource(R.color.toolbarBackground),
contentColor = colorResource(R.color.toolbarText),
title = {
Text(
text = "Node Details: ${detailsNodeInfo?.user?.shortName}",
)
},
navigationIcon = {
IconButton(onClick = navigateBack) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.navigate_back),
)
}
}
)
},
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text(
modifier = Modifier.padding(8.dp),
text = detailsNodeInfo?.user?.longName.orEmpty(),
)
}
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class UsersFragment : ScreenFragment("Users"), Logging {
R.id.remote_admin -> {
navigateToRadioConfig(node)
}

R.id.more_details -> {
navigateToMoreDetails(node)
}
}
}
}
Expand All @@ -96,6 +100,11 @@ class UsersFragment : ScreenFragment("Users"), Logging {
parentFragmentManager.navigateToRadioConfig(node.num)
}

private fun navigateToMoreDetails(node: NodeInfo) {
info("calling MoreDetails --> destNum: ${node.num}")
parentFragmentManager.navigateToNodeDetails(node.num)
}


override fun onCreateView(
inflater: LayoutInflater,
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/menu/menu_nodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
android:id="@+id/remove"
android:title="@string/remove"
app:showAsAction="withText" />
<item
android:id="@+id/more_details"
android:title="@string/more_details"
app:showAsAction="withText" />
</group>
<group android:id="@+id/group_admin">
<item
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
<string name="node_sort_last_heard">Last heard</string>
<string name="node_sort_via_mqtt">via MQTT</string>

<string name="more_details">More Details</string>

<string name="elevation_suffix" translatable="false">MSL</string>

<string name="channel_name">Channel Name</string>
Expand Down Expand Up @@ -214,4 +216,5 @@
<string name="scanned_channels">Scanned Channels</string>
<string name="current_channels">Current Channels</string>
<string name="replace">Replace</string>
<string name="navigate_back">Navigate Back</string>
</resources>