Temporary remove all TV code (#229)
* Remove all tv code * Remove banner
This commit is contained in:
parent
4a611e160d
commit
25efbb6eab
46 changed files with 54 additions and 2522 deletions
|
@ -74,7 +74,6 @@ dependencies {
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.androidx.constraintlayout)
|
implementation(libs.androidx.constraintlayout)
|
||||||
implementation(libs.androidx.core)
|
implementation(libs.androidx.core)
|
||||||
implementation(libs.androidx.leanback)
|
|
||||||
implementation(libs.androidx.lifecycle.runtime)
|
implementation(libs.androidx.lifecycle.runtime)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel)
|
implementation(libs.androidx.lifecycle.viewmodel)
|
||||||
implementation(libs.androidx.media3.exoplayer)
|
implementation(libs.androidx.media3.exoplayer)
|
||||||
|
|
|
@ -3,14 +3,11 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<uses-feature android:name="android.software.leanback" android:required="false" />
|
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
|
||||||
<uses-feature android:name="android.hardware.wifi" android:required="false" />
|
<uses-feature android:name="android.hardware.wifi" android:required="false" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".BaseApplication"
|
android:name=".BaseApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:banner="@mipmap/ic_banner"
|
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:fullBackupOnly="true"
|
android:fullBackupOnly="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@ -23,10 +20,6 @@
|
||||||
android:name=".PlayerActivity"
|
android:name=".PlayerActivity"
|
||||||
android:screenOrientation="sensorLandscape" />
|
android:screenOrientation="sensorLandscape" />
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".tv.TvPlayerActivity"
|
|
||||||
android:screenOrientation="userLandscape" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -34,9 +27,7 @@
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package dev.jdtech.jellyfin
|
package dev.jdtech.jellyfin
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
@ -26,7 +24,6 @@ import javax.inject.Inject
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
|
|
||||||
private val viewModel: MainViewModel by viewModels()
|
private val viewModel: MainViewModel by viewModels()
|
||||||
|
|
||||||
|
@ -43,7 +40,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
uiModeManager = getSystemService(UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
@ -53,52 +49,40 @@ class MainActivity : AppCompatActivity() {
|
||||||
val inflater = navController.navInflater
|
val inflater = navController.navInflater
|
||||||
val graph = inflater.inflate(R.navigation.app_navigation)
|
val graph = inflater.inflate(R.navigation.app_navigation)
|
||||||
|
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
checkServersEmpty(graph) {
|
||||||
graph.setStartDestination(R.id.homeFragmentTv)
|
navController.setGraph(graph, intent.extras)
|
||||||
checkServersEmpty(graph)
|
}
|
||||||
checkUser(graph)
|
checkUser(graph) {
|
||||||
if (!viewModel.startDestinationTvChanged) {
|
navController.setGraph(graph, intent.extras)
|
||||||
viewModel.startDestinationTvChanged = true
|
|
||||||
navController.setGraph(graph, intent.extras)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
checkServersEmpty(graph) {
|
|
||||||
navController.setGraph(graph, intent.extras)
|
|
||||||
}
|
|
||||||
checkUser(graph) {
|
|
||||||
navController.setGraph(graph, intent.extras)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiModeManager.currentModeType != Configuration.UI_MODE_TYPE_TELEVISION) {
|
val navView: NavigationBarView = binding.navView as NavigationBarView
|
||||||
val navView: NavigationBarView = binding.navView as NavigationBarView
|
|
||||||
|
|
||||||
setSupportActionBar(binding.mainToolbar)
|
setSupportActionBar(binding.mainToolbar)
|
||||||
|
|
||||||
// Passing each menu ID as a set of Ids because each
|
// Passing each menu ID as a set of Ids because each
|
||||||
// menu should be considered as top level destinations.
|
// menu should be considered as top level destinations.
|
||||||
val appBarConfiguration = AppBarConfiguration(
|
val appBarConfiguration = AppBarConfiguration(
|
||||||
setOf(
|
setOf(
|
||||||
R.id.homeFragment,
|
R.id.homeFragment,
|
||||||
R.id.mediaFragment,
|
R.id.mediaFragment,
|
||||||
R.id.favoriteFragment,
|
R.id.favoriteFragment,
|
||||||
R.id.downloadFragment
|
R.id.downloadFragment
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
// navView.setupWithNavController(navController)
|
// navView.setupWithNavController(navController)
|
||||||
// Don't save the state of other main navigation items, only this experimental function allows turning off this behavior
|
// Don't save the state of other main navigation items, only this experimental function allows turning off this behavior
|
||||||
NavigationUI.setupWithNavController(navView, navController, false)
|
NavigationUI.setupWithNavController(navView, navController, false)
|
||||||
|
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
binding.navView!!.visibility = when (destination.id) {
|
binding.navView.visibility = when (destination.id) {
|
||||||
R.id.twoPaneSettingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment, R.id.about_libraries_dest, R.id.usersFragment, R.id.serverAddressesFragment -> View.GONE
|
R.id.twoPaneSettingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment, R.id.about_libraries_dest, R.id.usersFragment, R.id.serverAddressesFragment -> View.GONE
|
||||||
else -> View.VISIBLE
|
else -> View.VISIBLE
|
||||||
}
|
|
||||||
if (destination.id == R.id.about_libraries_dest) binding.mainToolbar?.title =
|
|
||||||
getString(R.string.app_info)
|
|
||||||
}
|
}
|
||||||
|
if (destination.id == R.id.about_libraries_dest) binding.mainToolbar.title =
|
||||||
|
getString(R.string.app_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
loadDownloadLocation(applicationContext)
|
loadDownloadLocation(applicationContext)
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
package dev.jdtech.jellyfin.dialogs
|
package dev.jdtech.jellyfin.dialogs
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
|
@ -17,16 +13,10 @@ class AddServerAddressDialog(
|
||||||
private val viewModel: ServerAddressesViewModel
|
private val viewModel: ServerAddressesViewModel
|
||||||
) : DialogFragment() {
|
) : DialogFragment() {
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
val editText = EditText(this.context)
|
val editText = EditText(this.context)
|
||||||
editText.hint = "http://<server_ip>:8096"
|
editText.hint = "http://<server_ip>:8096"
|
||||||
return activity?.let { activity ->
|
return activity?.let { activity ->
|
||||||
val builder = if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
val builder = MaterialAlertDialogBuilder(activity)
|
||||||
AlertDialog.Builder(activity)
|
|
||||||
} else {
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
|
||||||
}
|
|
||||||
builder
|
builder
|
||||||
.setTitle("Add server address")
|
.setTitle("Add server address")
|
||||||
.setView(editText)
|
.setView(editText)
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.widget.AppCompatEditText
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
@ -27,7 +24,6 @@ import timber.log.Timber
|
||||||
class AddServerFragment : Fragment() {
|
class AddServerFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentAddServerBinding
|
private lateinit var binding: FragmentAddServerBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
private val viewModel: AddServerViewModel by viewModels()
|
private val viewModel: AddServerViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -36,8 +32,6 @@ class AddServerFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentAddServerBinding.inflate(inflater)
|
binding = FragmentAddServerBinding.inflate(inflater)
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
(binding.editTextServerAddress as AppCompatEditText).setOnEditorActionListener { _, actionId, _ ->
|
(binding.editTextServerAddress as AppCompatEditText).setOnEditorActionListener { _, actionId, _ ->
|
||||||
return@setOnEditorActionListener when (actionId) {
|
return@setOnEditorActionListener when (actionId) {
|
||||||
|
@ -98,42 +92,24 @@ class AddServerFragment : Fragment() {
|
||||||
private fun bindUiStateNormal() {
|
private fun bindUiStateNormal() {
|
||||||
binding.buttonConnect.isEnabled = true
|
binding.buttonConnect.isEnabled = true
|
||||||
binding.progressCircular.isVisible = false
|
binding.progressCircular.isVisible = false
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
binding.editTextServerAddressLayout.isEnabled = true
|
||||||
(binding.editTextServerAddress as AppCompatEditText).isEnabled = true
|
|
||||||
} else {
|
|
||||||
binding.editTextServerAddressLayout!!.isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateError(uiState: AddServerViewModel.UiState.Error) {
|
private fun bindUiStateError(uiState: AddServerViewModel.UiState.Error) {
|
||||||
binding.buttonConnect.isEnabled = true
|
binding.buttonConnect.isEnabled = true
|
||||||
binding.progressCircular.isVisible = false
|
binding.progressCircular.isVisible = false
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
binding.editTextServerAddressLayout.apply {
|
||||||
(binding.editTextServerAddress as AppCompatEditText).apply {
|
error = uiState.message
|
||||||
error = uiState.message
|
isEnabled = true
|
||||||
isEnabled = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.editTextServerAddressLayout!!.apply {
|
|
||||||
error = uiState.message
|
|
||||||
isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateLoading() {
|
private fun bindUiStateLoading() {
|
||||||
binding.buttonConnect.isEnabled = false
|
binding.buttonConnect.isEnabled = false
|
||||||
binding.progressCircular.isVisible = true
|
binding.progressCircular.isVisible = true
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
binding.editTextServerAddressLayout.apply {
|
||||||
(binding.editTextServerAddress as AppCompatEditText).apply {
|
error = null
|
||||||
error = null
|
isEnabled = false
|
||||||
isEnabled = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.editTextServerAddressLayout!!.apply {
|
|
||||||
error = null
|
|
||||||
isEnabled = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
|
@ -10,7 +8,6 @@ import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.view.MenuHost
|
import androidx.core.view.MenuHost
|
||||||
import androidx.core.view.MenuProvider
|
import androidx.core.view.MenuProvider
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -22,7 +19,6 @@ import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.recyclerview.widget.LinearSnapHelper
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemPagingAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemPagingAdapter
|
||||||
|
@ -42,7 +38,6 @@ import org.jellyfin.sdk.model.api.SortOrder
|
||||||
class LibraryFragment : Fragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentLibraryBinding
|
private lateinit var binding: FragmentLibraryBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
private val viewModel: LibraryViewModel by viewModels()
|
private val viewModel: LibraryViewModel by viewModels()
|
||||||
private val args: LibraryFragmentArgs by navArgs()
|
private val args: LibraryFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
@ -57,8 +52,6 @@ class LibraryFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentLibraryBinding.inflate(inflater, container, false)
|
binding = FragmentLibraryBinding.inflate(inflater, container, false)
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +98,6 @@ class LibraryFragment : Fragment() {
|
||||||
viewLifecycleOwner, Lifecycle.State.RESUMED
|
viewLifecycleOwner, Lifecycle.State.RESUMED
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.title?.text = args.libraryName
|
|
||||||
|
|
||||||
binding.errorLayout.errorRetryButton.setOnClickListener {
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
viewModel.loadItems(args.libraryId, args.libraryType)
|
viewModel.loadItems(args.libraryId, args.libraryType)
|
||||||
}
|
}
|
||||||
|
@ -118,11 +109,6 @@ class LibraryFragment : Fragment() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
|
||||||
val snapHelper = LinearSnapHelper()
|
|
||||||
snapHelper.attachToRecyclerView(binding.itemsRecyclerView)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.itemsRecyclerView.adapter =
|
binding.itemsRecyclerView.adapter =
|
||||||
ViewItemPagingAdapter(
|
ViewItemPagingAdapter(
|
||||||
ViewItemPagingAdapter.OnClickListener { item ->
|
ViewItemPagingAdapter.OnClickListener { item ->
|
||||||
|
@ -205,22 +191,12 @@ class LibraryFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToMediaInfoFragment(item: BaseItemDto) {
|
private fun navigateToMediaInfoFragment(item: BaseItemDto) {
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
findNavController().navigate(
|
||||||
findNavController().navigate(
|
LibraryFragmentDirections.actionLibraryFragmentToMediaInfoFragment(
|
||||||
LibraryFragmentDirections.actionLibraryFragmentToMediaDetailFragment(
|
item.id,
|
||||||
item.id,
|
item.name,
|
||||||
item.name,
|
item.type
|
||||||
item.type
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
} else {
|
)
|
||||||
findNavController().navigate(
|
|
||||||
LibraryFragmentDirections.actionLibraryFragmentToMediaInfoFragment(
|
|
||||||
item.id,
|
|
||||||
item.name,
|
|
||||||
item.type
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.appcompat.widget.AppCompatEditText
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
@ -31,7 +28,6 @@ import timber.log.Timber
|
||||||
class LoginFragment : Fragment() {
|
class LoginFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentLoginBinding
|
private lateinit var binding: FragmentLoginBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
private val viewModel: LoginViewModel by viewModels()
|
private val viewModel: LoginViewModel by viewModels()
|
||||||
private val args: LoginFragmentArgs by navArgs()
|
private val args: LoginFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
@ -47,8 +43,6 @@ class LoginFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentLoginBinding.inflate(inflater)
|
binding = FragmentLoginBinding.inflate(inflater)
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
if (args.reLogin) {
|
if (args.reLogin) {
|
||||||
appPreferences.currentServer?.let { currentServerId ->
|
appPreferences.currentServer?.let { currentServerId ->
|
||||||
|
@ -117,49 +111,28 @@ class LoginFragment : Fragment() {
|
||||||
private fun bindUiStateNormal() {
|
private fun bindUiStateNormal() {
|
||||||
binding.buttonLogin.isEnabled = true
|
binding.buttonLogin.isEnabled = true
|
||||||
binding.progressCircular.isVisible = false
|
binding.progressCircular.isVisible = false
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
binding.editTextUsernameLayout.isEnabled = true
|
||||||
(binding.editTextUsername as AppCompatEditText).isEnabled = true
|
binding.editTextPasswordLayout.isEnabled = true
|
||||||
(binding.editTextPassword as AppCompatEditText).isEnabled = true
|
|
||||||
} else {
|
|
||||||
binding.editTextUsernameLayout!!.isEnabled = true
|
|
||||||
binding.editTextPasswordLayout!!.isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateError(uiState: LoginViewModel.UiState.Error) {
|
private fun bindUiStateError(uiState: LoginViewModel.UiState.Error) {
|
||||||
binding.buttonLogin.isEnabled = true
|
binding.buttonLogin.isEnabled = true
|
||||||
binding.progressCircular.isVisible = false
|
binding.progressCircular.isVisible = false
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
binding.editTextUsernameLayout.apply {
|
||||||
(binding.editTextUsername as AppCompatEditText).apply {
|
error = uiState.message
|
||||||
error = uiState.message
|
isEnabled = true
|
||||||
isEnabled = true
|
|
||||||
}
|
|
||||||
(binding.editTextPassword as AppCompatEditText).isEnabled = true
|
|
||||||
} else {
|
|
||||||
binding.editTextUsernameLayout!!.apply {
|
|
||||||
error = uiState.message
|
|
||||||
isEnabled = true
|
|
||||||
}
|
|
||||||
binding.editTextPasswordLayout!!.isEnabled = true
|
|
||||||
}
|
}
|
||||||
|
binding.editTextPasswordLayout.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateLoading() {
|
private fun bindUiStateLoading() {
|
||||||
binding.buttonLogin.isEnabled = false
|
binding.buttonLogin.isEnabled = false
|
||||||
binding.progressCircular.isVisible = true
|
binding.progressCircular.isVisible = true
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
binding.editTextUsernameLayout.apply {
|
||||||
(binding.editTextUsername as AppCompatEditText).apply {
|
error = null
|
||||||
error = null
|
isEnabled = false
|
||||||
isEnabled = false
|
|
||||||
}
|
|
||||||
(binding.editTextPassword as AppCompatEditText).isEnabled = false
|
|
||||||
} else {
|
|
||||||
binding.editTextUsernameLayout!!.apply {
|
|
||||||
error = null
|
|
||||||
isEnabled = false
|
|
||||||
}
|
|
||||||
binding.editTextPasswordLayout!!.isEnabled = false
|
|
||||||
}
|
}
|
||||||
|
binding.editTextPasswordLayout.isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUsersStateUsers(usersState: LoginViewModel.UsersState.Users) {
|
private fun bindUsersStateUsers(usersState: LoginViewModel.UsersState.Users) {
|
||||||
|
@ -179,10 +152,6 @@ class LoginFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToHomeFragment() {
|
private fun navigateToHomeFragment() {
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToHomeFragment())
|
||||||
findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToHomeFragmentTv())
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToHomeFragment())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
@ -27,7 +24,6 @@ import timber.log.Timber
|
||||||
class ServerAddressesFragment : Fragment() {
|
class ServerAddressesFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentServerAddressesBinding
|
private lateinit var binding: FragmentServerAddressesBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
private val viewModel: ServerAddressesViewModel by viewModels()
|
private val viewModel: ServerAddressesViewModel by viewModels()
|
||||||
private val args: UsersFragmentArgs by navArgs()
|
private val args: UsersFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
@ -37,8 +33,6 @@ class ServerAddressesFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentServerAddressesBinding.inflate(inflater)
|
binding = FragmentServerAddressesBinding.inflate(inflater)
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
binding.addressesRecyclerView.adapter =
|
binding.addressesRecyclerView.adapter =
|
||||||
ServerAddressAdapter(
|
ServerAddressAdapter(
|
||||||
|
@ -98,10 +92,6 @@ class ServerAddressesFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToMainActivity() {
|
private fun navigateToMainActivity() {
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
findNavController().navigate(UsersFragmentDirections.actionUsersFragmentToHomeFragment())
|
||||||
findNavController().navigate(UsersFragmentDirections.actionUsersFragmentToHomeFragmentTv())
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(UsersFragmentDirections.actionUsersFragmentToHomeFragment())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
@ -24,7 +21,6 @@ import kotlinx.coroutines.launch
|
||||||
class ServerSelectFragment : Fragment() {
|
class ServerSelectFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentServerSelectBinding
|
private lateinit var binding: FragmentServerSelectBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
private val viewModel: ServerSelectViewModel by viewModels()
|
private val viewModel: ServerSelectViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
@ -33,8 +29,6 @@ class ServerSelectFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentServerSelectBinding.inflate(inflater)
|
binding = FragmentServerSelectBinding.inflate(inflater)
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
|
|
||||||
|
@ -78,10 +72,6 @@ class ServerSelectFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToMainActivity() {
|
private fun navigateToMainActivity() {
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
findNavController().navigate(ServerSelectFragmentDirections.actionServerSelectFragmentToHomeFragment())
|
||||||
findNavController().navigate(ServerSelectFragmentDirections.actionServerSelectFragmentToHomeFragmentTv())
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(ServerSelectFragmentDirections.actionServerSelectFragmentToHomeFragment())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
@ -27,7 +24,6 @@ import timber.log.Timber
|
||||||
class UsersFragment : Fragment() {
|
class UsersFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragmentUsersBinding
|
private lateinit var binding: FragmentUsersBinding
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
private val viewModel: UsersViewModel by viewModels()
|
private val viewModel: UsersViewModel by viewModels()
|
||||||
private val args: UsersFragmentArgs by navArgs()
|
private val args: UsersFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
@ -37,8 +33,6 @@ class UsersFragment : Fragment() {
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentUsersBinding.inflate(inflater)
|
binding = FragmentUsersBinding.inflate(inflater)
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
binding.usersRecyclerView.adapter =
|
binding.usersRecyclerView.adapter =
|
||||||
UserListAdapter(
|
UserListAdapter(
|
||||||
|
@ -101,10 +95,6 @@ class UsersFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToMainActivity() {
|
private fun navigateToMainActivity() {
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
findNavController().navigate(UsersFragmentDirections.actionUsersFragmentToHomeFragment())
|
||||||
findNavController().navigate(UsersFragmentDirections.actionUsersFragmentToHomeFragmentTv())
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(UsersFragmentDirections.actionUsersFragmentToHomeFragment())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.tv
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.Gravity
|
|
||||||
import android.view.KeyEvent
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.WindowManager
|
|
||||||
import android.widget.ImageButton
|
|
||||||
import android.widget.PopupWindow
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.core.content.res.ResourcesCompat
|
|
||||||
import androidx.navigation.navArgs
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import dev.jdtech.jellyfin.BasePlayerActivity
|
|
||||||
import dev.jdtech.jellyfin.PlayerActivityArgs
|
|
||||||
import dev.jdtech.jellyfin.R
|
|
||||||
import dev.jdtech.jellyfin.databinding.ActivityPlayerTvBinding
|
|
||||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
|
||||||
import dev.jdtech.jellyfin.mpv.TrackType
|
|
||||||
import dev.jdtech.jellyfin.mpv.TrackType.AUDIO
|
|
||||||
import dev.jdtech.jellyfin.mpv.TrackType.SUBTITLE
|
|
||||||
import dev.jdtech.jellyfin.tv.ui.TrackSelectorAdapter
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
internal class TvPlayerActivity : BasePlayerActivity() {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityPlayerTvBinding
|
|
||||||
override val viewModel: PlayerActivityViewModel by viewModels()
|
|
||||||
private val args: PlayerActivityArgs by navArgs()
|
|
||||||
private var displayedPopup: PopupWindow? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
Timber.d("Player activity created.")
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
binding = ActivityPlayerTvBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
|
||||||
|
|
||||||
binding.playerView.player = viewModel.player
|
|
||||||
val playerControls = binding.playerView.findViewById<View>(R.id.player_controls)
|
|
||||||
configureInsets(playerControls)
|
|
||||||
|
|
||||||
bind()
|
|
||||||
viewModel.initializePlayer(args.items)
|
|
||||||
hideSystemUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
|
||||||
return if (!binding.playerView.isControllerFullyVisible) {
|
|
||||||
binding.playerView.showController()
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bind() = with(binding.playerView) {
|
|
||||||
val videoNameTextView = findViewById<TextView>(R.id.video_name)
|
|
||||||
viewModel.currentItemTitle.observe(this@TvPlayerActivity) { title ->
|
|
||||||
videoNameTextView.text = title
|
|
||||||
}
|
|
||||||
|
|
||||||
findViewById<ImageButton>(R.id.exo_play_pause).apply {
|
|
||||||
setOnClickListener {
|
|
||||||
when {
|
|
||||||
viewModel.player.isPlaying -> {
|
|
||||||
viewModel.player.pause()
|
|
||||||
setImageDrawable(
|
|
||||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
viewModel.player.isLoading -> Unit
|
|
||||||
else -> {
|
|
||||||
viewModel.player.play()
|
|
||||||
setImageDrawable(
|
|
||||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findViewById<View>(R.id.back_button).setOnClickListener {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
bindAudioControl()
|
|
||||||
bindSubtitleControl()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindAudioControl() {
|
|
||||||
val audioBtn = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track)
|
|
||||||
|
|
||||||
audioBtn.setOnFocusChangeListener { _, hasFocus ->
|
|
||||||
displayedPopup = if (hasFocus) {
|
|
||||||
val items = viewModel.currentAudioTracks.toUiTrack()
|
|
||||||
audioBtn.showPopupWindowAbove(items, AUDIO)
|
|
||||||
} else {
|
|
||||||
displayedPopup?.dismiss()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindSubtitleControl() {
|
|
||||||
val subtitleBtn = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle)
|
|
||||||
|
|
||||||
subtitleBtn.setOnFocusChangeListener { v, hasFocus ->
|
|
||||||
v.isFocusable = true
|
|
||||||
displayedPopup = if (hasFocus) {
|
|
||||||
val items = viewModel.currentSubtitleTracks.toUiTrack()
|
|
||||||
subtitleBtn.showPopupWindowAbove(items, SUBTITLE)
|
|
||||||
} else {
|
|
||||||
displayedPopup?.dismiss()
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<MPVPlayer.Companion.Track>.toUiTrack() = map { track ->
|
|
||||||
TrackSelectorAdapter.Track(
|
|
||||||
title = track.title,
|
|
||||||
language = track.lang,
|
|
||||||
codec = track.codec,
|
|
||||||
selected = track.selected,
|
|
||||||
playerTrack = track
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun View.showPopupWindowAbove(
|
|
||||||
items: List<TrackSelectorAdapter.Track>,
|
|
||||||
type: TrackType
|
|
||||||
): PopupWindow {
|
|
||||||
val popup = PopupWindow(this.context)
|
|
||||||
popup.contentView = LayoutInflater.from(context).inflate(R.layout.track_selector, null)
|
|
||||||
val recyclerView = popup.contentView.findViewById<RecyclerView>(R.id.track_selector)
|
|
||||||
|
|
||||||
recyclerView.adapter = TrackSelectorAdapter(items, viewModel, type) { popup.dismiss() }
|
|
||||||
|
|
||||||
val startViewCoords = IntArray(2)
|
|
||||||
getLocationInWindow(startViewCoords)
|
|
||||||
|
|
||||||
val itemHeight = resources.getDimension(R.dimen.track_selection_item_height).toInt()
|
|
||||||
val totalHeight = items.size * itemHeight
|
|
||||||
|
|
||||||
popup.showAsDropDown(
|
|
||||||
binding.root,
|
|
||||||
startViewCoords.first(),
|
|
||||||
startViewCoords.last() - totalHeight,
|
|
||||||
Gravity.TOP
|
|
||||||
)
|
|
||||||
|
|
||||||
return popup
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,209 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.tv.ui
|
|
||||||
|
|
||||||
import android.app.UiModeManager
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.KeyEvent.KEYCODE_DPAD_DOWN
|
|
||||||
import android.view.KeyEvent.KEYCODE_DPAD_DOWN_LEFT
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.ImageButton
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.leanback.app.BrowseSupportFragment
|
|
||||||
import androidx.leanback.widget.ArrayObjectAdapter
|
|
||||||
import androidx.leanback.widget.DiffCallback
|
|
||||||
import androidx.leanback.widget.HeaderItem
|
|
||||||
import androidx.leanback.widget.ListRow
|
|
||||||
import androidx.leanback.widget.ListRowPresenter
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import dev.jdtech.jellyfin.R
|
|
||||||
import dev.jdtech.jellyfin.adapters.HomeItem
|
|
||||||
import dev.jdtech.jellyfin.fragments.HomeFragmentDirections
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.HomeViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
internal class HomeFragment : BrowseSupportFragment() {
|
|
||||||
|
|
||||||
private val viewModel: HomeViewModel by viewModels()
|
|
||||||
|
|
||||||
private lateinit var rowsAdapter: ArrayObjectAdapter
|
|
||||||
private lateinit var uiModeManager: UiModeManager
|
|
||||||
|
|
||||||
private val adapterMap = mutableMapOf<String, ArrayObjectAdapter>()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
uiModeManager =
|
|
||||||
requireContext().getSystemService(AppCompatActivity.UI_MODE_SERVICE) as UiModeManager
|
|
||||||
|
|
||||||
val rowPresenter = ListRowPresenter()
|
|
||||||
rowPresenter.selectEffectEnabled = false
|
|
||||||
|
|
||||||
headersState = HEADERS_ENABLED
|
|
||||||
rowsAdapter = ArrayObjectAdapter(rowPresenter)
|
|
||||||
adapter = rowsAdapter
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
view.findViewById<ImageButton>(R.id.settings).apply {
|
|
||||||
setOnKeyListener { _, keyCode, _ ->
|
|
||||||
if (keyCode == KEYCODE_DPAD_DOWN || keyCode == KEYCODE_DPAD_DOWN_LEFT) {
|
|
||||||
headersSupportFragment.view?.requestFocus()
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setOnClickListener { navigateToSettingsFragment() }
|
|
||||||
}
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
viewModel.uiState.collect { uiState ->
|
|
||||||
Timber.d("$uiState")
|
|
||||||
when (uiState) {
|
|
||||||
is HomeViewModel.UiState.Normal -> bindUiStateNormal(uiState)
|
|
||||||
is HomeViewModel.UiState.Loading -> bindUiStateLoading()
|
|
||||||
is HomeViewModel.UiState.Error -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
viewModel.loadData(includeLibraries = true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val diffCallbackListRow = object : DiffCallback<ListRow>() {
|
|
||||||
override fun areItemsTheSame(oldItem: ListRow, newItem: ListRow): Boolean {
|
|
||||||
return oldItem.id == newItem.id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: ListRow, newItem: ListRow): Boolean {
|
|
||||||
Timber.d((oldItem.adapter.size() == newItem.adapter.size()).toString())
|
|
||||||
return oldItem.adapter.size() == newItem.adapter.size()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindUiStateNormal(uiState: HomeViewModel.UiState.Normal) {
|
|
||||||
progressBarManager.hide()
|
|
||||||
uiState.apply {
|
|
||||||
rowsAdapter.setItems(homeItems.map { homeItem -> homeItem.toListRow() }, diffCallbackListRow)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindUiStateLoading() {
|
|
||||||
progressBarManager.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun HomeItem.toListRow(): ListRow {
|
|
||||||
return ListRow(
|
|
||||||
toHeader(),
|
|
||||||
toItems()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun HomeItem.toHeader(): HeaderItem {
|
|
||||||
return when (this) {
|
|
||||||
is HomeItem.Libraries -> HeaderItem(section.name)
|
|
||||||
is HomeItem.Section -> HeaderItem(homeSection.name)
|
|
||||||
is HomeItem.ViewItem -> HeaderItem(
|
|
||||||
String.format(
|
|
||||||
resources.getString(R.string.latest_library),
|
|
||||||
view.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val diffCallback = object : DiffCallback<BaseItemDto>() {
|
|
||||||
override fun areItemsTheSame(oldItem: BaseItemDto, newItem: BaseItemDto): Boolean {
|
|
||||||
return oldItem.id == newItem.id
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: BaseItemDto, newItem: BaseItemDto): Boolean {
|
|
||||||
return oldItem == newItem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun HomeItem.toItems(): ArrayObjectAdapter {
|
|
||||||
val name = this.toHeader().name
|
|
||||||
val items = when (this) {
|
|
||||||
is HomeItem.Libraries -> section.items
|
|
||||||
is HomeItem.Section -> homeSection.items
|
|
||||||
is HomeItem.ViewItem -> view.items
|
|
||||||
}
|
|
||||||
if (name in adapterMap) {
|
|
||||||
adapterMap[name]?.setItems(items, diffCallback)
|
|
||||||
} else {
|
|
||||||
adapterMap[name] = when (this) {
|
|
||||||
is HomeItem.Libraries -> ArrayObjectAdapter(
|
|
||||||
LibaryItemPresenter { item ->
|
|
||||||
navigateToLibraryFragment(item)
|
|
||||||
}
|
|
||||||
).apply { setItems(items, diffCallback) }
|
|
||||||
is HomeItem.Section -> ArrayObjectAdapter(
|
|
||||||
DynamicMediaItemPresenter { item ->
|
|
||||||
navigateToMediaDetailFragment(item)
|
|
||||||
}
|
|
||||||
).apply { setItems(items, diffCallback) }
|
|
||||||
is HomeItem.ViewItem -> ArrayObjectAdapter(
|
|
||||||
MediaItemPresenter { item ->
|
|
||||||
navigateToMediaDetailFragment(item)
|
|
||||||
}
|
|
||||||
).apply { setItems(items, diffCallback) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return adapterMap[name]!!
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun navigateToLibraryFragment(library: BaseItemDto) {
|
|
||||||
if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION) {
|
|
||||||
findNavController().navigate(
|
|
||||||
dev.jdtech.jellyfin.tv.ui.HomeFragmentDirections.actionHomeFragmentToLibraryFragment(
|
|
||||||
library.id,
|
|
||||||
library.name,
|
|
||||||
library.collectionType
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
findNavController().navigate(
|
|
||||||
HomeFragmentDirections.actionNavigationHomeToLibraryFragment(
|
|
||||||
library.id,
|
|
||||||
library.name,
|
|
||||||
library.collectionType
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun navigateToMediaDetailFragment(item: BaseItemDto) {
|
|
||||||
findNavController().navigate(
|
|
||||||
HomeFragmentDirections.actionHomeFragmentToMediaDetailFragment(
|
|
||||||
item.id,
|
|
||||||
item.seriesName ?: item.name,
|
|
||||||
item.type
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun navigateToSettingsFragment() {
|
|
||||||
findNavController().navigate(
|
|
||||||
HomeFragmentDirections.actionHomeFragmentToSettingsFragment()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,258 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.tv.ui
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.navigation.fragment.navArgs
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import dev.jdtech.jellyfin.R
|
|
||||||
import dev.jdtech.jellyfin.adapters.PersonListAdapter
|
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
|
||||||
import dev.jdtech.jellyfin.bindBaseItemImage
|
|
||||||
import dev.jdtech.jellyfin.databinding.MediaDetailFragmentBinding
|
|
||||||
import dev.jdtech.jellyfin.dialogs.VideoVersionDialogFragment
|
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel.PlayerItemError
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel.PlayerItems
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
internal class MediaDetailFragment : Fragment() {
|
|
||||||
|
|
||||||
private lateinit var binding: MediaDetailFragmentBinding
|
|
||||||
|
|
||||||
private val viewModel: MediaInfoViewModel by viewModels()
|
|
||||||
private val playerViewModel: PlayerViewModel by viewModels()
|
|
||||||
|
|
||||||
private val args: MediaDetailFragmentArgs by navArgs()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
viewModel.loadData(args.itemId, args.itemType)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
binding = MediaDetailFragmentBinding.inflate(inflater)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
||||||
viewModel.uiState.collect { uiState ->
|
|
||||||
Timber.d("$uiState")
|
|
||||||
when (uiState) {
|
|
||||||
is MediaInfoViewModel.UiState.Normal -> bindUiStateNormal(uiState)
|
|
||||||
is MediaInfoViewModel.UiState.Loading -> Unit
|
|
||||||
is MediaInfoViewModel.UiState.Error -> Unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val seasonsAdapter = ViewItemListAdapter(
|
|
||||||
fixedWidth = true,
|
|
||||||
onClickListener = ViewItemListAdapter.OnClickListener {}
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.seasonsRow.gridView.adapter = seasonsAdapter
|
|
||||||
binding.seasonsRow.gridView.verticalSpacing = 25
|
|
||||||
|
|
||||||
val castAdapter = PersonListAdapter {
|
|
||||||
Toast.makeText(requireContext(), "Not yet implemented", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.castRow.gridView.adapter = castAdapter
|
|
||||||
binding.castRow.gridView.verticalSpacing = 25
|
|
||||||
|
|
||||||
playerViewModel.onPlaybackRequested(lifecycleScope) { playerItems ->
|
|
||||||
when (playerItems) {
|
|
||||||
is PlayerItemError -> bindPlayerItemsError(playerItems)
|
|
||||||
is PlayerItems -> bindPlayerItems(playerItems)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.playButton.setOnClickListener {
|
|
||||||
binding.playButton.setImageResource(android.R.color.transparent)
|
|
||||||
binding.progressCircular.isVisible = true
|
|
||||||
viewModel.item?.let { item ->
|
|
||||||
playerViewModel.loadPlayerItems(item) {
|
|
||||||
VideoVersionDialogFragment(item, playerViewModel).show(
|
|
||||||
parentFragmentManager,
|
|
||||||
"videoversiondialog"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.trailerButton.setOnClickListener {
|
|
||||||
if (viewModel.item?.remoteTrailers.isNullOrEmpty()) return@setOnClickListener
|
|
||||||
val intent = Intent(
|
|
||||||
Intent.ACTION_VIEW,
|
|
||||||
Uri.parse(viewModel.item?.remoteTrailers?.get(0)?.url)
|
|
||||||
)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.checkButton.setOnClickListener {
|
|
||||||
when (viewModel.played) {
|
|
||||||
true -> {
|
|
||||||
viewModel.markAsUnplayed(args.itemId)
|
|
||||||
binding.checkButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
R.color.white,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
false -> {
|
|
||||||
viewModel.markAsPlayed(args.itemId)
|
|
||||||
binding.checkButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
R.color.red,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.favoriteButton.setOnClickListener {
|
|
||||||
when (viewModel.favorite) {
|
|
||||||
true -> {
|
|
||||||
viewModel.unmarkAsFavorite(args.itemId)
|
|
||||||
binding.favoriteButton.setImageResource(R.drawable.ic_heart)
|
|
||||||
binding.favoriteButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
R.color.white,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
false -> {
|
|
||||||
viewModel.markAsFavorite(args.itemId)
|
|
||||||
binding.favoriteButton.setImageResource(R.drawable.ic_heart_filled)
|
|
||||||
binding.favoriteButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
R.color.red,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindUiStateNormal(uiState: MediaInfoViewModel.UiState.Normal) {
|
|
||||||
uiState.apply {
|
|
||||||
binding.seasonsLayout.isVisible = seasons.isNotEmpty()
|
|
||||||
val seasonsAdapter = binding.seasonsRow.gridView.adapter as ViewItemListAdapter
|
|
||||||
seasonsAdapter.submitList(seasons)
|
|
||||||
binding.castLayout.isVisible = actors.isNotEmpty()
|
|
||||||
val actorsAdapter = binding.castRow.gridView.adapter as PersonListAdapter
|
|
||||||
actorsAdapter.submitList(actors)
|
|
||||||
|
|
||||||
// Check icon
|
|
||||||
when (played) {
|
|
||||||
true -> {
|
|
||||||
binding.checkButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
R.color.red,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
false -> {
|
|
||||||
val typedValue = TypedValue()
|
|
||||||
requireActivity().theme.resolveAttribute(R.attr.colorOnSecondaryContainer, typedValue, true)
|
|
||||||
/*binding.checkButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
typedValue.resourceId,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Favorite icon
|
|
||||||
val favoriteDrawable = when (favorite) {
|
|
||||||
true -> R.drawable.ic_heart_filled
|
|
||||||
false -> R.drawable.ic_heart
|
|
||||||
}
|
|
||||||
binding.favoriteButton.setImageResource(favoriteDrawable)
|
|
||||||
if (favorite) binding.favoriteButton.imageTintList = ColorStateList.valueOf(
|
|
||||||
resources.getColor(
|
|
||||||
R.color.red,
|
|
||||||
requireActivity().theme
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.title.text = item.name
|
|
||||||
binding.genres.text = genresString
|
|
||||||
binding.year.text = dateString
|
|
||||||
binding.playtime.text = runTime
|
|
||||||
binding.officialRating.text = item.officialRating
|
|
||||||
binding.communityRating.text = item.communityRating.toString()
|
|
||||||
binding.description.text = item.overview
|
|
||||||
bindBaseItemImage(binding.poster, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindPlayerItems(items: PlayerItems) {
|
|
||||||
navigateToPlayerActivity(items.items.toTypedArray())
|
|
||||||
binding.playButton.setImageDrawable(
|
|
||||||
ContextCompat.getDrawable(
|
|
||||||
requireActivity(),
|
|
||||||
R.drawable.ic_play
|
|
||||||
)
|
|
||||||
)
|
|
||||||
binding.progressCircular.visibility = View.INVISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun bindPlayerItemsError(error: PlayerItemError) {
|
|
||||||
Timber.e(error.error.message)
|
|
||||||
|
|
||||||
binding.errorLayout.errorPanel.isVisible = true
|
|
||||||
binding.playButton.setImageDrawable(
|
|
||||||
ContextCompat.getDrawable(
|
|
||||||
requireActivity(),
|
|
||||||
R.drawable.ic_play
|
|
||||||
)
|
|
||||||
)
|
|
||||||
binding.progressCircular.visibility = View.INVISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun navigateToPlayerActivity(
|
|
||||||
playerItems: Array<PlayerItem>,
|
|
||||||
) {
|
|
||||||
findNavController().navigate(
|
|
||||||
MediaDetailFragmentDirections.actionMediaDetailFragmentToPlayerActivity(
|
|
||||||
playerItems
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.tv.ui
|
|
||||||
|
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.leanback.widget.Presenter
|
|
||||||
import dev.jdtech.jellyfin.R
|
|
||||||
import dev.jdtech.jellyfin.databinding.BaseItemBinding
|
|
||||||
import dev.jdtech.jellyfin.databinding.CollectionItemBinding
|
|
||||||
import dev.jdtech.jellyfin.databinding.HomeEpisodeItemBinding
|
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
|
||||||
|
|
||||||
class LibaryItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Presenter() {
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
|
||||||
return ViewHolder(
|
|
||||||
CollectionItemBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
).root
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) {
|
|
||||||
if (item is BaseItemDto) {
|
|
||||||
DataBindingUtil.getBinding<CollectionItemBinding>(viewHolder.view)?.apply {
|
|
||||||
this.collection = item
|
|
||||||
viewHolder.view.setOnClickListener { onClick(item) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) = Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
class MediaItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Presenter() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
|
||||||
return ViewHolder(
|
|
||||||
BaseItemBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
).root
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) {
|
|
||||||
if (item is BaseItemDto) {
|
|
||||||
DataBindingUtil.getBinding<BaseItemBinding>(viewHolder.view)?.apply {
|
|
||||||
this.item = item
|
|
||||||
this.itemName.text =
|
|
||||||
if (item.type == BaseItemKind.EPISODE) item.seriesName else item.name
|
|
||||||
this.itemCount.visibility =
|
|
||||||
if (item.userData?.unplayedItemCount != null && item.userData?.unplayedItemCount!! > 0) View.VISIBLE else View.GONE
|
|
||||||
this.itemLayout.layoutParams.width =
|
|
||||||
this.itemLayout.resources.getDimension(R.dimen.overview_media_width).toInt()
|
|
||||||
(this.itemLayout.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = 0
|
|
||||||
viewHolder.view.setOnClickListener { onClick(item) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) = Unit
|
|
||||||
}
|
|
||||||
|
|
||||||
class DynamicMediaItemPresenter(private val onClick: (BaseItemDto) -> Unit) : Presenter() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
|
||||||
return ViewHolder(
|
|
||||||
HomeEpisodeItemBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
).root
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: ViewHolder, item: Any) {
|
|
||||||
if (item is BaseItemDto) {
|
|
||||||
DataBindingUtil.getBinding<HomeEpisodeItemBinding>(viewHolder.view)?.apply {
|
|
||||||
episode = item
|
|
||||||
item.userData?.playedPercentage?.toInt()?.let {
|
|
||||||
progressBar.layoutParams.width = TypedValue.applyDimension(
|
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
|
||||||
(it.times(2.24)).toFloat(), progressBar.context.resources.displayMetrics
|
|
||||||
).toInt()
|
|
||||||
progressBar.isVisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.type == BaseItemKind.MOVIE) {
|
|
||||||
primaryName.text = item.name
|
|
||||||
secondaryName.visibility = View.GONE
|
|
||||||
} else if (item.type == BaseItemKind.EPISODE) {
|
|
||||||
primaryName.text = item.seriesName
|
|
||||||
}
|
|
||||||
|
|
||||||
viewHolder.view.setOnClickListener { onClick(item) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) = Unit
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.tv.ui
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Button
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import dev.jdtech.jellyfin.R
|
|
||||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
|
||||||
import dev.jdtech.jellyfin.mpv.TrackType
|
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
|
||||||
|
|
||||||
class TrackSelectorAdapter(
|
|
||||||
private val items: List<Track>,
|
|
||||||
private val viewModel: PlayerActivityViewModel,
|
|
||||||
private val trackType: TrackType,
|
|
||||||
private val dismissWindow: () -> Unit
|
|
||||||
) : RecyclerView.Adapter<TrackSelectorAdapter.TrackSelectorViewHolder>() {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackSelectorViewHolder {
|
|
||||||
return TrackSelectorViewHolder(
|
|
||||||
LayoutInflater.from(parent.context).inflate(R.layout.track_item, parent, false)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: TrackSelectorViewHolder, position: Int) {
|
|
||||||
holder.bind(items[position], viewModel, trackType, dismissWindow)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
|
||||||
|
|
||||||
class TrackSelectorViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
|
|
||||||
|
|
||||||
fun bind(
|
|
||||||
item: Track,
|
|
||||||
viewModel: PlayerActivityViewModel,
|
|
||||||
trackType: TrackType,
|
|
||||||
dismissWindow: () -> Unit
|
|
||||||
) {
|
|
||||||
view.findViewById<Button>(R.id.track_name).apply {
|
|
||||||
text = String.format(
|
|
||||||
view.resources.getString(R.string.track_selection),
|
|
||||||
item.language,
|
|
||||||
item.title,
|
|
||||||
item.codec
|
|
||||||
)
|
|
||||||
setOnClickListener {
|
|
||||||
viewModel.switchToTrack(trackType, item.playerTrack)
|
|
||||||
dismissWindow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Track(
|
|
||||||
val title: String,
|
|
||||||
val language: String,
|
|
||||||
val codec: String,
|
|
||||||
val selected: Boolean,
|
|
||||||
val playerTrack: MPVPlayer.Companion.Track
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -4,5 +4,4 @@ import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
class MainViewModel : ViewModel() {
|
class MainViewModel : ViewModel() {
|
||||||
var startDestinationChanged = false
|
var startDestinationChanged = false
|
||||||
var startDestinationTvChanged = false
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_enabled="true" android:state_focused="true">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="@color/neutral_700"/>
|
|
||||||
<stroke android:width="2dp" android:color="@color/white" />
|
|
||||||
<corners android:radius="10dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
<item android:state_enabled="true">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="@color/neutral_700"/>
|
|
||||||
<corners android:radius="10dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</selector>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_enabled="true" android:state_focused="true">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="?attr/colorPrimary"/>
|
|
||||||
<stroke android:width="2dp" android:color="@color/white" />
|
|
||||||
<corners android:radius="10dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
<item android:state_enabled="true">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="?attr/colorPrimary"/>
|
|
||||||
<corners android:radius="10dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</selector>
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item>
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<solid android:color="?attr/colorPrimary" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
<item
|
|
||||||
android:bottom="18dp"
|
|
||||||
android:drawable="@drawable/ic_user"
|
|
||||||
android:end="18dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:start="18dp"
|
|
||||||
android:top="18dp" />
|
|
||||||
</layer-list>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout 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.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/nav_host_fragment_activity_main"
|
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="parent"
|
|
||||||
app:navGraph="@navigation/app_navigation" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
|
@ -1,80 +0,0 @@
|
||||||
<?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>
|
|
||||||
|
|
||||||
<import type="android.view.View" />
|
|
||||||
|
|
||||||
<variable
|
|
||||||
name="item"
|
|
||||||
type="org.jellyfin.sdk.model.api.BaseItemDto" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/item_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/item_image"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:itemImage="@{item}"
|
|
||||||
app:layout_constraintDimensionRatio="H,2:3"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Findroid.Image"
|
|
||||||
app:strokeColor="@null" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/item_image"
|
|
||||||
tools:text="Movie title" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_count"
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/circle_background"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@{item.userData.unplayedItemCount.toString()}"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="9" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/played_icon"
|
|
||||||
android:layout_width="24dp"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/circle_background"
|
|
||||||
android:contentDescription="@string/episode_watched_indicator"
|
|
||||||
android:padding="4dp"
|
|
||||||
android:src="@drawable/ic_check"
|
|
||||||
android:visibility="@{item.userData.played == true ? View.VISIBLE : View.GONE}"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/item_image"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/item_image" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?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"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
tools:ignore="MissingDefaultResource">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/settings"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:contentDescription="@string/title_settings"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:src="@drawable/ic_settings"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/clock"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextClock
|
|
||||||
android:id="@+id/clock"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textSize="18sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="12:00" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,49 +0,0 @@
|
||||||
<?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="collection"
|
|
||||||
type="org.jellyfin.sdk.model.api.BaseItemDto" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="240dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/collection_image"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:baseItemImage="@{collection}"
|
|
||||||
app:layout_constraintDimensionRatio="H,16:9"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Findroid.Image"
|
|
||||||
app:strokeColor="@null" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text="@{collection.name}"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/collection_image"
|
|
||||||
tools:text="Movies" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -1,60 +0,0 @@
|
||||||
<?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.models.DiscoveredServer" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="64dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/server_icon"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:background="@drawable/button_setup_background"
|
|
||||||
app:layout_constraintDimensionRatio="w,1:1"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
app:srcCompat="@drawable/ic_server"
|
|
||||||
app:tint="@android:color/white" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/server_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@{server.name}"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/server_icon"
|
|
||||||
tools:text="username" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</layout>
|
|
|
@ -1,136 +0,0 @@
|
||||||
<?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"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/player_controls"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/player_background"
|
|
||||||
tools:ignore="MissingDefaultResource"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/back_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/player_controls_exit"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_arrow_left"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/video_name"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/back_button"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="The Dawn of Despair"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/btn_audio_track"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/select_audio_track"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_speaker"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/exo_progress"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/btn_subtitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/tv_controls_margin_start"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/select_subtile_track"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_closed_caption"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/exo_progress"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/btn_audio_track"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<androidx.media3.ui.DefaultTimeBar
|
|
||||||
android:id="@+id/exo_progress"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/exo_play_pause"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
android:focusable="true"
|
|
||||||
app:played_color="?colorPrimary"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/exo_rew"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/player_controls_rewind"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_rewind"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/exo_play_pause"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/tv_controls_margin_start"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/play_button_description"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_pause"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/exo_rew"
|
|
||||||
android:focusedByDefault="true"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/exo_ffwd"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/tv_controls_margin_start"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/player_controls_fast_forward"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_fast_forward"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/exo_play_pause"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/exo_next"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/tv_controls_margin_start"
|
|
||||||
android:background="@drawable/transparent_circle_background"
|
|
||||||
android:contentDescription="@string/player_controls_skip"
|
|
||||||
android:padding="@dimen/tv_controls_padding"
|
|
||||||
android:src="@drawable/ic_skip_forward"
|
|
||||||
android:focusable="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/exo_ffwd"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,96 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView 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"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fillViewport="true"
|
|
||||||
tools:context=".fragments.AddServerFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<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/linearLayout"
|
|
||||||
android:layout_width="@dimen/setup_container_width"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
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"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:text="@string/add_server"
|
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
|
||||||
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:layout_marginBottom="12dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:listitem="@layout/discovered_server_item"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edit_text_server_address"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:autofillHints=""
|
|
||||||
android:focusedByDefault="true"
|
|
||||||
android:hint="@string/edit_text_server_address_hint"
|
|
||||||
android:imeOptions="actionGo"
|
|
||||||
android:inputType="textUri" />
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_connect"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/button_connect" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress_circular"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:elevation="8dp"
|
|
||||||
android:indeterminateTint="@color/white"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:visibility="invisible" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</ScrollView>
|
|
|
@ -1,54 +0,0 @@
|
||||||
<?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"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".fragments.LibraryFragment">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/error_layout"
|
|
||||||
layout="@layout/error_panel" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="Movies" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/items_recycler_view"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
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/title"
|
|
||||||
app:spanCount="@integer/library_columns"
|
|
||||||
tools:itemCount="14"
|
|
||||||
tools:listitem="@layout/base_item">
|
|
||||||
<requestFocus/>
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
|
||||||
android:id="@+id/loading_indicator"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView 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"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fillViewport="true"
|
|
||||||
tools:context=".fragments.LoginFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<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/linearLayout"
|
|
||||||
android:layout_width="@dimen/setup_container_width"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
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_login"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:text="@string/login"
|
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
|
||||||
android:textColor="?android:textColorPrimary" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/users_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="2"
|
|
||||||
tools:listitem="@layout/user_item"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edit_text_username"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:autofillHints="username"
|
|
||||||
android:hint="@string/edit_text_username_hint"
|
|
||||||
android:inputType="text" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/edit_text_password"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:autofillHints="password"
|
|
||||||
android:hint="@string/edit_text_password_hint"
|
|
||||||
android:imeOptions="actionGo"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
app:passwordToggleEnabled="true" />
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_login"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/button_login" />
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress_circular"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:elevation="8dp"
|
|
||||||
android:indeterminateTint="@color/white"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:visibility="invisible" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
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"
|
|
||||||
android:id="@+id/main_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/addresses_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/server_address_list_item" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_add_address"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:text="@string/add_address"
|
|
||||||
app:icon="@drawable/ic_plus" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
|
@ -1,40 +0,0 @@
|
||||||
<?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.viewmodels.ServerSelectViewModel" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/servers_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
|
||||||
app:servers="@{viewModel.servers}"
|
|
||||||
app:spanCount="10"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/server_item" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_add_server"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:text="@string/add_server" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
</layout>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
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"
|
|
||||||
android:id="@+id/main_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/users_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/user_list_item" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/button_add_user"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="bottom|end"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:text="@string/add_user"
|
|
||||||
app:icon="@drawable/ic_plus" />
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
|
@ -1,76 +0,0 @@
|
||||||
<?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="episode"
|
|
||||||
type="org.jellyfin.sdk.model.api.BaseItemDto" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="240dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/episode_image"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:baseItemImage="@{episode}"
|
|
||||||
app:layout_constraintDimensionRatio="H,16:9"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Findroid.Image"
|
|
||||||
app:strokeColor="@null" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/primary_name"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/episode_image"
|
|
||||||
tools:text="Wonder Egg Priority" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/secondary_name"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@{String.format(@string/episode_name_extended, episode.parentIndexNumber, episode.indexNumber, episode.name)}"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/primary_name"
|
|
||||||
tools:text="The Girl Flautist" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/progress_bar"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="4dp"
|
|
||||||
android:layout_marginHorizontal="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:background="@drawable/button_setup_background"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/episode_image"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
tools:layout_width="50dp"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</layout>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:focusable="true"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
tools:ignore="MissingDefaultResource"
|
|
||||||
>
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/header_icon"
|
|
||||||
android:layout_width="32dp"
|
|
||||||
android:layout_height="32dp"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/header_label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -1,258 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout 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"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:ignore="MissingDefaultResource">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/error_layout"
|
|
||||||
layout="@layout/error_panel"
|
|
||||||
tools:visibility="gone" />
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="@dimen/horizontal_margin"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="Alita: Battle Angel" />
|
|
||||||
|
|
||||||
<TextClock
|
|
||||||
android:id="@+id/clock"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="24dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:textSize="18sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="12:00" />
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/poster"
|
|
||||||
android:layout_width="320dp"
|
|
||||||
android:layout_height="180dp"
|
|
||||||
android:layout_marginStart="@dimen/horizontal_margin"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/title"
|
|
||||||
app:shapeAppearance="@style/ShapeAppearanceOverlay.Findroid.Image"
|
|
||||||
app:strokeColor="@null" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/main_info"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/horizontal_margin"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/poster"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/poster">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/info"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="12dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/year"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
tools:text="2019" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/playtime"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
tools:text="122 min" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/official_rating"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
tools:text="PG-13" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/community_rating"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:drawablePadding="4dp"
|
|
||||||
android:gravity="bottom"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
app:drawableStartCompat="@drawable/ic_star"
|
|
||||||
app:drawableTint="@color/yellow"
|
|
||||||
tools:text="7.3" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/genres"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
|
||||||
tools:text="Action, Science Fiction, Adventure" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/description"
|
|
||||||
android:layout_width="400dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="5"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
|
||||||
tools:text="An angel falls. A warrior rises. When Alita awakens with no memory of who she is in a future world she does not recognize, she is taken in by Ido, a compassionate doctor who realizes that somewhere in this abandoned cyborg shell is the heart and soul of a young woman with an extraordinary past." />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/buttons"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="12dp">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/play_button"
|
|
||||||
android:layout_width="72dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:background="@drawable/button_setup_background"
|
|
||||||
android:contentDescription="@string/play_button_description"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:focusedByDefault="true"
|
|
||||||
android:nextFocusLeft="@id/play_button"
|
|
||||||
android:paddingHorizontal="24dp"
|
|
||||||
android:paddingVertical="12dp"
|
|
||||||
android:src="@drawable/ic_play">
|
|
||||||
|
|
||||||
<requestFocus />
|
|
||||||
</ImageButton>
|
|
||||||
|
|
||||||
<ProgressBar
|
|
||||||
android:id="@+id/progress_circular"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:elevation="8dp"
|
|
||||||
android:indeterminateTint="@color/white"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:visibility="invisible" />
|
|
||||||
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/trailer_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:background="@drawable/button_accent_background"
|
|
||||||
android:contentDescription="@string/trailer_button_description"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:src="@drawable/ic_film" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/check_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:background="@drawable/button_accent_background"
|
|
||||||
android:contentDescription="@string/check_button_description"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:src="@drawable/ic_check" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/favorite_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/button_accent_background"
|
|
||||||
android:contentDescription="@string/favorite_button_description"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:src="@drawable/ic_heart" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/seasons_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/main_info">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/season_title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/horizontal_margin"
|
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
android:text="@string/seasons"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />
|
|
||||||
|
|
||||||
<androidx.leanback.widget.ListRowView
|
|
||||||
android:id="@+id/seasons_row"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/cast_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/seasons_layout">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/cast_title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/horizontal_margin"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/cast_amp_crew"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />
|
|
||||||
|
|
||||||
<androidx.leanback.widget.ListRowView
|
|
||||||
android:id="@+id/cast_row"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
|
@ -1,54 +0,0 @@
|
||||||
<?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="person"
|
|
||||||
type="org.jellyfin.sdk.model.api.BaseItemPerson" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/item_layout"
|
|
||||||
android:layout_width="110dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="8dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/person_image"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="160dp"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:personImage="@{person}"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Findroid.Image"
|
|
||||||
app:strokeColor="@null" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/person_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@{person.name}"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
tools:text="Rosa Salazar" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/person_role"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@{person.role}"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodySmall"
|
|
||||||
tools:text="Alita" />
|
|
||||||
</LinearLayout>
|
|
||||||
</layout>
|
|
|
@ -1,58 +0,0 @@
|
||||||
<?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.models.Server" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:foreground="@drawable/ripple_background"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
android:paddingBottom="24dp">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/server_icon"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:background="@drawable/button_setup_background"
|
|
||||||
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">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
app:srcCompat="@drawable/ic_server" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/server_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@{server.name}"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
|
||||||
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>
|
|
|
@ -1,26 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="@dimen/track_selection_item_height"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:ignore="MissingDefaultResource"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true"
|
|
||||||
>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/track_name"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
android:textSize="@dimen/track_selection_item_text_size"
|
|
||||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:ellipsize="end"
|
|
||||||
tools:text="subtitle track"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.leanback.widget.BrowseFrameLayout
|
|
||||||
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"
|
|
||||||
android:id="@+id/selector_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:descendantFocusability="afterDescendants"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:ignore="MissingDefaultResource"
|
|
||||||
>
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/track_selector"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:descendantFocusability="afterDescendants"
|
|
||||||
android:orientation="vertical"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:ignore="MissingDefaultResource"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</androidx.leanback.widget.BrowseFrameLayout>
|
|
|
@ -1,53 +0,0 @@
|
||||||
<?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="user"
|
|
||||||
type="dev.jdtech.jellyfin.models.User" />
|
|
||||||
</data>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="64dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="12dp"
|
|
||||||
android:background="@drawable/focus_border"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:focusableInTouchMode="true">
|
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
|
||||||
android:id="@+id/user_image"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:duplicateParentState="true"
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
app:layout_constraintDimensionRatio="w,1:1"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Findroid.Image"
|
|
||||||
app:userImage="@{user}" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/user_name"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:ellipsize="end"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:text="@{user.name}"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/user_image"
|
|
||||||
tools:text="username" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</layout>
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout 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.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/nav_host_fragment_activity_main"
|
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:layout_constraintBottom_toTopOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="parent"
|
|
||||||
app:navGraph="@navigation/app_navigation" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<FrameLayout 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"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".PlayerActivity">
|
|
||||||
|
|
||||||
<androidx.media3.ui.PlayerView
|
|
||||||
android:id="@+id/player_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/black"
|
|
||||||
app:animation_enabled="false"
|
|
||||||
app:show_buffering="always" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_banner_background"/>
|
|
||||||
<foreground android:drawable="@drawable/ic_banner_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
|
@ -39,42 +39,11 @@
|
||||||
app:destination="@id/addServerFragment"
|
app:destination="@id/addServerFragment"
|
||||||
app:popUpTo="@id/homeFragment"
|
app:popUpTo="@id/homeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_homeFragment_to_mediaDetailFragment"
|
|
||||||
app:destination="@id/mediaDetailFragment" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeFragment_to_searchResultFragment"
|
android:id="@+id/action_homeFragment_to_searchResultFragment"
|
||||||
app:destination="@id/searchResultFragment" />
|
app:destination="@id/searchResultFragment" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
|
||||||
android:id="@+id/homeFragmentTv"
|
|
||||||
android:name="dev.jdtech.jellyfin.tv.ui.HomeFragment"
|
|
||||||
android:label="@string/title_home" >
|
|
||||||
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_homeFragment_to_mediaDetailFragment"
|
|
||||||
app:destination="@id/mediaDetailFragment" />
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_homeFragment_to_settingsFragment"
|
|
||||||
app:destination="@id/twoPaneSettingsFragment"
|
|
||||||
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" />
|
|
||||||
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_homeFragment_to_addServerFragment"
|
|
||||||
app:destination="@id/addServerFragment"
|
|
||||||
app:popUpTo="@id/homeFragment"
|
|
||||||
app:popUpToInclusive="true" />
|
|
||||||
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_homeFragment_to_libraryFragment"
|
|
||||||
app:destination="@id/libraryFragment" />
|
|
||||||
|
|
||||||
</fragment>
|
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/mediaFragment"
|
android:id="@+id/mediaFragment"
|
||||||
android:name="dev.jdtech.jellyfin.fragments.MediaFragment"
|
android:name="dev.jdtech.jellyfin.fragments.MediaFragment"
|
||||||
|
@ -133,9 +102,6 @@
|
||||||
app:exitAnim="@anim/nav_default_exit_anim"
|
app:exitAnim="@anim/nav_default_exit_anim"
|
||||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_libraryFragment_to_mediaDetailFragment"
|
|
||||||
app:destination="@id/mediaDetailFragment" />
|
|
||||||
<argument
|
<argument
|
||||||
android:name="libraryType"
|
android:name="libraryType"
|
||||||
android:defaultValue="unknown"
|
android:defaultValue="unknown"
|
||||||
|
@ -181,29 +147,7 @@
|
||||||
app:argType="boolean"
|
app:argType="boolean"
|
||||||
android:defaultValue="false" />
|
android:defaultValue="false" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
|
||||||
android:id="@+id/mediaDetailFragment"
|
|
||||||
android:name="dev.jdtech.jellyfin.tv.ui.MediaDetailFragment"
|
|
||||||
android:label="{itemName}"
|
|
||||||
tools:layout="@layout/media_detail_fragment">
|
|
||||||
|
|
||||||
<argument
|
|
||||||
android:name="itemId"
|
|
||||||
app:argType="java.util.UUID" />
|
|
||||||
<argument
|
|
||||||
android:name="itemName"
|
|
||||||
android:defaultValue="Media Info"
|
|
||||||
app:argType="string"
|
|
||||||
app:nullable="true" />
|
|
||||||
<argument
|
|
||||||
android:name="itemType"
|
|
||||||
app:argType="org.jellyfin.sdk.model.api.BaseItemKind"
|
|
||||||
android:defaultValue="MOVIE" />
|
|
||||||
|
|
||||||
<action
|
|
||||||
android:id="@+id/action_mediaDetailFragment_to_playerActivity"
|
|
||||||
app:destination="@id/playerActivityTv" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/seasonFragment"
|
android:id="@+id/seasonFragment"
|
||||||
android:name="dev.jdtech.jellyfin.fragments.SeasonFragment"
|
android:name="dev.jdtech.jellyfin.fragments.SeasonFragment"
|
||||||
|
@ -334,11 +278,6 @@
|
||||||
app:destination="@id/homeFragment"
|
app:destination="@id/homeFragment"
|
||||||
app:popUpTo="@id/homeFragment"
|
app:popUpTo="@id/homeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_serverSelectFragment_to_homeFragmentTv"
|
|
||||||
app:destination="@id/homeFragmentTv"
|
|
||||||
app:popUpTo="@id/homeFragmentTv"
|
|
||||||
app:popUpToInclusive="true" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/loginFragment"
|
android:id="@+id/loginFragment"
|
||||||
|
@ -350,11 +289,6 @@
|
||||||
app:destination="@id/homeFragment"
|
app:destination="@id/homeFragment"
|
||||||
app:popUpTo="@id/homeFragment"
|
app:popUpTo="@id/homeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_loginFragment_to_homeFragmentTv"
|
|
||||||
app:destination="@id/homeFragmentTv"
|
|
||||||
app:popUpTo="@id/homeFragmentTv"
|
|
||||||
app:popUpToInclusive="true" />
|
|
||||||
<argument
|
<argument
|
||||||
android:name="reLogin"
|
android:name="reLogin"
|
||||||
app:argType="boolean"
|
app:argType="boolean"
|
||||||
|
@ -386,16 +320,6 @@
|
||||||
app:argType="dev.jdtech.jellyfin.models.PlayerItem[]" />
|
app:argType="dev.jdtech.jellyfin.models.PlayerItem[]" />
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
|
||||||
android:id="@+id/playerActivityTv"
|
|
||||||
android:name="dev.jdtech.jellyfin.tv.TvPlayerActivity"
|
|
||||||
android:label="activity_player_tv"
|
|
||||||
tools:layout="@layout/activity_player_tv">
|
|
||||||
<argument
|
|
||||||
android:name="items"
|
|
||||||
app:argType="dev.jdtech.jellyfin.models.PlayerItem[]" />
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<include app:graph="@navigation/aboutlibs_navigation" />
|
<include app:graph="@navigation/aboutlibs_navigation" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_loginFragment"
|
android:id="@+id/action_global_loginFragment"
|
||||||
|
@ -414,11 +338,6 @@
|
||||||
app:destination="@id/homeFragment"
|
app:destination="@id/homeFragment"
|
||||||
app:popUpTo="@id/homeFragment"
|
app:popUpTo="@id/homeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_usersFragment_to_homeFragmentTv"
|
|
||||||
app:destination="@id/homeFragmentTv"
|
|
||||||
app:popUpTo="@id/homeFragmentTv"
|
|
||||||
app:popUpToInclusive="true" />
|
|
||||||
<argument
|
<argument
|
||||||
android:name="serverId"
|
android:name="serverId"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
|
@ -434,11 +353,6 @@
|
||||||
app:destination="@id/homeFragment"
|
app:destination="@id/homeFragment"
|
||||||
app:popUpTo="@id/homeFragment"
|
app:popUpTo="@id/homeFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_usersFragment_to_homeFragmentTv"
|
|
||||||
app:destination="@id/homeFragmentTv"
|
|
||||||
app:popUpTo="@id/homeFragmentTv"
|
|
||||||
app:popUpToInclusive="true" />
|
|
||||||
<argument
|
<argument
|
||||||
android:name="serverId"
|
android:name="serverId"
|
||||||
app:argType="string" />
|
app:argType="string" />
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:ignore="MissingDefaultResource">
|
|
||||||
|
|
||||||
<dimen name="horizontal_margin">48dp</dimen>
|
|
||||||
<dimen name="vertical_margin">48dp</dimen>
|
|
||||||
|
|
||||||
<dimen name="tv_controls_padding">8dp</dimen>
|
|
||||||
<dimen name="tv_controls_margin_start">32dp</dimen>
|
|
||||||
|
|
||||||
<dimen name="track_selection_item_height">48dp</dimen>
|
|
||||||
<dimen name="track_selection_item_text_size">16sp</dimen>
|
|
||||||
|
|
||||||
<item name="library_columns" type="integer">6</item>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
|
|
||||||
<style name="roundedCardView">
|
|
||||||
<item name="cornerFamily">rounded</item>
|
|
||||||
<item name="cornerSize">40dp</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<resources>
|
|
||||||
<!-- Base application theme. -->
|
|
||||||
<style name="Theme.Findroid" parent="Theme.AppCompat.Leanback">
|
|
||||||
<item name="colorPrimary">@color/blue_600</item>
|
|
||||||
<item name="colorPrimaryVariant">@color/blue_800</item>
|
|
||||||
<item name="colorSecondary">@color/green_500</item>
|
|
||||||
<item name="colorSecondaryVariant">@color/green_800</item>
|
|
||||||
<!-- Status bar color. -->
|
|
||||||
<item name="android:statusBarColor">@color/neutral_900</item>
|
|
||||||
<!-- Customize your theme here. -->
|
|
||||||
<item name="android:windowBackground">@color/neutral_900</item>
|
|
||||||
<item name="android:colorBackgroundFloating">@color/neutral_900</item>
|
|
||||||
|
|
||||||
<item name="defaultBrandColor">@color/neutral_900</item>
|
|
||||||
<item name="browseTitleViewLayout">@layout/browse_support_fragment_title_view</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
Loading…
Reference in a new issue