Add Server Select layout with RecyclerView
This commit is contained in:
parent
e1e1da8ca6
commit
131dc7aa08
15 changed files with 340 additions and 61 deletions
|
@ -1,6 +1,7 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
|
id 'org.jetbrains.kotlin.kapt'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -32,7 +33,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
dataBinding true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,13 @@ class AddServerFragment : Fragment() {
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
_binding = FragmentAddServerBinding.inflate(inflater, container, false)
|
_binding = FragmentAddServerBinding.inflate(inflater, container, false)
|
||||||
val view = binding.root
|
|
||||||
|
|
||||||
binding.buttonConnect.setOnClickListener { v: View ->
|
binding.buttonConnect.setOnClickListener { v: View ->
|
||||||
v.findNavController().navigate(R.id.action_addServerFragment_to_loginFragment)
|
v.findNavController().navigate(R.id.action_addServerFragment_to_loginFragment)
|
||||||
}
|
}
|
||||||
|
|
||||||
return view
|
return binding.root
|
||||||
}
|
}
|
||||||
}
|
}
|
12
app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt
Normal file
12
app/src/main/java/dev/jdtech/jellyfin/BindingAdapters.kt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package dev.jdtech.jellyfin
|
||||||
|
|
||||||
|
import androidx.databinding.BindingAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dev.jdtech.jellyfin.database.Server
|
||||||
|
import dev.jdtech.jellyfin.serverselect.ServerGridAdapter
|
||||||
|
|
||||||
|
@BindingAdapter("listData")
|
||||||
|
fun bindRecyclerView(recyclerView: RecyclerView, data: List<Server>?) {
|
||||||
|
val adapter = recyclerView.adapter as ServerGridAdapter
|
||||||
|
adapter.submitList(data)
|
||||||
|
}
|
9
app/src/main/java/dev/jdtech/jellyfin/database/Server.kt
Normal file
9
app/src/main/java/dev/jdtech/jellyfin/database/Server.kt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package dev.jdtech.jellyfin.database
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class Server(
|
||||||
|
val id: UUID,
|
||||||
|
val name: String,
|
||||||
|
val address: String
|
||||||
|
)
|
|
@ -0,0 +1,49 @@
|
||||||
|
package dev.jdtech.jellyfin.serverselect
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dev.jdtech.jellyfin.database.Server
|
||||||
|
import dev.jdtech.jellyfin.databinding.ServerItemBinding
|
||||||
|
|
||||||
|
class ServerGridAdapter(val onClickListener: OnClickListener) :
|
||||||
|
ListAdapter<Server, ServerGridAdapter.ServerViewHolder>(DiffCallback) {
|
||||||
|
class ServerViewHolder(private var binding: ServerItemBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
fun bind(server: Server) {
|
||||||
|
binding.server = server
|
||||||
|
binding.executePendingBindings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object DiffCallback : DiffUtil.ItemCallback<Server>() {
|
||||||
|
override fun areItemsTheSame(oldItem: Server, newItem: Server): Boolean {
|
||||||
|
return oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItem: Server, newItem: Server): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int
|
||||||
|
): ServerGridAdapter.ServerViewHolder {
|
||||||
|
return ServerViewHolder(ServerItemBinding.inflate(LayoutInflater.from(parent.context)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ServerGridAdapter.ServerViewHolder, position: Int) {
|
||||||
|
val server = getItem(position)
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
onClickListener.onClick(server)
|
||||||
|
}
|
||||||
|
holder.bind(server)
|
||||||
|
}
|
||||||
|
|
||||||
|
class OnClickListener(val clickListener: (server: Server) -> Unit) {
|
||||||
|
fun onClick(server: Server) = clickListener(server)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.jdtech.jellyfin.serverselect
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import dev.jdtech.jellyfin.R
|
||||||
|
import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
|
||||||
|
|
||||||
|
|
||||||
|
class ServerSelectFragment : Fragment() {
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
val binding = FragmentServerSelectBinding.inflate(inflater)
|
||||||
|
|
||||||
|
val viewModel = ViewModelProvider(this).get(ServerSelectViewModel::class.java)
|
||||||
|
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.serversRecyclerView.adapter = ServerGridAdapter(ServerGridAdapter.OnClickListener {
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.buttonAddServer.setOnClickListener {
|
||||||
|
this.findNavController().navigate(R.id.action_serverSelectFragment_to_addServerFragment)
|
||||||
|
}
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package dev.jdtech.jellyfin.serverselect
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dev.jdtech.jellyfin.database.Server
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ServerSelectViewModel : ViewModel() {
|
||||||
|
private val _servers = MutableLiveData<List<Server>>()
|
||||||
|
val servers: LiveData<List<Server>>
|
||||||
|
get() = _servers
|
||||||
|
|
||||||
|
init {
|
||||||
|
val server = Server(UUID.randomUUID(), "JDTech", "https://jellyfin.jdtech.dev")
|
||||||
|
val demoServer = Server(UUID.randomUUID(), "Demo", "https://demo.jellyfin.org")
|
||||||
|
_servers.value = listOf(server, demoServer)
|
||||||
|
}
|
||||||
|
}
|
11
app/src/main/res/drawable/server_add_icon.xml
Normal file
11
app/src/main/res/drawable/server_add_icon.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="@color/primary_variant">
|
||||||
|
<item android:id="@android:id/background">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<stroke android:color="@color/primary" android:width="2dp" />
|
||||||
|
<corners
|
||||||
|
android:radius="10dp" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
|
@ -1,7 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context=".AddServerFragment">
|
tools:context=".AddServerFragment">
|
||||||
|
@ -55,9 +58,9 @@
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_connect"
|
android:id="@+id/button_connect"
|
||||||
|
style="@style/setup_button"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="@drawable/button_setup_background"
|
|
||||||
android:text="@string/button_connect" />
|
android:text="@string/button_connect" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
||||||
|
|
|
@ -68,8 +68,7 @@
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button_login"
|
android:id="@+id/button_login"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
style="@style/setup_button"
|
||||||
android:background="@drawable/button_setup_background"
|
|
||||||
android:text="@string/button_login" />
|
android:text="@string/button_login" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
80
app/src/main/res/layout/fragment_server_select.xml
Normal file
80
app/src/main/res/layout/fragment_server_select.xml
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="viewModel"
|
||||||
|
type="dev.jdtech.jellyfin.serverselect.ServerSelectViewModel" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".AddServerFragment">
|
||||||
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_banner"
|
||||||
|
android:layout_width="268dp"
|
||||||
|
android:layout_height="75dp"
|
||||||
|
android:layout_marginTop="64dp"
|
||||||
|
android:contentDescription="@string/jellyfin_banner"
|
||||||
|
android:src="@drawable/ic_banner"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/main_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginEnd="24dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/image_banner"
|
||||||
|
app:layout_constraintVertical_bias="0.36">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_add_server"
|
||||||
|
style="@style/text_header"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="32dp"
|
||||||
|
android:text="@string/select_server"
|
||||||
|
android:textColor="?android:textColorPrimary" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/servers_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/image_banner"
|
||||||
|
app:layout_constraintVertical_bias="0.36"
|
||||||
|
app:listData="@{viewModel.servers}"
|
||||||
|
app:spanCount="3"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:listitem="@layout/server_item" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_add_server"
|
||||||
|
style="@style/setup_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/add_server"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/main_content" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
46
app/src/main/res/layout/server_item.xml
Normal file
46
app/src/main/res/layout/server_item.xml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="server"
|
||||||
|
type="dev.jdtech.jellyfin.database.Server" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingBottom="24dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/server_icon"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:contentDescription="@string/server_icon"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/server_name"
|
||||||
|
app:layout_constraintDimensionRatio="w,1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/button_setup_background" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/server_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@{server.name}"
|
||||||
|
android:textAlignment="center"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/server_icon"
|
||||||
|
tools:text="JDTech" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
|
@ -3,7 +3,7 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/navigation"
|
android:id="@+id/navigation"
|
||||||
app:startDestination="@id/addServerFragment">
|
app:startDestination="@id/serverSelectFragment">
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/addServerFragment"
|
android:id="@+id/addServerFragment"
|
||||||
android:name="dev.jdtech.jellyfin.AddServerFragment"
|
android:name="dev.jdtech.jellyfin.AddServerFragment"
|
||||||
|
@ -22,4 +22,16 @@
|
||||||
android:name="dev.jdtech.jellyfin.LoginFragment"
|
android:name="dev.jdtech.jellyfin.LoginFragment"
|
||||||
android:label="fragment_login"
|
android:label="fragment_login"
|
||||||
tools:layout="@layout/fragment_login" />
|
tools:layout="@layout/fragment_login" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/serverSelectFragment"
|
||||||
|
android:name="dev.jdtech.jellyfin.serverselect.ServerSelectFragment"
|
||||||
|
android:label="ServerSelectFragment" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_serverSelectFragment_to_addServerFragment"
|
||||||
|
app:destination="@id/addServerFragment"
|
||||||
|
app:enterAnim="@anim/nav_default_enter_anim"
|
||||||
|
app:exitAnim="@anim/nav_default_exit_anim"
|
||||||
|
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||||
|
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||||
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
|
@ -3,9 +3,11 @@
|
||||||
<string name="jellyfin_banner">Jellyfin banner</string>
|
<string name="jellyfin_banner">Jellyfin banner</string>
|
||||||
<string name="add_server">Add server</string>
|
<string name="add_server">Add server</string>
|
||||||
<string name="login">Login</string>
|
<string name="login">Login</string>
|
||||||
|
<string name="select_server">Select server</string>
|
||||||
<string name="edit_text_server_address_hint">Server address</string>
|
<string name="edit_text_server_address_hint">Server address</string>
|
||||||
<string name="edit_text_username_hint">Username</string>
|
<string name="edit_text_username_hint">Username</string>
|
||||||
<string name="edit_text_password_hint">Password</string>
|
<string name="edit_text_password_hint">Password</string>
|
||||||
<string name="button_connect">Connect</string>
|
<string name="button_connect">Connect</string>
|
||||||
<string name="button_login">Login</string>
|
<string name="button_login">Login</string>
|
||||||
|
<string name="server_icon">Server icon</string>
|
||||||
</resources>
|
</resources>
|
|
@ -22,4 +22,8 @@
|
||||||
<style name="text_metadata">
|
<style name="text_metadata">
|
||||||
<item name="android:textSize">12sp</item>
|
<item name="android:textSize">12sp</item>
|
||||||
</style>
|
</style>
|
||||||
|
<style name="setup_button">
|
||||||
|
<item name="android:layout_height">48dp</item>
|
||||||
|
<item name="android:background">@drawable/button_setup_background</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue