commit
27265098ba
48 changed files with 605 additions and 397 deletions
|
@ -5,7 +5,7 @@ plugins {
|
||||||
id 'kotlin-kapt'
|
id 'kotlin-kapt'
|
||||||
id 'androidx.navigation.safeargs.kotlin'
|
id 'androidx.navigation.safeargs.kotlin'
|
||||||
id 'dagger.hilt.android.plugin'
|
id 'dagger.hilt.android.plugin'
|
||||||
id 'com.google.android.gms.oss-licenses-plugin'
|
id "com.mikepenz.aboutlibraries.plugin"
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -16,8 +16,8 @@ android {
|
||||||
applicationId "dev.jdtech.jellyfin"
|
applicationId "dev.jdtech.jellyfin"
|
||||||
minSdkVersion 24
|
minSdkVersion 24
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 1
|
versionCode 2
|
||||||
versionName "0.1.0"
|
versionName "0.1.1"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ android {
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
dataBinding true
|
dataBinding true
|
||||||
|
viewBinding true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,11 +97,12 @@ dependencies {
|
||||||
implementation files('libs/extension-ffmpeg-release.aar')
|
implementation files('libs/extension-ffmpeg-release.aar')
|
||||||
|
|
||||||
// Timber
|
// Timber
|
||||||
def timber_version = "5.0.0"
|
def timber_version = "5.0.1"
|
||||||
implementation "com.jakewharton.timber:timber:$timber_version"
|
implementation "com.jakewharton.timber:timber:$timber_version"
|
||||||
|
|
||||||
def oss_licenses_version = "17.0.0"
|
def about_libraries_version = "8.9.1"
|
||||||
implementation "com.google.android.gms:play-services-oss-licenses:$oss_licenses_version"
|
implementation "com.mikepenz:aboutlibraries-core:$about_libraries_version"
|
||||||
|
implementation "com.mikepenz:aboutlibraries:$about_libraries_version"
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
|
|
|
@ -27,11 +27,6 @@
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
|
|
||||||
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"/>
|
|
||||||
|
|
||||||
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
|
|
||||||
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"/>
|
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
@ -51,9 +51,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
binding.navView.visibility = when (destination.id) {
|
binding.navView.visibility = when (destination.id) {
|
||||||
R.id.settingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment -> View.GONE
|
R.id.settingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment, R.id.about_libraries_dest -> View.GONE
|
||||||
else -> View.VISIBLE
|
else -> View.VISIBLE
|
||||||
}
|
}
|
||||||
|
if (destination.id == R.id.about_libraries_dest) binding.mainToolbar.title = getString(R.string.app_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.navigateToAddServer.observe(this, {
|
viewModel.navigateToAddServer.observe(this, {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package dev.jdtech.jellyfin.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import dev.jdtech.jellyfin.R
|
||||||
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
|
class ErrorDialogFragment(private val errorMessage: String) : DialogFragment() {
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
return activity?.let {
|
||||||
|
val builder = MaterialAlertDialogBuilder(it, R.style.ErrorDialogStyle)
|
||||||
|
builder
|
||||||
|
.setMessage(errorMessage)
|
||||||
|
.setPositiveButton("close") { _, _ ->
|
||||||
|
}
|
||||||
|
.setNeutralButton("share") { _, _ ->
|
||||||
|
val sendIntent: Intent = Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
putExtra(Intent.EXTRA_TEXT, errorMessage)
|
||||||
|
type = "text/plain"
|
||||||
|
}
|
||||||
|
|
||||||
|
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||||
|
startActivity(shareIntent)
|
||||||
|
|
||||||
|
}
|
||||||
|
builder.create()
|
||||||
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.databinding.EpisodeBottomSheetBinding
|
import dev.jdtech.jellyfin.databinding.EpisodeBottomSheetBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
|
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
|
||||||
|
|
||||||
|
@ -98,17 +99,20 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.playerItemsError.observe(viewLifecycleOwner, {
|
viewModel.playerItemsError.observe(viewLifecycleOwner, { errorMessage ->
|
||||||
when (it) {
|
if (errorMessage != null) {
|
||||||
true -> {
|
|
||||||
binding.playerItemsError.visibility = View.VISIBLE
|
binding.playerItemsError.visibility = View.VISIBLE
|
||||||
binding.playButton.setImageDrawable(ContextCompat.getDrawable(requireActivity(), R.drawable.ic_play))
|
binding.playButton.setImageDrawable(ContextCompat.getDrawable(requireActivity(), R.drawable.ic_play))
|
||||||
binding.progressCircular.visibility = View.INVISIBLE
|
binding.progressCircular.visibility = View.INVISIBLE
|
||||||
}
|
} else {
|
||||||
false -> binding.playerItemsError.visibility = View.GONE
|
binding.playerItemsError.visibility = View.GONE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.playerItemsErrorDetails.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.playerItemsError.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.loadEpisode(args.episodeId)
|
viewModel.loadEpisode(args.episodeId)
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
|
|
@ -7,13 +7,13 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
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.FavoritesListAdapter
|
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
|
import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel
|
import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
|
@ -29,16 +29,6 @@ class FavoriteFragment : Fragment() {
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentFavoriteBinding.inflate(inflater, container, false)
|
binding = FragmentFavoriteBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
val snackbar =
|
|
||||||
Snackbar.make(
|
|
||||||
binding.mainLayout,
|
|
||||||
getString(R.string.error_loading_data),
|
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
|
||||||
viewModel.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
|
binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
|
||||||
|
@ -53,11 +43,23 @@ class FavoriteFragment : Fragment() {
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
if (error) {
|
if (error != null) {
|
||||||
snackbar.show()
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
|
binding.favoritesRecyclerView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
|
binding.favoritesRecyclerView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
|
viewModel.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.favoriteSections.observe(viewLifecycleOwner, { sections ->
|
viewModel.favoriteSections.observe(viewLifecycleOwner, { sections ->
|
||||||
if (sections.isEmpty()) {
|
if (sections.isEmpty()) {
|
||||||
binding.noFavoritesText.visibility = View.VISIBLE
|
binding.noFavoritesText.visibility = View.VISIBLE
|
||||||
|
|
|
@ -5,13 +5,13 @@ import android.view.*
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
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.HomeEpisodeListAdapter
|
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ViewListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentHomeBinding
|
import dev.jdtech.jellyfin.databinding.FragmentHomeBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.viewmodels.HomeViewModel
|
import dev.jdtech.jellyfin.viewmodels.HomeViewModel
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
|
@ -49,12 +49,6 @@ class HomeFragment : Fragment() {
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentHomeBinding.inflate(inflater, container, false)
|
binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
val snackbar =
|
|
||||||
Snackbar.make(binding.mainLayout, getString(R.string.error_loading_data), Snackbar.LENGTH_INDEFINITE)
|
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
|
||||||
viewModel.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
binding.viewsRecyclerView.adapter = ViewListAdapter(ViewListAdapter.OnClickListener {
|
binding.viewsRecyclerView.adapter = ViewListAdapter(ViewListAdapter.OnClickListener {
|
||||||
|
@ -78,11 +72,23 @@ class HomeFragment : Fragment() {
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
if (error) {
|
if (error != null) {
|
||||||
snackbar.show()
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
|
binding.viewsRecyclerView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
|
binding.viewsRecyclerView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
|
viewModel.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ import android.view.ViewGroup
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
|
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
|
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
|
@ -39,21 +39,23 @@ class LibraryFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
val snackbar =
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
Snackbar.make(
|
if (error != null) {
|
||||||
binding.mainLayout,
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
getString(R.string.error_loading_data),
|
binding.itemsRecyclerView.visibility = View.GONE
|
||||||
Snackbar.LENGTH_INDEFINITE
|
} else {
|
||||||
)
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
binding.itemsRecyclerView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
viewModel.loadItems(args.libraryId)
|
viewModel.loadItems(args.libraryId)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
if (error) {
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
snackbar.show()
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
||||||
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
||||||
|
|
|
@ -6,11 +6,11 @@ import androidx.appcompat.widget.SearchView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
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.CollectionListAdapter
|
import dev.jdtech.jellyfin.adapters.CollectionListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentMediaBinding
|
import dev.jdtech.jellyfin.databinding.FragmentMediaBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.viewmodels.MediaViewModel
|
import dev.jdtech.jellyfin.viewmodels.MediaViewModel
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
|
@ -53,16 +53,6 @@ class MediaFragment : Fragment() {
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentMediaBinding.inflate(inflater, container, false)
|
binding = FragmentMediaBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
val snackbar =
|
|
||||||
Snackbar.make(
|
|
||||||
binding.mainLayout,
|
|
||||||
getString(R.string.error_loading_data),
|
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
|
||||||
viewModel.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
binding.viewsRecyclerView.adapter =
|
binding.viewsRecyclerView.adapter =
|
||||||
|
@ -75,11 +65,23 @@ class MediaFragment : Fragment() {
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
if (error) {
|
if (error != null) {
|
||||||
snackbar.show()
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
|
binding.viewsRecyclerView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
|
binding.viewsRecyclerView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
|
viewModel.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,12 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
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.PersonListAdapter
|
import dev.jdtech.jellyfin.adapters.PersonListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentMediaInfoBinding
|
import dev.jdtech.jellyfin.databinding.FragmentMediaInfoBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.dialogs.VideoVersionDialogFragment
|
import dev.jdtech.jellyfin.dialogs.VideoVersionDialogFragment
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
|
import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
|
||||||
|
@ -44,24 +44,26 @@ class MediaInfoFragment : Fragment() {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val snackbar =
|
|
||||||
Snackbar.make(
|
|
||||||
binding.mainLayout,
|
|
||||||
getString(R.string.error_loading_data),
|
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
|
||||||
viewModel.loadData(args.itemId, args.itemType)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
if (error) {
|
if (error != null) {
|
||||||
snackbar.show()
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
|
binding.mediaInfoScrollview.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
|
binding.mediaInfoScrollview.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
|
viewModel.loadData(args.itemId, args.itemType)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.item.observe(viewLifecycleOwner, { item ->
|
viewModel.item.observe(viewLifecycleOwner, { item ->
|
||||||
if (item.originalTitle != item.name) {
|
if (item.originalTitle != item.name) {
|
||||||
binding.originalTitle.visibility = View.VISIBLE
|
binding.originalTitle.visibility = View.VISIBLE
|
||||||
|
@ -91,7 +93,12 @@ class MediaInfoFragment : Fragment() {
|
||||||
viewModel.item.value!!.userData!!.playbackPositionTicks.div(10000)
|
viewModel.item.value!!.userData!!.playbackPositionTicks.div(10000)
|
||||||
)
|
)
|
||||||
viewModel.doneNavigatingToPlayer()
|
viewModel.doneNavigatingToPlayer()
|
||||||
binding.playButton.setImageDrawable(ContextCompat.getDrawable(requireActivity(), R.drawable.ic_play))
|
binding.playButton.setImageDrawable(
|
||||||
|
ContextCompat.getDrawable(
|
||||||
|
requireActivity(),
|
||||||
|
R.drawable.ic_play
|
||||||
|
)
|
||||||
|
)
|
||||||
binding.progressCircular.visibility = View.INVISIBLE
|
binding.progressCircular.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -114,17 +121,25 @@ class MediaInfoFragment : Fragment() {
|
||||||
binding.favoriteButton.setImageResource(drawable)
|
binding.favoriteButton.setImageResource(drawable)
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.playerItemsError.observe(viewLifecycleOwner, {
|
viewModel.playerItemsError.observe(viewLifecycleOwner, { errorMessage ->
|
||||||
when (it) {
|
if (errorMessage != null) {
|
||||||
true -> {
|
|
||||||
binding.playerItemsError.visibility = View.VISIBLE
|
binding.playerItemsError.visibility = View.VISIBLE
|
||||||
binding.playButton.setImageDrawable(ContextCompat.getDrawable(requireActivity(), R.drawable.ic_play))
|
binding.playButton.setImageDrawable(
|
||||||
|
ContextCompat.getDrawable(
|
||||||
|
requireActivity(),
|
||||||
|
R.drawable.ic_play
|
||||||
|
)
|
||||||
|
)
|
||||||
binding.progressCircular.visibility = View.INVISIBLE
|
binding.progressCircular.visibility = View.INVISIBLE
|
||||||
}
|
} else {
|
||||||
false -> binding.playerItemsError.visibility = View.GONE
|
binding.playerItemsError.visibility = View.GONE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.playerItemsErrorDetails.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.playerItemsError.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
binding.trailerButton.setOnClickListener {
|
binding.trailerButton.setOnClickListener {
|
||||||
val intent = Intent(
|
val intent = Intent(
|
||||||
Intent.ACTION_VIEW,
|
Intent.ACTION_VIEW,
|
||||||
|
@ -155,10 +170,20 @@ class MediaInfoFragment : Fragment() {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
navigateToPlayerActivity(
|
navigateToPlayerActivity(
|
||||||
arrayOf(PlayerItem(args.itemId, viewModel.mediaSources.value!![0].id!!)),
|
arrayOf(
|
||||||
|
PlayerItem(
|
||||||
|
args.itemId,
|
||||||
|
viewModel.mediaSources.value!![0].id!!
|
||||||
|
)
|
||||||
|
),
|
||||||
viewModel.item.value!!.userData!!.playbackPositionTicks.div(10000),
|
viewModel.item.value!!.userData!!.playbackPositionTicks.div(10000),
|
||||||
)
|
)
|
||||||
binding.playButton.setImageDrawable(ContextCompat.getDrawable(requireActivity(), R.drawable.ic_play))
|
binding.playButton.setImageDrawable(
|
||||||
|
ContextCompat.getDrawable(
|
||||||
|
requireActivity(),
|
||||||
|
R.drawable.ic_play
|
||||||
|
)
|
||||||
|
)
|
||||||
binding.progressCircular.visibility = View.INVISIBLE
|
binding.progressCircular.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,13 @@ import android.view.ViewGroup
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
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.FavoritesListAdapter
|
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentSearchResultBinding
|
import dev.jdtech.jellyfin.databinding.FragmentSearchResultBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.viewmodels.SearchResultViewModel
|
import dev.jdtech.jellyfin.viewmodels.SearchResultViewModel
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
|
@ -32,16 +32,6 @@ class SearchResultFragment : Fragment() {
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentSearchResultBinding.inflate(inflater, container, false)
|
binding = FragmentSearchResultBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
val snackbar =
|
|
||||||
Snackbar.make(
|
|
||||||
binding.mainLayout,
|
|
||||||
getString(R.string.error_loading_data),
|
|
||||||
Snackbar.LENGTH_INDEFINITE
|
|
||||||
)
|
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
|
||||||
viewModel.loadData(args.query)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.lifecycleOwner = viewLifecycleOwner
|
binding.lifecycleOwner = viewLifecycleOwner
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
binding.searchResultsRecyclerView.adapter = FavoritesListAdapter(
|
binding.searchResultsRecyclerView.adapter = FavoritesListAdapter(
|
||||||
|
@ -56,11 +46,23 @@ class SearchResultFragment : Fragment() {
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
if (error) {
|
if (error != null) {
|
||||||
snackbar.show()
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
|
binding.searchResultsRecyclerView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
|
binding.searchResultsRecyclerView.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
|
viewModel.loadData(args.query)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.sections.observe(viewLifecycleOwner, { sections ->
|
viewModel.sections.observe(viewLifecycleOwner, { sections ->
|
||||||
if (sections.isEmpty()) {
|
if (sections.isEmpty()) {
|
||||||
binding.noSearchResultsText.visibility = View.VISIBLE
|
binding.noSearchResultsText.visibility = View.VISIBLE
|
||||||
|
|
|
@ -8,11 +8,11 @@ import android.view.ViewGroup
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.google.android.material.snackbar.Snackbar
|
|
||||||
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.EpisodeListAdapter
|
import dev.jdtech.jellyfin.adapters.EpisodeListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding
|
import dev.jdtech.jellyfin.databinding.FragmentSeasonBinding
|
||||||
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
|
import dev.jdtech.jellyfin.viewmodels.SeasonViewModel
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
|
@ -37,21 +37,23 @@ class SeasonFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
val snackbar =
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
Snackbar.make(
|
if (error != null) {
|
||||||
binding.mainLayout,
|
binding.errorLayout.errorPanel.visibility = View.VISIBLE
|
||||||
getString(R.string.error_loading_data),
|
binding.episodesRecyclerView.visibility = View.GONE
|
||||||
Snackbar.LENGTH_INDEFINITE
|
} else {
|
||||||
)
|
binding.errorLayout.errorPanel.visibility = View.GONE
|
||||||
snackbar.setAction(getString(R.string.retry)) {
|
binding.episodesRecyclerView.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
viewModel.loadEpisodes(args.seriesId, args.seasonId)
|
viewModel.loadEpisodes(args.seriesId, args.seasonId)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.error.observe(viewLifecycleOwner, { error ->
|
binding.errorLayout.errorDetailsButton.setOnClickListener {
|
||||||
if (error) {
|
ErrorDialogFragment(viewModel.error.value ?: getString(R.string.unknown_error)).show(parentFragmentManager, "errordialog")
|
||||||
snackbar.show()
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
viewModel.finishedLoading.observe(viewLifecycleOwner, {
|
||||||
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
binding.loadingIndicator.visibility = if (it) View.GONE else View.VISIBLE
|
||||||
|
|
|
@ -8,7 +8,6 @@ import android.view.ViewGroup
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.jdtech.jellyfin.R
|
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
|
import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding
|
||||||
import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment
|
import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment
|
||||||
import dev.jdtech.jellyfin.adapters.ServerGridAdapter
|
import dev.jdtech.jellyfin.adapters.ServerGridAdapter
|
||||||
|
|
|
@ -8,7 +8,6 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
|
|
||||||
class SettingsFragment : PreferenceFragmentCompat() {
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
@ -38,8 +37,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
findPreference<Preference>("ossLicenses")?.setOnPreferenceClickListener {
|
findPreference<Preference>("appInfo")?.setOnPreferenceClickListener {
|
||||||
startActivity(Intent(requireContext(), OssLicensesMenuActivity::class.java))
|
findNavController().navigate(SettingsFragmentDirections.actionSettingsFragmentToAboutLibraries())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,10 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
|
||||||
val items: List<BaseItemDto>
|
val items: List<BaseItemDto>
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
items =
|
items =
|
||||||
jellyfinApi.itemsApi.getResumeItems(jellyfinApi.userId!!).content.items ?: listOf()
|
jellyfinApi.itemsApi.getResumeItems(
|
||||||
|
jellyfinApi.userId!!,
|
||||||
|
includeItemTypes = listOf("Movie", "Episode"),
|
||||||
|
).content.items ?: listOf()
|
||||||
}
|
}
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
package dev.jdtech.jellyfin.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import androidx.preference.Preference
|
|
||||||
|
|
||||||
class VersionPreference(context: Context, attrs: AttributeSet): Preference(context, attrs) {
|
|
||||||
init {
|
|
||||||
val packageManager = context.packageManager
|
|
||||||
val packageInfo = packageManager.getPackageInfo(context.packageName, 0)
|
|
||||||
val versionName = packageInfo.versionName
|
|
||||||
summary = versionName
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,8 +9,11 @@ import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
import dev.jdtech.jellyfin.database.Server
|
||||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.jellyfin.sdk.discovery.RecommendedServerInfo
|
||||||
|
import org.jellyfin.sdk.discovery.RecommendedServerInfoScore
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -34,19 +37,32 @@ constructor(
|
||||||
* - Connect to server and check if it is a Jellyfin server
|
* - Connect to server and check if it is a Jellyfin server
|
||||||
* - Check if server is not already in Database
|
* - Check if server is not already in Database
|
||||||
*/
|
*/
|
||||||
fun checkServer(baseUrl: String) {
|
fun checkServer(inputValue: String) {
|
||||||
_error.value = null
|
_error.value = null
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val candidates = jellyfinApi.jellyfin.discovery.getAddressCandidates(inputValue)
|
||||||
|
val recommended = jellyfinApi.jellyfin.discovery.getRecommendedServers(
|
||||||
|
candidates,
|
||||||
|
RecommendedServerInfoScore.GOOD
|
||||||
|
)
|
||||||
|
val recommendedServer: RecommendedServerInfo
|
||||||
|
|
||||||
|
try {
|
||||||
|
recommendedServer = recommended.first()
|
||||||
|
} catch (e: NoSuchElementException) {
|
||||||
|
throw Exception("Server not found")
|
||||||
|
}
|
||||||
|
|
||||||
jellyfinApi.apply {
|
jellyfinApi.apply {
|
||||||
api.baseUrl = baseUrl
|
api.baseUrl = recommendedServer.address
|
||||||
api.accessToken = null
|
api.accessToken = null
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
val publicSystemInfo by jellyfinApi.systemApi.getPublicSystemInfo()
|
|
||||||
Timber.d("Remote server: ${publicSystemInfo.id}")
|
|
||||||
|
|
||||||
if (serverAlreadyInDatabase(publicSystemInfo.id)) {
|
Timber.d("Remote server: ${recommendedServer.systemInfo?.id}")
|
||||||
|
|
||||||
|
if (serverAlreadyInDatabase(recommendedServer.systemInfo?.id)) {
|
||||||
_error.value = "Server already added"
|
_error.value = "Server already added"
|
||||||
_navigateToLogin.value = false
|
_navigateToLogin.value = false
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -43,8 +43,8 @@ constructor(
|
||||||
|
|
||||||
var playerItems: MutableList<PlayerItem> = mutableListOf()
|
var playerItems: MutableList<PlayerItem> = mutableListOf()
|
||||||
|
|
||||||
private val _playerItemsError = MutableLiveData<Boolean>()
|
private val _playerItemsError = MutableLiveData<String>()
|
||||||
val playerItemsError: LiveData<Boolean> = _playerItemsError
|
val playerItemsError: LiveData<String> = _playerItemsError
|
||||||
|
|
||||||
fun loadEpisode(episodeId: UUID) {
|
fun loadEpisode(episodeId: UUID) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
@ -62,13 +62,13 @@ constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun preparePlayer() {
|
fun preparePlayer() {
|
||||||
_playerItemsError.value = false
|
_playerItemsError.value = null
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
createPlayerItems(_item.value!!)
|
createPlayerItems(_item.value!!)
|
||||||
_navigateToPlayer.value = true
|
_navigateToPlayer.value = true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_playerItemsError.value = true
|
_playerItemsError.value = e.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,15 @@ constructor(
|
||||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadData() {
|
fun loadData() {
|
||||||
_error.value = false
|
_error.value = null
|
||||||
_finishedLoading.value = false
|
_finishedLoading.value = false
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
|
@ -78,7 +78,7 @@ constructor(
|
||||||
_favoriteSections.value = tempFavoriteSections
|
_favoriteSections.value = tempFavoriteSections
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
_finishedLoading.value = true
|
_finishedLoading.value = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,15 +38,15 @@ constructor(
|
||||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadData()
|
loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadData() {
|
fun loadData() {
|
||||||
_error.value = false
|
_error.value = null
|
||||||
_finishedLoading.value = false
|
_finishedLoading.value = false
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
|
@ -57,7 +57,8 @@ constructor(
|
||||||
Timber.d("Collection type: ${view.collectionType}")
|
Timber.d("Collection type: ${view.collectionType}")
|
||||||
if (view.collectionType == "homevideos" ||
|
if (view.collectionType == "homevideos" ||
|
||||||
view.collectionType == "music" ||
|
view.collectionType == "music" ||
|
||||||
view.collectionType == "playlists"
|
view.collectionType == "playlists" ||
|
||||||
|
view.collectionType == "books"
|
||||||
) continue
|
) continue
|
||||||
val latestItems = jellyfinRepository.getLatestMedia(view.id)
|
val latestItems = jellyfinRepository.getLatestMedia(view.id)
|
||||||
if (latestItems.isEmpty()) continue
|
if (latestItems.isEmpty()) continue
|
||||||
|
@ -87,7 +88,7 @@ constructor(
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
_finishedLoading.value = true
|
_finishedLoading.value = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,18 +20,18 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
||||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
fun loadItems(parentId: UUID) {
|
fun loadItems(parentId: UUID) {
|
||||||
_error.value = false
|
_error.value = null
|
||||||
_finishedLoading.value = false
|
_finishedLoading.value = false
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
_items.value = jellyfinRepository.getItems(parentId)
|
_items.value = jellyfinRepository.getItems(parentId)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
_finishedLoading.value = true
|
_finishedLoading.value = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
import dev.jdtech.jellyfin.database.Server
|
||||||
|
|
|
@ -64,16 +64,16 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
||||||
private val _favorite = MutableLiveData<Boolean>()
|
private val _favorite = MutableLiveData<Boolean>()
|
||||||
val favorite: LiveData<Boolean> = _favorite
|
val favorite: LiveData<Boolean> = _favorite
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
var playerItems: MutableList<PlayerItem> = mutableListOf()
|
var playerItems: MutableList<PlayerItem> = mutableListOf()
|
||||||
|
|
||||||
private val _playerItemsError = MutableLiveData<Boolean>()
|
private val _playerItemsError = MutableLiveData<String>()
|
||||||
val playerItemsError: LiveData<Boolean> = _playerItemsError
|
val playerItemsError: LiveData<String> = _playerItemsError
|
||||||
|
|
||||||
fun loadData(itemId: UUID, itemType: String) {
|
fun loadData(itemId: UUID, itemType: String) {
|
||||||
_error.value = false
|
_error.value = null
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
_item.value = jellyfinRepository.getItem(itemId)
|
_item.value = jellyfinRepository.getItem(itemId)
|
||||||
|
@ -96,7 +96,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,13 +184,13 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun preparePlayer() {
|
fun preparePlayer() {
|
||||||
_playerItemsError.value = false
|
_playerItemsError.value = null
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
createPlayerItems(_item.value!!)
|
createPlayerItems(_item.value!!)
|
||||||
_navigateToPlayer.value = playerItems.toTypedArray()
|
_navigateToPlayer.value = playerItems.toTypedArray()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_playerItemsError.value = true
|
_playerItemsError.value = e.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@ constructor(
|
||||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadData()
|
loadData()
|
||||||
|
@ -30,7 +30,7 @@ constructor(
|
||||||
|
|
||||||
fun loadData() {
|
fun loadData() {
|
||||||
_finishedLoading.value = false
|
_finishedLoading.value = false
|
||||||
_error.value = false
|
_error.value = null
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val items = jellyfinRepository.getItems()
|
val items = jellyfinRepository.getItems()
|
||||||
|
@ -39,11 +39,12 @@ constructor(
|
||||||
it.collectionType != "homevideos" &&
|
it.collectionType != "homevideos" &&
|
||||||
it.collectionType != "music" &&
|
it.collectionType != "music" &&
|
||||||
it.collectionType != "playlists" &&
|
it.collectionType != "playlists" &&
|
||||||
it.collectionType != "boxsets"
|
it.collectionType != "boxsets" &&
|
||||||
|
it.collectionType != "books"
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
_finishedLoading.value = true
|
_finishedLoading.value = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,11 @@ constructor(
|
||||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
fun loadData(query: String) {
|
fun loadData(query: String) {
|
||||||
_error.value = false
|
_error.value = null
|
||||||
_finishedLoading.value = false
|
_finishedLoading.value = false
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
|
@ -74,7 +74,7 @@ constructor(
|
||||||
_sections.value = tempSections
|
_sections.value = tempSections
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
_finishedLoading.value = true
|
_finishedLoading.value = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,18 +24,18 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
||||||
private val _finishedLoading = MutableLiveData<Boolean>()
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
private val _error = MutableLiveData<Boolean>()
|
private val _error = MutableLiveData<String>()
|
||||||
val error: LiveData<Boolean> = _error
|
val error: LiveData<String> = _error
|
||||||
|
|
||||||
fun loadEpisodes(seriesId: UUID, seasonId: UUID) {
|
fun loadEpisodes(seriesId: UUID, seasonId: UUID) {
|
||||||
_error.value = false
|
_error.value = null
|
||||||
_finishedLoading.value = false
|
_finishedLoading.value = false
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
_episodes.value = getEpisodes(seriesId, seasonId)
|
_episodes.value = getEpisodes(seriesId, seasonId)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
_error.value = true
|
_error.value = e.message
|
||||||
}
|
}
|
||||||
_finishedLoading.value = true
|
_finishedLoading.value = true
|
||||||
}
|
}
|
||||||
|
|
28
app/src/main/res/drawable/ic_alert_circle.xml
Normal file
28
app/src/main/res/drawable/ic_alert_circle.xml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorOnBackground">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="@android:color/white"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M12,8L12,12"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="@android:color/white"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M12,16L12.01,16"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="@android:color/white"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -13,4 +13,4 @@
|
||||||
android:background="@color/black"
|
android:background="@color/black"
|
||||||
app:show_subtitle_button="true" />
|
app:show_subtitle_button="true" />
|
||||||
|
|
||||||
</FrameLayout>
|
</merge>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="12dp"
|
android:layout_marginHorizontal="12dp"
|
||||||
android:layout_marginBottom="32dp"
|
android:layout_marginBottom="24dp"
|
||||||
android:foreground="?android:attr/selectableItemBackground"
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:text="@{collection.name}"
|
android:text="@{collection.name}"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
android:textSize="18sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/collection_image"
|
app:layout_constraintTop_toBottomOf="@id/collection_image"
|
||||||
|
|
|
@ -65,13 +65,14 @@
|
||||||
tools:text="1. To You, in 2000 Years: The Fall of Shiganshina, Part 1" />
|
tools:text="1. To You, in 2000 Years: The Fall of Shiganshina, Part 1" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/episode_metadata"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginEnd="24dp"
|
android:layout_marginEnd="24dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="@id/episode_name"
|
app:layout_constraintStart_toStartOf="@id/episode_image"
|
||||||
app:layout_constraintTop_toBottomOf="@id/episode_name">
|
app:layout_constraintTop_toBottomOf="@id/episode_image">
|
||||||
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -115,7 +116,7 @@
|
||||||
android:layout_marginBottom="24dp"
|
android:layout_marginBottom="24dp"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/episode_image">
|
app:layout_constraintTop_toBottomOf="@id/episode_metadata">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -136,10 +137,10 @@
|
||||||
android:id="@+id/progress_circular"
|
android:id="@+id/progress_circular"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:elevation="8dp"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
|
android:elevation="8dp"
|
||||||
android:indeterminateTint="@color/white"
|
android:indeterminateTint="@color/white"
|
||||||
|
android:padding="8dp"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -163,21 +164,37 @@
|
||||||
android:src="@drawable/ic_heart" />
|
android:src="@drawable/ic_heart" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/player_items_error"
|
android:id="@+id/player_items_error"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="24dp"
|
android:layout_marginHorizontal="24dp"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/buttons"
|
app:layout_constraintTop_toBottomOf="@id/buttons"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/player_items_error_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
android:text="@string/error_preparing_player_items"
|
android:text="@string/error_preparing_player_items"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
android:textColor="@color/red"
|
android:textColor="@color/red" />
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible" />
|
<TextView
|
||||||
|
android:id="@+id/player_items_error_details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/view_details_underlined"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:textColor="@color/red" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|
|
@ -82,5 +82,13 @@
|
||||||
app:layout_constraintStart_toStartOf="@id/episode_title"
|
app:layout_constraintStart_toStartOf="@id/episode_title"
|
||||||
app:layout_constraintTop_toBottomOf="@id/episode_title"
|
app:layout_constraintTop_toBottomOf="@id/episode_title"
|
||||||
tools:text="After one hundred years of peace, humanity is suddenly reminded of the terror of being at the Titans' mercy. After one hundred years of peace, humanity is suddenly reminded of the terror of being at the Titans' mercy." />
|
tools:text="After one hundred years of peace, humanity is suddenly reminded of the terror of being at the Titans' mercy. After one hundred years of peace, humanity is suddenly reminded of the terror of being at the Titans' mercy." />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:background="@drawable/header_gradient"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/episode_desc"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/episode_desc"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/episode_desc" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</layout>
|
</layout>
|
53
app/src/main/res/layout/error_panel.xml
Normal file
53
app/src/main/res/layout/error_panel.xml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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/error_panel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/ic_alert_circle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:text="@string/error_loading_data"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/error_details_button"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:text="@string/view_details" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/error_retry_button"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/retry" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -9,12 +9,6 @@
|
||||||
type="dev.jdtech.jellyfin.viewmodels.FavoriteViewModel" />
|
type="dev.jdtech.jellyfin.viewmodels.FavoriteViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".fragments.FavoriteFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
@ -30,6 +24,10 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:trackCornerRadius="10dp" />
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/error_layout"
|
||||||
|
layout="@layout/error_panel" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/no_favorites_text"
|
android:id="@+id/no_favorites_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -58,6 +56,4 @@
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
|
@ -10,11 +10,6 @@
|
||||||
type="dev.jdtech.jellyfin.viewmodels.HomeViewModel" />
|
type="dev.jdtech.jellyfin.viewmodels.HomeViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -32,6 +27,8 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:trackCornerRadius="10dp" />
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/views_recycler_view"
|
android:id="@+id/views_recycler_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -47,6 +44,5 @@
|
||||||
tools:itemCount="4"
|
tools:itemCount="4"
|
||||||
tools:listitem="@layout/view_item" />
|
tools:listitem="@layout/view_item" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -10,11 +10,6 @@
|
||||||
type="dev.jdtech.jellyfin.viewmodels.LibraryViewModel" />
|
type="dev.jdtech.jellyfin.viewmodels.LibraryViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -31,6 +26,8 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:trackCornerRadius="10dp" />
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/items_recycler_view"
|
android:id="@+id/items_recycler_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -50,7 +47,6 @@
|
||||||
tools:listitem="@layout/base_item" />
|
tools:listitem="@layout/base_item" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
|
|
||||||
|
|
|
@ -10,11 +10,6 @@
|
||||||
type="dev.jdtech.jellyfin.viewmodels.MediaViewModel" />
|
type="dev.jdtech.jellyfin.viewmodels.MediaViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -32,6 +27,8 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:trackCornerRadius="10dp" />
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/views_recycler_view"
|
android:id="@+id/views_recycler_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -51,5 +48,4 @@
|
||||||
tools:listitem="@layout/collection_item" />
|
tools:listitem="@layout/collection_item" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -12,15 +12,22 @@
|
||||||
type="dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel" />
|
type="dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/error_layout"
|
||||||
|
layout="@layout/error_panel" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/media_info_scrollview"
|
android:id="@+id/media_info_scrollview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -185,18 +192,35 @@
|
||||||
android:src="@drawable/ic_heart" />
|
android:src="@drawable/ic_heart" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/player_items_error"
|
android:id="@+id/player_items_error"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="24dp"
|
android:layout_marginHorizontal="24dp"
|
||||||
android:layout_marginTop="-12dp"
|
android:layout_marginTop="-12dp"
|
||||||
android:layout_marginBottom="12dp"
|
android:layout_marginBottom="12dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/player_items_error_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
android:text="@string/error_preparing_player_items"
|
android:text="@string/error_preparing_player_items"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
android:textColor="@color/red"
|
android:textColor="@color/red" />
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible" />
|
<TextView
|
||||||
|
android:id="@+id/player_items_error_details"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/view_details_underlined"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:textColor="@color/red" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/info"
|
android:id="@+id/info"
|
||||||
|
@ -415,7 +439,6 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
@ -4,17 +4,12 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
<variable
|
<variable
|
||||||
name="viewModel"
|
name="viewModel"
|
||||||
type="dev.jdtech.jellyfin.viewmodels.SearchResultViewModel" />
|
type="dev.jdtech.jellyfin.viewmodels.SearchResultViewModel" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".fragments.FavoriteFragment">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
@ -30,16 +25,18 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:trackCornerRadius="10dp" />
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<include android:id="@+id/error_layout" layout="@layout/error_panel" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/no_search_results_text"
|
android:id="@+id/no_search_results_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/no_search_results"
|
android:text="@string/no_search_results"
|
||||||
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/search_results_recycler_view"
|
android:id="@+id/search_results_recycler_view"
|
||||||
|
@ -47,17 +44,15 @@
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingTop="16dp"
|
android:paddingTop="16dp"
|
||||||
|
app:favoriteSections="@{viewModel.sections}"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:favoriteSections="@{viewModel.sections}"
|
|
||||||
tools:itemCount="4"
|
tools:itemCount="4"
|
||||||
tools:listitem="@layout/favorite_section" />
|
tools:listitem="@layout/favorite_section" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
|
@ -12,12 +12,6 @@
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/main_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
@ -33,6 +27,10 @@
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:trackCornerRadius="10dp" />
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/error_layout"
|
||||||
|
layout="@layout/error_panel" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/episodes_recycler_view"
|
android:id="@+id/episodes_recycler_view"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -47,6 +45,4 @@
|
||||||
tools:itemCount="4"
|
tools:itemCount="4"
|
||||||
tools:listitem="@layout/episode_item" />
|
tools:listitem="@layout/episode_item" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
</layout>
|
</layout>
|
|
@ -56,10 +56,13 @@
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/settingsFragment"
|
android:id="@+id/settingsFragment"
|
||||||
android:name="dev.jdtech.jellyfin.fragments.SettingsFragment"
|
android:name="dev.jdtech.jellyfin.fragments.SettingsFragment"
|
||||||
android:label="@string/title_settings" >
|
android:label="@string/title_settings">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_navigation_settings_to_serverSelectFragment2"
|
android:id="@+id/action_navigation_settings_to_serverSelectFragment2"
|
||||||
app:destination="@id/serverSelectFragment" />
|
app:destination="@id/serverSelectFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_settingsFragment_to_about_libraries"
|
||||||
|
app:destination="@id/about_libraries" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/libraryFragment"
|
android:id="@+id/libraryFragment"
|
||||||
|
@ -173,7 +176,7 @@
|
||||||
android:id="@+id/searchResultFragment"
|
android:id="@+id/searchResultFragment"
|
||||||
android:name="dev.jdtech.jellyfin.fragments.SearchResultFragment"
|
android:name="dev.jdtech.jellyfin.fragments.SearchResultFragment"
|
||||||
android:label="{query}"
|
android:label="{query}"
|
||||||
tools:layout="@layout/fragment_search_result" >
|
tools:layout="@layout/fragment_search_result">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_favoriteFragment_to_episodeBottomSheetFragment"
|
android:id="@+id/action_favoriteFragment_to_episodeBottomSheetFragment"
|
||||||
app:destination="@id/episodeBottomSheetFragment" />
|
app:destination="@id/episodeBottomSheetFragment" />
|
||||||
|
@ -222,7 +225,7 @@
|
||||||
android:id="@+id/initializingFragment"
|
android:id="@+id/initializingFragment"
|
||||||
android:name="dev.jdtech.jellyfin.fragments.InitializingFragment"
|
android:name="dev.jdtech.jellyfin.fragments.InitializingFragment"
|
||||||
android:label="@string/initializing"
|
android:label="@string/initializing"
|
||||||
tools:layout="@layout/fragment_initializing" >
|
tools:layout="@layout/fragment_initializing">
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_initializingFragment_to_navigation_home"
|
android:id="@+id/action_initializingFragment_to_navigation_home"
|
||||||
app:destination="@id/homeFragment"
|
app:destination="@id/homeFragment"
|
||||||
|
@ -234,4 +237,7 @@
|
||||||
app:popUpTo="@id/initializingFragment"
|
app:popUpTo="@id/initializingFragment"
|
||||||
app:popUpToInclusive="true" />
|
app:popUpToInclusive="true" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
|
<include app:graph="@navigation/aboutlibs_navigation" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
|
@ -2,7 +2,7 @@
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="setup_container_width">400dp</dimen>
|
<dimen name="setup_container_width">400dp</dimen>
|
||||||
<item name="server_columns" type="integer">6</item>
|
<item name="server_columns" type="integer">6</item>
|
||||||
<item name="collection_columns" type="integer">2</item>
|
<item name="collection_columns" type="integer">3</item>
|
||||||
<item name="library_columns" type="integer">4</item>
|
<item name="library_columns" type="integer">4</item>
|
||||||
<item name="recyclerview_animation_duration" type="integer">450</item>
|
<item name="recyclerview_animation_duration" type="integer">450</item>
|
||||||
<dimen name="nextup_media_width">350dp</dimen>
|
<dimen name="nextup_media_width">350dp</dimen>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<item name="server_columns" type="integer">8</item>
|
<item name="server_columns" type="integer">8</item>
|
||||||
<item name="collection_columns" type="integer">3</item>
|
|
||||||
<item name="library_columns" type="integer">6</item>
|
<item name="library_columns" type="integer">6</item>
|
||||||
</resources>
|
</resources>
|
8
app/src/main/res/values/about_libraries.xml
Normal file
8
app/src/main/res/values/about_libraries.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="aboutLibraries_description_showIcon">true</string>
|
||||||
|
<string name="aboutLibraries_description_showVersion">true</string>
|
||||||
|
<string name="aboutLibraries_description_name">@string/app_name</string>
|
||||||
|
<string name="aboutLibraries_description_text">@string/app_description</string>
|
||||||
|
<string name="aboutLibraries_showLicense">true</string>
|
||||||
|
</resources>
|
|
@ -5,7 +5,7 @@
|
||||||
<dimen name="setup_container_width">@dimen/match_parent</dimen>
|
<dimen name="setup_container_width">@dimen/match_parent</dimen>
|
||||||
<dimen name="overview_media_width">150dp</dimen>
|
<dimen name="overview_media_width">150dp</dimen>
|
||||||
<item name="server_columns" type="integer">3</item>
|
<item name="server_columns" type="integer">3</item>
|
||||||
<item name="collection_columns" type="integer">1</item>
|
<item name="collection_columns" type="integer">2</item>
|
||||||
<item name="library_columns" type="integer">2</item>
|
<item name="library_columns" type="integer">2</item>
|
||||||
<item name="recyclerview_animation_duration" type="integer">250</item>
|
<item name="recyclerview_animation_duration" type="integer">250</item>
|
||||||
<dimen name="nextup_media_width">@dimen/match_parent</dimen>
|
<dimen name="nextup_media_width">@dimen/match_parent</dimen>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Findroid</string>
|
<string name="app_name">Findroid</string>
|
||||||
|
<string name="app_description">Third-party native Jellyfin app</string>
|
||||||
<string name="jellyfin_banner">Jellyfin banner</string>
|
<string name="jellyfin_banner">Jellyfin banner</string>
|
||||||
<string name="add_server">Add server</string>
|
<string name="add_server">Add server</string>
|
||||||
<string name="login">Login</string>
|
<string name="login">Login</string>
|
||||||
|
@ -14,7 +15,6 @@
|
||||||
<string name="remove_server_dialog_text">Are you sure you want to remove the server %1$s</string>
|
<string name="remove_server_dialog_text">Are you sure you want to remove the server %1$s</string>
|
||||||
<string name="remove">Remove</string>
|
<string name="remove">Remove</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="title_activity_main">MainActivity</string>
|
|
||||||
<string name="title_home">Home</string>
|
<string name="title_home">Home</string>
|
||||||
<string name="title_media">My media</string>
|
<string name="title_media">My media</string>
|
||||||
<string name="title_favorite">Favorites</string>
|
<string name="title_favorite">Favorites</string>
|
||||||
|
@ -49,9 +49,11 @@
|
||||||
<string name="manage_servers">Manage servers</string>
|
<string name="manage_servers">Manage servers</string>
|
||||||
<string name="settings_category_appearance">Appearance</string>
|
<string name="settings_category_appearance">Appearance</string>
|
||||||
<string name="theme">Theme</string>
|
<string name="theme">Theme</string>
|
||||||
<string name="error_preparing_player_items">Error preparing player items</string>
|
<string name="error_preparing_player_items">Error preparing player items.</string>
|
||||||
<string name="version">Version</string>
|
<string name="view_details">View details</string>
|
||||||
<string name="open_source_licenses">Open source licenses</string>
|
<string name="view_details_underlined"><u>@string/view_details</u></string>
|
||||||
<string name="about">About</string>
|
<string name="about">About</string>
|
||||||
<string name="privacy_policy">Privacy policy</string>
|
<string name="privacy_policy">Privacy policy</string>
|
||||||
|
<string name="app_info">App info</string>
|
||||||
|
<string name="unknown_error">Unknown error</string>
|
||||||
</resources>
|
</resources>
|
|
@ -10,4 +10,14 @@
|
||||||
<item name="cornerFamily">rounded</item>
|
<item name="cornerFamily">rounded</item>
|
||||||
<item name="cornerSize">10dp</item>
|
<item name="cornerSize">10dp</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="ErrorDialogStyle" parent="MaterialAlertDialog.MaterialComponents">
|
||||||
|
<item name="materialAlertDialogBodyTextStyle">@style/AlertDialogBodyText</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AlertDialogBodyText" parent="MaterialAlertDialog.MaterialComponents.Body.Text">
|
||||||
|
<item name="android:fontFamily">monospace</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -46,10 +46,8 @@
|
||||||
app:title="@string/privacy_policy" />
|
app:title="@string/privacy_policy" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:key="ossLicenses"
|
app:key="appInfo"
|
||||||
app:title="@string/open_source_licenses" />
|
app:title="@string/app_info" />
|
||||||
|
|
||||||
<dev.jdtech.jellyfin.utils.VersionPreference app:title="@string/version" />
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,12 @@ buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url "https://plugins.gradle.org/m2/"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.0.0'
|
classpath 'com.android.tools.build:gradle:7.0.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
@ -17,8 +20,8 @@ buildscript {
|
||||||
def hilt_version = "2.38.1"
|
def hilt_version = "2.38.1"
|
||||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||||
|
|
||||||
def oss_licenses_version = "0.10.4"
|
def about_libraries_version = "8.9.1"
|
||||||
classpath "com.google.android.gms:oss-licenses-plugin:$oss_licenses_version"
|
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue