kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
node list view kinda works
rodzic
737b98076a
commit
0ff97ba3c4
|
@ -77,6 +77,8 @@ dependencies {
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.core:core-ktx:1.2.0'
|
implementation 'androidx.core:core-ktx:1.2.0'
|
||||||
implementation "androidx.fragment:fragment-ktx:1.2.4"
|
implementation "androidx.fragment:fragment-ktx:1.2.4"
|
||||||
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
|
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
|
||||||
implementation 'com.google.android.material:material:1.1.0'
|
implementation 'com.google.android.material:material:1.1.0'
|
||||||
implementation 'androidx.viewpager2:viewpager2:1.0.0'
|
implementation 'androidx.viewpager2:viewpager2:1.0.0'
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.geeksville.mesh.service.*
|
import com.geeksville.mesh.service.*
|
||||||
import com.geeksville.mesh.ui.ChannelFragment
|
import com.geeksville.mesh.ui.ChannelFragment
|
||||||
import com.geeksville.mesh.ui.MapFragment
|
import com.geeksville.mesh.ui.MapFragment
|
||||||
|
import com.geeksville.mesh.ui.UsersFragment
|
||||||
import com.geeksville.util.Exceptions
|
import com.geeksville.util.Exceptions
|
||||||
import com.geeksville.util.exceptionReporter
|
import com.geeksville.util.exceptionReporter
|
||||||
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
import com.google.android.gms.auth.api.signin.GoogleSignIn
|
||||||
|
@ -112,6 +113,11 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
|
|
||||||
// private val tabIndexes = generateSequence(0) { it + 1 } FIXME, instead do withIndex or zip? to get the ids below, also stop duplicating strings
|
// private val tabIndexes = generateSequence(0) { it + 1 } FIXME, instead do withIndex or zip? to get the ids below, also stop duplicating strings
|
||||||
private val tabInfos = arrayOf(
|
private val tabInfos = arrayOf(
|
||||||
|
TabInfo(
|
||||||
|
"Users",
|
||||||
|
R.drawable.ic_twotone_people_24,
|
||||||
|
UsersFragment()
|
||||||
|
),
|
||||||
TabInfo(
|
TabInfo(
|
||||||
"Channel",
|
"Channel",
|
||||||
R.drawable.ic_twotone_contactless_24,
|
R.drawable.ic_twotone_contactless_24,
|
||||||
|
@ -128,10 +134,7 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
R.drawable.ic_twotone_message_24,
|
R.drawable.ic_twotone_message_24,
|
||||||
ComposeFragment("Messages", 1) { MessagesContent() }),
|
ComposeFragment("Messages", 1) { MessagesContent() }),
|
||||||
|
|
||||||
TabInfo(
|
|
||||||
"Users",
|
|
||||||
R.drawable.ic_twotone_people_24,
|
|
||||||
ComposeFragment("Users", 3) { UsersContent() }),
|
|
||||||
TabInfo(
|
TabInfo(
|
||||||
"Settings",
|
"Settings",
|
||||||
R.drawable.ic_twotone_settings_applications_24,
|
R.drawable.ic_twotone_settings_applications_24,
|
||||||
|
@ -470,9 +473,12 @@ class MainActivity : AppCompatActivity(), Logging,
|
||||||
ServiceClient<com.geeksville.mesh.IMeshService>({
|
ServiceClient<com.geeksville.mesh.IMeshService>({
|
||||||
com.geeksville.mesh.IMeshService.Stub.asInterface(it)
|
com.geeksville.mesh.IMeshService.Stub.asInterface(it)
|
||||||
}) {
|
}) {
|
||||||
override fun onConnected(service: com.geeksville.mesh.IMeshService) {
|
override fun onConnected(service: com.geeksville.mesh.IMeshService) = exceptionReporter {
|
||||||
model.meshService = service
|
model.meshService = service
|
||||||
|
|
||||||
|
debug("Getting latest radioconfig from service")
|
||||||
|
model.radioConfig.value = MeshProtos.RadioConfig.parseFrom(service.radioConfig)
|
||||||
|
|
||||||
// We don't start listening for packets until after we are connected to the service
|
// We don't start listening for packets until after we are connected to the service
|
||||||
registerMeshReceiver()
|
registerMeshReceiver()
|
||||||
|
|
||||||
|
|
|
@ -59,22 +59,6 @@ class UIViewModel : ViewModel(), Logging {
|
||||||
|
|
||||||
/// various radio settings (including the channel)
|
/// various radio settings (including the channel)
|
||||||
val radioConfig = object : MutableLiveData<MeshProtos.RadioConfig?>(null) {
|
val radioConfig = object : MutableLiveData<MeshProtos.RadioConfig?>(null) {
|
||||||
/**
|
|
||||||
* Called when the number of active observers change to 1 from 0.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* This callback can be used to know that this LiveData is being used thus should be kept
|
|
||||||
* up to date.
|
|
||||||
*/
|
|
||||||
override fun onActive() {
|
|
||||||
super.onActive()
|
|
||||||
|
|
||||||
// Get the current radio config from the service
|
|
||||||
meshService?.let {
|
|
||||||
debug("Getting latest radioconfig from service")
|
|
||||||
value = MeshProtos.RadioConfig.parseFrom(it.radioConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
|
|
|
@ -17,9 +17,6 @@ import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.channel_fragment.*
|
import kotlinx.android.synthetic.main.channel_fragment.*
|
||||||
|
|
||||||
|
|
||||||
object ChannelLog : Logging
|
|
||||||
|
|
||||||
|
|
||||||
class ChannelFragment : ScreenFragment("Channel"), Logging {
|
class ChannelFragment : ScreenFragment("Channel"), Logging {
|
||||||
|
|
||||||
private val model: UIViewModel by activityViewModels()
|
private val model: UIViewModel by activityViewModels()
|
|
@ -1,24 +1,133 @@
|
||||||
package com.geeksville.mesh.ui
|
package com.geeksville.mesh.ui
|
||||||
|
|
||||||
/*
|
|
||||||
import androidx.compose.Composable
|
import android.os.Bundle
|
||||||
import androidx.ui.core.ContextAmbient
|
import android.view.LayoutInflater
|
||||||
import androidx.ui.foundation.Text
|
import android.view.View
|
||||||
import androidx.ui.layout.Column
|
import android.view.ViewGroup
|
||||||
import androidx.ui.layout.LayoutPadding
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.ui.layout.Row
|
import androidx.lifecycle.Observer
|
||||||
import androidx.ui.material.Button
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.ui.unit.dp
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.geeksville.android.Logging
|
||||||
|
import com.geeksville.mesh.NodeInfo
|
||||||
import com.geeksville.mesh.R
|
import com.geeksville.mesh.R
|
||||||
import com.geeksville.mesh.model.NodeDB
|
import com.geeksville.mesh.model.UIViewModel
|
||||||
import com.geeksville.mesh.model.UIState
|
import kotlinx.android.synthetic.main.adapter_node_layout.view.*
|
||||||
import com.geeksville.mesh.service.MeshService
|
import kotlinx.android.synthetic.main.nodelist_fragment.*
|
||||||
import com.geeksville.mesh.service.RadioInterfaceService
|
|
||||||
import com.geeksville.mesh.service.SoftwareUpdateService
|
|
||||||
|
|
||||||
|
|
||||||
|
class UsersFragment : ScreenFragment("Users"), Logging {
|
||||||
|
|
||||||
|
private val model: UIViewModel by activityViewModels()
|
||||||
|
|
||||||
|
// Provide a direct reference to each of the views within a data item
|
||||||
|
// Used to cache the views within the item layout for fast access
|
||||||
|
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
val nodeNameView = itemView.nodeNameView
|
||||||
|
}
|
||||||
|
|
||||||
|
private val nodesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when RecyclerView needs a new [ViewHolder] of the given type to represent
|
||||||
|
* an item.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This new ViewHolder should be constructed with a new View that can represent the items
|
||||||
|
* of the given type. You can either create a new View manually or inflate it from an XML
|
||||||
|
* layout file.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The new ViewHolder will be used to display items of the adapter using
|
||||||
|
* [.onBindViewHolder]. Since it will be re-used to display
|
||||||
|
* different items in the data set, it is a good idea to cache references to sub views of
|
||||||
|
* the View to avoid unnecessary [View.findViewById] calls.
|
||||||
|
*
|
||||||
|
* @param parent The ViewGroup into which the new View will be added after it is bound to
|
||||||
|
* an adapter position.
|
||||||
|
* @param viewType The view type of the new View.
|
||||||
|
*
|
||||||
|
* @return A new ViewHolder that holds a View of the given view type.
|
||||||
|
* @see .getItemViewType
|
||||||
|
* @see .onBindViewHolder
|
||||||
|
*/
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val inflater = LayoutInflater.from(requireContext())
|
||||||
|
|
||||||
|
// Inflate the custom layout
|
||||||
|
|
||||||
|
// Inflate the custom layout
|
||||||
|
val contactView: View = inflater.inflate(R.layout.adapter_node_layout, parent, false)
|
||||||
|
|
||||||
|
// Return a new holder instance
|
||||||
|
return ViewHolder(contactView)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total number of items in the data set held by the adapter.
|
||||||
|
*
|
||||||
|
* @return The total number of items in this adapter.
|
||||||
|
*/
|
||||||
|
override fun getItemCount(): Int = nodes.size
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by RecyclerView to display the data at the specified position. This method should
|
||||||
|
* update the contents of the [ViewHolder.itemView] to reflect the item at the given
|
||||||
|
* position.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Note that unlike [android.widget.ListView], RecyclerView will not call this method
|
||||||
|
* again if the position of the item changes in the data set unless the item itself is
|
||||||
|
* invalidated or the new position cannot be determined. For this reason, you should only
|
||||||
|
* use the `position` parameter while acquiring the related data item inside
|
||||||
|
* this method and should not keep a copy of it. If you need the position of an item later
|
||||||
|
* on (e.g. in a click listener), use [ViewHolder.getAdapterPosition] which will
|
||||||
|
* have the updated adapter position.
|
||||||
|
*
|
||||||
|
* Override [.onBindViewHolder] instead if Adapter can
|
||||||
|
* handle efficient partial bind.
|
||||||
|
*
|
||||||
|
* @param holder The ViewHolder which should be updated to represent the contents of the
|
||||||
|
* item at the given position in the data set.
|
||||||
|
* @param position The position of the item within the adapter's data set.
|
||||||
|
*/
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val n = nodes[position]
|
||||||
|
|
||||||
|
holder.nodeNameView.text = n.user?.longName ?: n.user?.id ?: "Unknown node"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var nodes = arrayOf<NodeInfo>()
|
||||||
|
|
||||||
|
/// Called when our node DB changes
|
||||||
|
fun onNodesChanged(nodesIn: Collection<NodeInfo>) {
|
||||||
|
nodes = nodesIn.toTypedArray()
|
||||||
|
notifyDataSetChanged() // FIXME, this is super expensive and redraws all nodes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
return inflater.inflate(R.layout.nodelist_fragment, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
nodeListView.adapter = nodesAdapter
|
||||||
|
nodeListView.layoutManager = LinearLayoutManager(requireContext())
|
||||||
|
|
||||||
|
model.nodeDB.nodes.observe(viewLifecycleOwner, Observer { it ->
|
||||||
|
nodesAdapter.onNodesChanged(it.values)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
@Composable
|
@Composable
|
||||||
fun UsersContent() {
|
fun UsersContent() {
|
||||||
Column {
|
Column {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/Widget.App.CardView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardCornerRadius="5dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/nodeNameView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:text="Unknown Username"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/nodeListView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -28,4 +28,5 @@
|
||||||
<item name="android:minHeight">48dp</item>
|
<item name="android:minHeight">48dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Widget.App.CardView" parent="Widget.MaterialComponents.CardView"></style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Ładowanie…
Reference in New Issue