Introduce klint (#186)
* Add ktlint plugin * Make code ktlint compliant * Make code ktlint compliant
This commit is contained in:
parent
45ccea57af
commit
ad5e722d44
105 changed files with 682 additions and 524 deletions
|
@ -6,6 +6,7 @@ plugins {
|
||||||
id("androidx.navigation.safeargs.kotlin")
|
id("androidx.navigation.safeargs.kotlin")
|
||||||
id("dagger.hilt.android.plugin")
|
id("dagger.hilt.android.plugin")
|
||||||
id("com.mikepenz.aboutlibraries.plugin")
|
id("com.mikepenz.aboutlibraries.plugin")
|
||||||
|
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
@ -59,6 +60,12 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ktlint {
|
||||||
|
android.set(true)
|
||||||
|
ignoreFailures.set(false)
|
||||||
|
disabledRules.add("max-line-length")
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.leanback:leanback:1.2.0-alpha02")
|
implementation("androidx.leanback:leanback:1.2.0-alpha02")
|
||||||
|
|
||||||
|
@ -67,7 +74,6 @@ dependencies {
|
||||||
implementation("androidx.activity:activity-ktx:1.6.1")
|
implementation("androidx.activity:activity-ktx:1.6.1")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
|
||||||
|
|
||||||
// Material
|
// Material
|
||||||
implementation("com.google.android.material:material:1.7.0")
|
implementation("com.google.android.material:material:1.7.0")
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,11 @@ abstract class BasePlayerActivity: AppCompatActivity() {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
protected fun hideSystemUI() {
|
protected fun hideSystemUI() {
|
||||||
// These methods are deprecated but we still use them because the new WindowInsetsControllerCompat has a bug which makes the action bar reappear
|
// These methods are deprecated but we still use them because the new WindowInsetsControllerCompat has a bug which makes the action bar reappear
|
||||||
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
|
window.decorView.systemUiVisibility = (
|
||||||
|
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or
|
||||||
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||||
|
)
|
||||||
|
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
|
|
@ -13,11 +13,11 @@ import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.database.Server
|
import dev.jdtech.jellyfin.database.Server
|
||||||
import dev.jdtech.jellyfin.models.User
|
import dev.jdtech.jellyfin.models.User
|
||||||
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.BaseItemPerson
|
import org.jellyfin.sdk.model.api.BaseItemPerson
|
||||||
import org.jellyfin.sdk.model.api.ImageType
|
import org.jellyfin.sdk.model.api.ImageType
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@BindingAdapter("servers")
|
@BindingAdapter("servers")
|
||||||
fun bindServers(recyclerView: RecyclerView, data: List<Server>?) {
|
fun bindServers(recyclerView: RecyclerView, data: List<Server>?) {
|
||||||
|
@ -96,13 +96,13 @@ fun bindBaseItemImage(imageView: ImageView, episode: BaseItemDto?) {
|
||||||
}
|
}
|
||||||
|
|
||||||
imageView
|
imageView
|
||||||
.loadImage("/items/${imageItemId}/Images/$imageType")
|
.loadImage("/items/$imageItemId/Images/$imageType")
|
||||||
.posterDescription(episode.name)
|
.posterDescription(episode.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@BindingAdapter("seasonPoster")
|
@BindingAdapter("seasonPoster")
|
||||||
fun bindSeasonPoster(imageView: ImageView, seasonId: UUID) {
|
fun bindSeasonPoster(imageView: ImageView, seasonId: UUID) {
|
||||||
imageView.loadImage("/items/${seasonId}/Images/${ImageType.PRIMARY}")
|
imageView.loadImage("/items/$seasonId/Images/${ImageType.PRIMARY}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@BindingAdapter("userImage")
|
@BindingAdapter("userImage")
|
||||||
|
@ -112,7 +112,11 @@ fun bindUserImage(imageView: ImageView, user: User) {
|
||||||
.posterDescription(user.name)
|
.posterDescription(user.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ImageView.loadImage(url: String, @DrawableRes placeholderId: Int = R.color.neutral_800, @DrawableRes errorPlaceHolderId: Int? = null): View {
|
private fun ImageView.loadImage(
|
||||||
|
url: String,
|
||||||
|
@DrawableRes placeholderId: Int = R.color.neutral_800,
|
||||||
|
@DrawableRes errorPlaceHolderId: Int? = null
|
||||||
|
): View {
|
||||||
val api = JellyfinApi.getInstance(context.applicationContext)
|
val api = JellyfinApi.getInstance(context.applicationContext)
|
||||||
|
|
||||||
Glide
|
Glide
|
||||||
|
|
|
@ -59,7 +59,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (uiModeManager.currentModeType != Configuration.UI_MODE_TYPE_TELEVISION) {
|
if (uiModeManager.currentModeType != Configuration.UI_MODE_TYPE_TELEVISION) {
|
||||||
val navView: NavigationBarView = binding.navView as NavigationBarView
|
val navView: NavigationBarView = binding.navView as NavigationBarView
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ import dev.jdtech.jellyfin.mpv.TrackType
|
||||||
import dev.jdtech.jellyfin.utils.AppPreferences
|
import dev.jdtech.jellyfin.utils.AppPreferences
|
||||||
import dev.jdtech.jellyfin.utils.PlayerGestureHelper
|
import dev.jdtech.jellyfin.utils.PlayerGestureHelper
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class PlayerActivity : BasePlayerActivity() {
|
class PlayerActivity : BasePlayerActivity() {
|
||||||
|
@ -177,4 +177,3 @@ class PlayerActivity : BasePlayerActivity() {
|
||||||
hideSystemUI()
|
hideSystemUI()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ import dev.jdtech.jellyfin.databinding.DiscoveredServerItemBinding
|
||||||
import dev.jdtech.jellyfin.models.DiscoveredServer
|
import dev.jdtech.jellyfin.models.DiscoveredServer
|
||||||
|
|
||||||
class DiscoveredServerListAdapter(
|
class DiscoveredServerListAdapter(
|
||||||
private val clickListener: (server: DiscoveredServer) -> Unit) :
|
private val clickListener: (server: DiscoveredServer) -> Unit
|
||||||
|
) :
|
||||||
ListAdapter<DiscoveredServer, DiscoveredServerListAdapter.DiscoveredServerViewHolder>(
|
ListAdapter<DiscoveredServer, DiscoveredServerListAdapter.DiscoveredServerViewHolder>(
|
||||||
DiffCallback
|
DiffCallback
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -12,8 +12,8 @@ import dev.jdtech.jellyfin.databinding.SeasonHeaderBinding
|
||||||
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.utils.downloadMetadataToBaseItemDto
|
import dev.jdtech.jellyfin.utils.downloadMetadataToBaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
private const val ITEM_VIEW_TYPE_HEADER = 0
|
private const val ITEM_VIEW_TYPE_HEADER = 0
|
||||||
private const val ITEM_VIEW_TYPE_EPISODE = 1
|
private const val ITEM_VIEW_TYPE_EPISODE = 1
|
||||||
|
|
|
@ -13,8 +13,7 @@ import dev.jdtech.jellyfin.utils.downloadSeriesMetadataToBaseItemDto
|
||||||
class DownloadSeriesListAdapter(
|
class DownloadSeriesListAdapter(
|
||||||
private val onClickListener: OnClickListener,
|
private val onClickListener: OnClickListener,
|
||||||
private val fixedWidth: Boolean = false,
|
private val fixedWidth: Boolean = false,
|
||||||
) :
|
) : ListAdapter<DownloadSeriesMetadata, DownloadSeriesListAdapter.ItemViewHolder>(DiffCallback) {
|
||||||
ListAdapter<DownloadSeriesMetadata, DownloadSeriesListAdapter.ItemViewHolder>(DiffCallback) {
|
|
||||||
|
|
||||||
class ItemViewHolder(private var binding: BaseItemBinding, private val parent: ViewGroup) :
|
class ItemViewHolder(private var binding: BaseItemBinding, private val parent: ViewGroup) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
@ -32,11 +31,17 @@ class DownloadSeriesListAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object DiffCallback : DiffUtil.ItemCallback<DownloadSeriesMetadata>() {
|
companion object DiffCallback : DiffUtil.ItemCallback<DownloadSeriesMetadata>() {
|
||||||
override fun areItemsTheSame(oldItem: DownloadSeriesMetadata, newItem: DownloadSeriesMetadata): Boolean {
|
override fun areItemsTheSame(
|
||||||
|
oldItem: DownloadSeriesMetadata,
|
||||||
|
newItem: DownloadSeriesMetadata
|
||||||
|
): Boolean {
|
||||||
return oldItem.itemId == newItem.itemId
|
return oldItem.itemId == newItem.itemId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: DownloadSeriesMetadata, newItem: DownloadSeriesMetadata): Boolean {
|
override fun areContentsTheSame(
|
||||||
|
oldItem: DownloadSeriesMetadata,
|
||||||
|
newItem: DownloadSeriesMetadata
|
||||||
|
): Boolean {
|
||||||
return oldItem == newItem
|
return oldItem == newItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +52,8 @@ class DownloadSeriesListAdapter(
|
||||||
LayoutInflater.from(parent.context),
|
LayoutInflater.from(parent.context),
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
), parent
|
),
|
||||||
|
parent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@ import dev.jdtech.jellyfin.utils.downloadMetadataToBaseItemDto
|
||||||
class DownloadViewItemListAdapter(
|
class DownloadViewItemListAdapter(
|
||||||
private val onClickListener: OnClickListener,
|
private val onClickListener: OnClickListener,
|
||||||
private val fixedWidth: Boolean = false,
|
private val fixedWidth: Boolean = false,
|
||||||
) :
|
) : ListAdapter<PlayerItem, DownloadViewItemListAdapter.ItemViewHolder>(DiffCallback) {
|
||||||
ListAdapter<PlayerItem, DownloadViewItemListAdapter.ItemViewHolder>(DiffCallback) {
|
|
||||||
|
|
||||||
class ItemViewHolder(private var binding: BaseItemBinding, private val parent: ViewGroup) :
|
class ItemViewHolder(private var binding: BaseItemBinding, private val parent: ViewGroup) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
@ -48,7 +47,8 @@ class DownloadViewItemListAdapter(
|
||||||
LayoutInflater.from(parent.context),
|
LayoutInflater.from(parent.context),
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
), parent
|
),
|
||||||
|
parent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dev.jdtech.jellyfin.databinding.EpisodeItemBinding
|
import dev.jdtech.jellyfin.databinding.EpisodeItemBinding
|
||||||
import dev.jdtech.jellyfin.databinding.SeasonHeaderBinding
|
import dev.jdtech.jellyfin.databinding.SeasonHeaderBinding
|
||||||
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
private const val ITEM_VIEW_TYPE_HEADER = 0
|
private const val ITEM_VIEW_TYPE_HEADER = 0
|
||||||
private const val ITEM_VIEW_TYPE_EPISODE = 1
|
private const val ITEM_VIEW_TYPE_EPISODE = 1
|
||||||
|
|
|
@ -17,8 +17,10 @@ class HomeEpisodeListAdapter(private val onClickListener: OnClickListener) : Lis
|
||||||
fun bind(episode: BaseItemDto) {
|
fun bind(episode: BaseItemDto) {
|
||||||
binding.episode = episode
|
binding.episode = episode
|
||||||
if (episode.userData?.playedPercentage != null) {
|
if (episode.userData?.playedPercentage != null) {
|
||||||
binding.progressBar.layoutParams.width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
|
binding.progressBar.layoutParams.width = TypedValue.applyDimension(
|
||||||
(episode.userData?.playedPercentage?.times(2.24))!!.toFloat(), binding.progressBar.context.resources.displayMetrics).toInt()
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
(episode.userData?.playedPercentage?.times(2.24))!!.toFloat(), binding.progressBar.context.resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
binding.progressBar.visibility = View.VISIBLE
|
binding.progressBar.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@ import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
class ViewItemListAdapter(
|
class ViewItemListAdapter(
|
||||||
private val onClickListener: OnClickListener,
|
private val onClickListener: OnClickListener,
|
||||||
private val fixedWidth: Boolean = false,
|
private val fixedWidth: Boolean = false,
|
||||||
) :
|
) : ListAdapter<BaseItemDto, ViewItemListAdapter.ItemViewHolder>(DiffCallback) {
|
||||||
ListAdapter<BaseItemDto, ViewItemListAdapter.ItemViewHolder>(DiffCallback) {
|
|
||||||
|
|
||||||
class ItemViewHolder(private var binding: BaseItemBinding, private val parent: ViewGroup) :
|
class ItemViewHolder(private var binding: BaseItemBinding, private val parent: ViewGroup) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
@ -49,7 +48,8 @@ class ViewItemListAdapter(
|
||||||
LayoutInflater.from(parent.context),
|
LayoutInflater.from(parent.context),
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
), parent
|
),
|
||||||
|
parent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,8 @@ class ViewItemPagingAdapter(
|
||||||
LayoutInflater.from(parent.context),
|
LayoutInflater.from(parent.context),
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
), parent
|
),
|
||||||
|
parent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,8 @@ class ViewListAdapter(
|
||||||
NextUpSectionBinding.inflate(
|
NextUpSectionBinding.inflate(
|
||||||
LayoutInflater.from(
|
LayoutInflater.from(
|
||||||
parent.context
|
parent.context
|
||||||
), parent, false
|
),
|
||||||
|
parent, false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
ITEM_VIEW_TYPE_VIEW -> ViewViewHolder(
|
ITEM_VIEW_TYPE_VIEW -> ViewViewHolder(
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dev.jdtech.jellyfin.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dev.jdtech.jellyfin.BuildConfig
|
import dev.jdtech.jellyfin.BuildConfig
|
||||||
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.api.client.extensions.devicesApi
|
import org.jellyfin.sdk.api.client.extensions.devicesApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.itemsApi
|
import org.jellyfin.sdk.api.client.extensions.itemsApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.mediaInfoApi
|
import org.jellyfin.sdk.api.client.extensions.mediaInfoApi
|
||||||
|
@ -15,7 +16,6 @@ import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||||
import org.jellyfin.sdk.api.client.extensions.videosApi
|
import org.jellyfin.sdk.api.client.extensions.videosApi
|
||||||
import org.jellyfin.sdk.createJellyfin
|
import org.jellyfin.sdk.createJellyfin
|
||||||
import org.jellyfin.sdk.model.ClientInfo
|
import org.jellyfin.sdk.model.ClientInfo
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Jellyfin API class using org.jellyfin.sdk:jellyfin-platform-android
|
* Jellyfin API class using org.jellyfin.sdk:jellyfin-platform-android
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.jdtech.jellyfin.database
|
package dev.jdtech.jellyfin.database
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
|
|
|
@ -4,7 +4,7 @@ import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import dev.jdtech.jellyfin.models.DownloadItem
|
import dev.jdtech.jellyfin.models.DownloadItem
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface DownloadDatabaseDao {
|
interface DownloadDatabaseDao {
|
||||||
|
@ -28,5 +28,4 @@ interface DownloadDatabaseDao {
|
||||||
|
|
||||||
@Query("SELECT EXISTS (SELECT 1 FROM downloads WHERE id = :id)")
|
@Query("SELECT EXISTS (SELECT 1 FROM downloads WHERE id = :id)")
|
||||||
fun exists(id: UUID): Boolean
|
fun exists(id: UUID): Boolean
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,7 +1,11 @@
|
||||||
package dev.jdtech.jellyfin.database
|
package dev.jdtech.jellyfin.database
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.room.*
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface ServerDatabaseDao {
|
interface ServerDatabaseDao {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
object SharedPreferencesModule {
|
object SharedPreferencesModule {
|
||||||
|
|
|
@ -19,7 +19,6 @@ class DeleteServerDialogFragment(private val viewModel: ServerSelectViewModel, v
|
||||||
viewModel.deleteServer(server)
|
viewModel.deleteServer(server)
|
||||||
}
|
}
|
||||||
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
|
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
|
||||||
|
|
||||||
}
|
}
|
||||||
builder.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
|
|
|
@ -28,7 +28,6 @@ class ErrorDialogFragment(
|
||||||
|
|
||||||
val shareIntent = Intent.createChooser(sendIntent, null)
|
val shareIntent = Intent.createChooser(sendIntent, null)
|
||||||
startActivity(shareIntent)
|
startActivity(shareIntent)
|
||||||
|
|
||||||
}
|
}
|
||||||
builder.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
|
|
|
@ -8,9 +8,9 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.utils.SortBy
|
import dev.jdtech.jellyfin.utils.SortBy
|
||||||
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
|
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
|
||||||
import org.jellyfin.sdk.model.api.SortOrder
|
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
|
|
||||||
class SortDialogFragment(
|
class SortDialogFragment(
|
||||||
private val parentId: UUID,
|
private val parentId: UUID,
|
||||||
|
|
|
@ -29,7 +29,5 @@ class SpeedSelectionDialogFragment(
|
||||||
}
|
}
|
||||||
builder.create()
|
builder.create()
|
||||||
} ?: throw IllegalStateException("Activity cannot be null")
|
} ?: throw IllegalStateException("Activity cannot be null")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,7 +22,8 @@ class TrackSelectionDialogFragment(
|
||||||
builder.setTitle(getString(R.string.select_audio_track))
|
builder.setTitle(getString(R.string.select_audio_track))
|
||||||
.setSingleChoiceItems(
|
.setSingleChoiceItems(
|
||||||
getTrackNames(viewModel.currentAudioTracks),
|
getTrackNames(viewModel.currentAudioTracks),
|
||||||
viewModel.currentAudioTracks.indexOfFirst { it.selected }) { dialog, which ->
|
viewModel.currentAudioTracks.indexOfFirst { it.selected }
|
||||||
|
) { dialog, which ->
|
||||||
viewModel.switchToTrack(
|
viewModel.switchToTrack(
|
||||||
TrackType.AUDIO,
|
TrackType.AUDIO,
|
||||||
viewModel.currentAudioTracks[which]
|
viewModel.currentAudioTracks[which]
|
||||||
|
@ -38,7 +39,8 @@ class TrackSelectionDialogFragment(
|
||||||
builder.setTitle(getString(R.string.select_subtile_track))
|
builder.setTitle(getString(R.string.select_subtile_track))
|
||||||
.setSingleChoiceItems(
|
.setSingleChoiceItems(
|
||||||
getTrackNames(viewModel.currentSubtitleTracks),
|
getTrackNames(viewModel.currentSubtitleTracks),
|
||||||
viewModel.currentSubtitleTracks.indexOfFirst { if (viewModel.disableSubtitle) it.ffIndex == -1 else it.selected }) { dialog, which ->
|
viewModel.currentSubtitleTracks.indexOfFirst { if (viewModel.disableSubtitle) it.ffIndex == -1 else it.selected }
|
||||||
|
) { dialog, which ->
|
||||||
viewModel.switchToTrack(
|
viewModel.switchToTrack(
|
||||||
TrackType.SUBTITLE,
|
TrackType.SUBTITLE,
|
||||||
viewModel.currentSubtitleTracks[which]
|
viewModel.currentSubtitleTracks[which]
|
||||||
|
|
|
@ -4,9 +4,9 @@ import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
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 java.lang.IllegalStateException
|
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
class VideoVersionDialogFragment(
|
class VideoVersionDialogFragment(
|
||||||
|
|
|
@ -31,7 +31,8 @@ class AddServerFragment : Fragment() {
|
||||||
private val viewModel: AddServerViewModel by viewModels()
|
private val viewModel: AddServerViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentAddServerBinding.inflate(inflater)
|
binding = FragmentAddServerBinding.inflate(inflater)
|
||||||
|
@ -136,7 +137,9 @@ class AddServerFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindDiscoveredServersStateServers(serversState: AddServerViewModel.DiscoveredServersState.Servers) {
|
private fun bindDiscoveredServersStateServers(
|
||||||
|
serversState: AddServerViewModel.DiscoveredServersState.Servers
|
||||||
|
) {
|
||||||
val servers = serversState.servers
|
val servers = serversState.servers
|
||||||
if (servers.isEmpty()) {
|
if (servers.isEmpty()) {
|
||||||
binding.serversRecyclerView.isVisible = false
|
binding.serversRecyclerView.isVisible = false
|
||||||
|
|
|
@ -12,16 +12,18 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.jdtech.jellyfin.adapters.*
|
import dev.jdtech.jellyfin.adapters.DownloadSeriesListAdapter
|
||||||
|
import dev.jdtech.jellyfin.adapters.DownloadViewItemListAdapter
|
||||||
|
import dev.jdtech.jellyfin.adapters.DownloadsListAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentDownloadBinding
|
import dev.jdtech.jellyfin.databinding.FragmentDownloadBinding
|
||||||
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
|
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
|
||||||
import dev.jdtech.jellyfin.viewmodels.DownloadViewModel
|
import dev.jdtech.jellyfin.viewmodels.DownloadViewModel
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class DownloadFragment : Fragment() {
|
class DownloadFragment : Fragment() {
|
||||||
|
@ -32,15 +34,18 @@ class DownloadFragment : Fragment() {
|
||||||
private lateinit var errorDialog: ErrorDialogFragment
|
private lateinit var errorDialog: ErrorDialogFragment
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentDownloadBinding.inflate(inflater, container, false)
|
binding = FragmentDownloadBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
binding.downloadsRecyclerView.adapter = DownloadsListAdapter(
|
binding.downloadsRecyclerView.adapter =
|
||||||
|
DownloadsListAdapter(
|
||||||
DownloadViewItemListAdapter.OnClickListener { item ->
|
DownloadViewItemListAdapter.OnClickListener { item ->
|
||||||
navigateToMediaInfoFragment(item)
|
navigateToMediaInfoFragment(item)
|
||||||
}, DownloadSeriesListAdapter.OnClickListener { item ->
|
},
|
||||||
|
DownloadSeriesListAdapter.OnClickListener { item ->
|
||||||
navigateToDownloadSeriesFragment(item)
|
navigateToDownloadSeriesFragment(item)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -97,11 +102,7 @@ class DownloadFragment : Fragment() {
|
||||||
private fun navigateToMediaInfoFragment(item: PlayerItem) {
|
private fun navigateToMediaInfoFragment(item: PlayerItem) {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
DownloadFragmentDirections.actionDownloadFragmentToMediaInfoFragment(
|
DownloadFragmentDirections.actionDownloadFragmentToMediaInfoFragment(
|
||||||
UUID.randomUUID(),
|
UUID.randomUUID(), item.name, item.item!!.type, item, isOffline = true
|
||||||
item.name,
|
|
||||||
item.item!!.type,
|
|
||||||
item,
|
|
||||||
isOffline = true
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -109,8 +110,7 @@ class DownloadFragment : Fragment() {
|
||||||
private fun navigateToDownloadSeriesFragment(series: DownloadSeriesMetadata) {
|
private fun navigateToDownloadSeriesFragment(series: DownloadSeriesMetadata) {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
DownloadFragmentDirections.actionDownloadFragmentToDownloadSeriesFragment(
|
DownloadFragmentDirections.actionDownloadFragmentToDownloadSeriesFragment(
|
||||||
seriesMetadata = series,
|
seriesMetadata = series, seriesName = series.name
|
||||||
seriesName = series.name
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
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.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -18,8 +18,8 @@ import dev.jdtech.jellyfin.databinding.FragmentDownloadSeriesBinding
|
||||||
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.viewmodels.DownloadSeriesViewModel
|
import dev.jdtech.jellyfin.viewmodels.DownloadSeriesViewModel
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class DownloadSeriesFragment : Fragment() {
|
class DownloadSeriesFragment : Fragment() {
|
||||||
|
@ -32,7 +32,8 @@ class DownloadSeriesFragment : Fragment() {
|
||||||
private val args: DownloadSeriesFragmentArgs by navArgs()
|
private val args: DownloadSeriesFragmentArgs by navArgs()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentDownloadSeriesBinding.inflate(inflater, container, false)
|
binding = FragmentDownloadSeriesBinding.inflate(inflater, container, false)
|
||||||
|
@ -45,9 +46,12 @@ class DownloadSeriesFragment : Fragment() {
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
binding.episodesRecyclerView.adapter =
|
binding.episodesRecyclerView.adapter =
|
||||||
DownloadEpisodeListAdapter(DownloadEpisodeListAdapter.OnClickListener { episode ->
|
DownloadEpisodeListAdapter(
|
||||||
|
DownloadEpisodeListAdapter.OnClickListener { episode ->
|
||||||
navigateToEpisodeBottomSheetFragment(episode)
|
navigateToEpisodeBottomSheetFragment(episode)
|
||||||
}, args.seriesMetadata)
|
},
|
||||||
|
args.seriesMetadata
|
||||||
|
)
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
|
|
@ -26,11 +26,11 @@ import dev.jdtech.jellyfin.utils.setTintColor
|
||||||
import dev.jdtech.jellyfin.utils.setTintColorAttribute
|
import dev.jdtech.jellyfin.utils.setTintColorAttribute
|
||||||
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
|
import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.LocationType
|
import org.jellyfin.sdk.model.api.LocationType
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
|
@ -195,7 +195,6 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
binding.episodeName.text = String.format(
|
binding.episodeName.text = String.format(
|
||||||
getString(R.string.episode_name_extended),
|
getString(R.string.episode_name_extended),
|
||||||
episode.parentIndexNumber,
|
episode.parentIndexNumber,
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
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.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -32,7 +32,8 @@ class FavoriteFragment : Fragment() {
|
||||||
private lateinit var errorDialog: ErrorDialogFragment
|
private lateinit var errorDialog: ErrorDialogFragment
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentFavoriteBinding.inflate(inflater, container, false)
|
binding = FragmentFavoriteBinding.inflate(inflater, container, false)
|
||||||
|
@ -40,9 +41,11 @@ class FavoriteFragment : Fragment() {
|
||||||
binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
|
binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
|
||||||
ViewItemListAdapter.OnClickListener { item ->
|
ViewItemListAdapter.OnClickListener { item ->
|
||||||
navigateToMediaInfoFragment(item)
|
navigateToMediaInfoFragment(item)
|
||||||
}, HomeEpisodeListAdapter.OnClickListener { item ->
|
},
|
||||||
|
HomeEpisodeListAdapter.OnClickListener { item ->
|
||||||
navigateToEpisodeBottomSheetFragment(item)
|
navigateToEpisodeBottomSheetFragment(item)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
|
|
@ -57,7 +57,8 @@ class HomeFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val menuHost: MenuHost = requireActivity()
|
val menuHost: MenuHost = requireActivity()
|
||||||
menuHost.addMenuProvider(object : MenuProvider {
|
menuHost.addMenuProvider(
|
||||||
|
object : MenuProvider {
|
||||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||||
menuInflater.inflate(R.menu.home_menu, menu)
|
menuInflater.inflate(R.menu.home_menu, menu)
|
||||||
}
|
}
|
||||||
|
@ -71,7 +72,9 @@ class HomeFragment : Fragment() {
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
},
|
||||||
|
viewLifecycleOwner, Lifecycle.State.RESUMED
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -97,7 +100,8 @@ class HomeFragment : Fragment() {
|
||||||
else -> Toast.makeText(requireContext(), R.string.unknown_error, LENGTH_LONG)
|
else -> Toast.makeText(requireContext(), R.string.unknown_error, LENGTH_LONG)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.errorLayout.errorRetryButton.setOnClickListener {
|
binding.errorLayout.errorRetryButton.setOnClickListener {
|
||||||
viewModel.loadData()
|
viewModel.loadData()
|
||||||
|
|
|
@ -4,7 +4,12 @@ import android.app.UiModeManager
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
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
|
||||||
|
@ -20,18 +25,18 @@ import androidx.paging.LoadState
|
||||||
import androidx.recyclerview.widget.LinearSnapHelper
|
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.viewmodels.LibraryViewModel
|
|
||||||
import dev.jdtech.jellyfin.adapters.ViewItemPagingAdapter
|
import dev.jdtech.jellyfin.adapters.ViewItemPagingAdapter
|
||||||
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
|
import dev.jdtech.jellyfin.databinding.FragmentLibraryBinding
|
||||||
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
import dev.jdtech.jellyfin.dialogs.ErrorDialogFragment
|
||||||
import dev.jdtech.jellyfin.dialogs.SortDialogFragment
|
import dev.jdtech.jellyfin.dialogs.SortDialogFragment
|
||||||
import dev.jdtech.jellyfin.utils.SortBy
|
import dev.jdtech.jellyfin.utils.SortBy
|
||||||
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
|
import dev.jdtech.jellyfin.utils.checkIfLoginRequired
|
||||||
|
import dev.jdtech.jellyfin.viewmodels.LibraryViewModel
|
||||||
|
import java.lang.IllegalArgumentException
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.SortOrder
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
import java.lang.IllegalArgumentException
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class LibraryFragment : Fragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
@ -47,7 +52,8 @@ class LibraryFragment : Fragment() {
|
||||||
lateinit var sp: SharedPreferences
|
lateinit var sp: SharedPreferences
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentLibraryBinding.inflate(inflater, container, false)
|
binding = FragmentLibraryBinding.inflate(inflater, container, false)
|
||||||
|
@ -95,7 +101,8 @@ class LibraryFragment : Fragment() {
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, viewLifecycleOwner, Lifecycle.State.RESUMED
|
},
|
||||||
|
viewLifecycleOwner, Lifecycle.State.RESUMED
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.title?.text = args.libraryName
|
binding.title?.text = args.libraryName
|
||||||
|
@ -117,9 +124,11 @@ class LibraryFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.itemsRecyclerView.adapter =
|
binding.itemsRecyclerView.adapter =
|
||||||
ViewItemPagingAdapter(ViewItemPagingAdapter.OnClickListener { item ->
|
ViewItemPagingAdapter(
|
||||||
|
ViewItemPagingAdapter.OnClickListener { item ->
|
||||||
navigateToMediaInfoFragment(item)
|
navigateToMediaInfoFragment(item)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
(binding.itemsRecyclerView.adapter as ViewItemPagingAdapter).addLoadStateListener {
|
(binding.itemsRecyclerView.adapter as ViewItemPagingAdapter).addLoadStateListener {
|
||||||
when (it.refresh) {
|
when (it.refresh) {
|
||||||
|
|
|
@ -31,7 +31,8 @@ class LoginFragment : Fragment() {
|
||||||
private val viewModel: LoginViewModel by viewModels()
|
private val viewModel: LoginViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentLoginBinding.inflate(inflater)
|
binding = FragmentLoginBinding.inflate(inflater)
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.WindowManager
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.core.view.MenuHost
|
import androidx.core.view.MenuHost
|
||||||
import androidx.core.view.MenuProvider
|
import androidx.core.view.MenuProvider
|
||||||
|
@ -41,9 +47,11 @@ class MediaFragment : Fragment() {
|
||||||
binding = FragmentMediaBinding.inflate(inflater, container, false)
|
binding = FragmentMediaBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
binding.viewsRecyclerView.adapter =
|
binding.viewsRecyclerView.adapter =
|
||||||
CollectionListAdapter(CollectionListAdapter.OnClickListener { library ->
|
CollectionListAdapter(
|
||||||
|
CollectionListAdapter.OnClickListener { library ->
|
||||||
navigateToLibraryFragment(library)
|
navigateToLibraryFragment(library)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
@ -73,7 +81,8 @@ class MediaFragment : Fragment() {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val menuHost: MenuHost = requireActivity()
|
val menuHost: MenuHost = requireActivity()
|
||||||
menuHost.addMenuProvider(object : MenuProvider {
|
menuHost.addMenuProvider(
|
||||||
|
object : MenuProvider {
|
||||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||||
menuInflater.inflate(R.menu.media_menu, menu)
|
menuInflater.inflate(R.menu.media_menu, menu)
|
||||||
|
|
||||||
|
@ -98,7 +107,9 @@ class MediaFragment : Fragment() {
|
||||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
},
|
||||||
|
viewLifecycleOwner, Lifecycle.State.RESUMED
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -133,7 +144,6 @@ class MediaFragment : Fragment() {
|
||||||
binding.viewsRecyclerView.isVisible = false
|
binding.viewsRecyclerView.isVisible = false
|
||||||
binding.errorLayout.errorPanel.isVisible = true
|
binding.errorLayout.errorPanel.isVisible = true
|
||||||
checkIfLoginRequired(uiState.error.message)
|
checkIfLoginRequired(uiState.error.message)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToLibraryFragment(library: BaseItemDto) {
|
private fun navigateToLibraryFragment(library: BaseItemDto) {
|
||||||
|
|
|
@ -31,11 +31,11 @@ import dev.jdtech.jellyfin.utils.setTintColor
|
||||||
import dev.jdtech.jellyfin.utils.setTintColorAttribute
|
import dev.jdtech.jellyfin.utils.setTintColorAttribute
|
||||||
import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
|
import dev.jdtech.jellyfin.viewmodels.MediaInfoViewModel
|
||||||
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
import dev.jdtech.jellyfin.viewmodels.PlayerViewModel
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MediaInfoFragment : Fragment() {
|
class MediaInfoFragment : Fragment() {
|
||||||
|
@ -48,7 +48,8 @@ class MediaInfoFragment : Fragment() {
|
||||||
lateinit var errorDialog: ErrorDialogFragment
|
lateinit var errorDialog: ErrorDialogFragment
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentMediaInfoBinding.inflate(inflater, container, false)
|
binding = FragmentMediaInfoBinding.inflate(inflater, container, false)
|
||||||
|
@ -111,9 +112,12 @@ class MediaInfoFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.seasonsRecyclerView.adapter =
|
binding.seasonsRecyclerView.adapter =
|
||||||
ViewItemListAdapter(ViewItemListAdapter.OnClickListener { season ->
|
ViewItemListAdapter(
|
||||||
|
ViewItemListAdapter.OnClickListener { season ->
|
||||||
navigateToSeasonFragment(season)
|
navigateToSeasonFragment(season)
|
||||||
}, fixedWidth = true)
|
},
|
||||||
|
fixedWidth = true
|
||||||
|
)
|
||||||
binding.peopleRecyclerView.adapter = PersonListAdapter { person ->
|
binding.peopleRecyclerView.adapter = PersonListAdapter { person ->
|
||||||
navigateToPersonDetail(person.id)
|
navigateToPersonDetail(person.id)
|
||||||
}
|
}
|
||||||
|
@ -187,7 +191,6 @@ class MediaInfoFragment : Fragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
binding.favoriteButton.isVisible = false
|
binding.favoriteButton.isVisible = false
|
||||||
binding.checkButton.isVisible = false
|
binding.checkButton.isVisible = false
|
||||||
|
@ -225,7 +228,6 @@ class MediaInfoFragment : Fragment() {
|
||||||
false -> binding.checkButton.setTintColorAttribute(R.attr.colorOnSecondaryContainer, requireActivity().theme)
|
false -> binding.checkButton.setTintColorAttribute(R.attr.colorOnSecondaryContainer, requireActivity().theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Favorite icon
|
// Favorite icon
|
||||||
val favoriteDrawable = when (favorite) {
|
val favoriteDrawable = when (favorite) {
|
||||||
true -> R.drawable.ic_heart_filled
|
true -> R.drawable.ic_heart_filled
|
||||||
|
@ -248,7 +250,6 @@ class MediaInfoFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
binding.name.text = item.name
|
binding.name.text = item.name
|
||||||
binding.originalTitle.text = item.originalTitle
|
binding.originalTitle.text = item.originalTitle
|
||||||
if (dateString.isEmpty()) {
|
if (dateString.isEmpty()) {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
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.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -34,7 +34,8 @@ class SearchResultFragment : Fragment() {
|
||||||
private lateinit var errorDialog: ErrorDialogFragment
|
private lateinit var errorDialog: ErrorDialogFragment
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentSearchResultBinding.inflate(inflater, container, false)
|
binding = FragmentSearchResultBinding.inflate(inflater, container, false)
|
||||||
|
@ -42,9 +43,11 @@ class SearchResultFragment : Fragment() {
|
||||||
binding.searchResultsRecyclerView.adapter = FavoritesListAdapter(
|
binding.searchResultsRecyclerView.adapter = FavoritesListAdapter(
|
||||||
ViewItemListAdapter.OnClickListener { item ->
|
ViewItemListAdapter.OnClickListener { item ->
|
||||||
navigateToMediaInfoFragment(item)
|
navigateToMediaInfoFragment(item)
|
||||||
}, HomeEpisodeListAdapter.OnClickListener { item ->
|
},
|
||||||
|
HomeEpisodeListAdapter.OnClickListener { item ->
|
||||||
navigateToEpisodeBottomSheetFragment(item)
|
navigateToEpisodeBottomSheetFragment(item)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
@ -73,7 +76,6 @@ class SearchResultFragment : Fragment() {
|
||||||
errorDialog.show(parentFragmentManager, "errordialog")
|
errorDialog.show(parentFragmentManager, "errordialog")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
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.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -32,7 +32,8 @@ class SeasonFragment : Fragment() {
|
||||||
private lateinit var errorDialog: ErrorDialogFragment
|
private lateinit var errorDialog: ErrorDialogFragment
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentSeasonBinding.inflate(inflater, container, false)
|
binding = FragmentSeasonBinding.inflate(inflater, container, false)
|
||||||
|
@ -70,10 +71,12 @@ class SeasonFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.episodesRecyclerView.adapter =
|
binding.episodesRecyclerView.adapter =
|
||||||
EpisodeListAdapter(EpisodeListAdapter.OnClickListener { episode ->
|
EpisodeListAdapter(
|
||||||
|
EpisodeListAdapter.OnClickListener { episode ->
|
||||||
navigateToEpisodeBottomSheetFragment(episode)
|
navigateToEpisodeBottomSheetFragment(episode)
|
||||||
}, args.seriesId, args.seriesName, args.seasonId, args.seasonName)
|
},
|
||||||
|
args.seriesId, args.seriesName, args.seasonId, args.seasonName
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindUiStateNormal(uiState: SeasonViewModel.UiState.Normal) {
|
private fun bindUiStateNormal(uiState: SeasonViewModel.UiState.Normal) {
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
package dev.jdtech.jellyfin.fragments
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
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.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import dev.jdtech.jellyfin.adapters.ServerGridAdapter
|
||||||
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.viewmodels.ServerSelectViewModel
|
import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ class ServerSelectFragment : Fragment() {
|
||||||
private val viewModel: ServerSelectViewModel by viewModels()
|
private val viewModel: ServerSelectViewModel by viewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = FragmentServerSelectBinding.inflate(inflater)
|
binding = FragmentServerSelectBinding.inflate(inflater)
|
||||||
|
@ -34,15 +35,18 @@ class ServerSelectFragment : Fragment() {
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
|
|
||||||
binding.serversRecyclerView.adapter =
|
binding.serversRecyclerView.adapter =
|
||||||
ServerGridAdapter(ServerGridAdapter.OnClickListener { server ->
|
ServerGridAdapter(
|
||||||
|
ServerGridAdapter.OnClickListener { server ->
|
||||||
viewModel.connectToServer(server)
|
viewModel.connectToServer(server)
|
||||||
}, ServerGridAdapter.OnLongClickListener { server ->
|
},
|
||||||
|
ServerGridAdapter.OnLongClickListener { server ->
|
||||||
DeleteServerDialogFragment(viewModel, server).show(
|
DeleteServerDialogFragment(viewModel, server).show(
|
||||||
parentFragmentManager,
|
parentFragmentManager,
|
||||||
"deleteServer"
|
"deleteServer"
|
||||||
)
|
)
|
||||||
true
|
true
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
binding.buttonAddServer.setOnClickListener {
|
binding.buttonAddServer.setOnClickListener {
|
||||||
navigateToAddServerFragment()
|
navigateToAddServerFragment()
|
||||||
|
|
|
@ -3,9 +3,9 @@ package dev.jdtech.jellyfin.models
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@Entity(tableName = "downloads")
|
@Entity(tableName = "downloads")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package dev.jdtech.jellyfin.models
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
import java.util.*
|
import java.util.UUID
|
||||||
|
|
||||||
data class DownloadSection(
|
data class DownloadSection(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package dev.jdtech.jellyfin.models
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class DownloadSeriesMetadata(
|
data class DownloadSeriesMetadata(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.jdtech.jellyfin.models
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
data class HomeSection(
|
data class HomeSection(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package dev.jdtech.jellyfin.models
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class PlayerItem(
|
data class PlayerItem(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package dev.jdtech.jellyfin.models
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
data class View(
|
data class View(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package dev.jdtech.jellyfin.mpv
|
package dev.jdtech.jellyfin.mpv
|
||||||
|
|
||||||
import `is`.xyz.mpv.MPVLib
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.AssetManager
|
import android.content.res.AssetManager
|
||||||
|
@ -13,24 +12,39 @@ import android.view.SurfaceHolder
|
||||||
import android.view.SurfaceView
|
import android.view.SurfaceView
|
||||||
import android.view.TextureView
|
import android.view.TextureView
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.BasePlayer
|
||||||
|
import com.google.android.exoplayer2.C
|
||||||
|
import com.google.android.exoplayer2.DeviceInfo
|
||||||
|
import com.google.android.exoplayer2.ExoPlaybackException
|
||||||
|
import com.google.android.exoplayer2.Format
|
||||||
|
import com.google.android.exoplayer2.MediaItem
|
||||||
|
import com.google.android.exoplayer2.MediaMetadata
|
||||||
|
import com.google.android.exoplayer2.PlaybackParameters
|
||||||
|
import com.google.android.exoplayer2.Player
|
||||||
import com.google.android.exoplayer2.Player.Commands
|
import com.google.android.exoplayer2.Player.Commands
|
||||||
|
import com.google.android.exoplayer2.Timeline
|
||||||
|
import com.google.android.exoplayer2.Tracks
|
||||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||||
import com.google.android.exoplayer2.source.TrackGroup
|
import com.google.android.exoplayer2.source.TrackGroup
|
||||||
import com.google.android.exoplayer2.text.Cue
|
import com.google.android.exoplayer2.text.Cue
|
||||||
import com.google.android.exoplayer2.text.CueGroup
|
import com.google.android.exoplayer2.text.CueGroup
|
||||||
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters
|
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters
|
||||||
import com.google.android.exoplayer2.util.*
|
import com.google.android.exoplayer2.util.Clock
|
||||||
|
import com.google.android.exoplayer2.util.FlagSet
|
||||||
|
import com.google.android.exoplayer2.util.ListenerSet
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes
|
||||||
|
import com.google.android.exoplayer2.util.Util
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
import com.google.android.exoplayer2.video.VideoSize
|
||||||
import dev.jdtech.jellyfin.utils.AppPreferences
|
import dev.jdtech.jellyfin.utils.AppPreferences
|
||||||
|
import `is`.xyz.mpv.MPVLib
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.util.concurrent.CopyOnWriteArraySet
|
|
||||||
|
|
||||||
@Suppress("SpellCheckingInspection")
|
@Suppress("SpellCheckingInspection")
|
||||||
class MPVPlayer(
|
class MPVPlayer(
|
||||||
|
@ -627,16 +641,16 @@ class MPVPlayer(
|
||||||
.addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPreviousMediaItem() && !isPlayingAd)
|
.addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPreviousMediaItem() && !isPlayingAd)
|
||||||
.addIf(
|
.addIf(
|
||||||
COMMAND_SEEK_TO_PREVIOUS,
|
COMMAND_SEEK_TO_PREVIOUS,
|
||||||
!currentTimeline.isEmpty
|
!currentTimeline.isEmpty &&
|
||||||
&& (hasPreviousMediaItem() || !isCurrentMediaItemLive || isCurrentMediaItemSeekable)
|
(hasPreviousMediaItem() || !isCurrentMediaItemLive || isCurrentMediaItemSeekable) &&
|
||||||
&& !isPlayingAd
|
!isPlayingAd
|
||||||
)
|
)
|
||||||
.addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNextMediaItem() && !isPlayingAd)
|
.addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNextMediaItem() && !isPlayingAd)
|
||||||
.addIf(
|
.addIf(
|
||||||
COMMAND_SEEK_TO_NEXT,
|
COMMAND_SEEK_TO_NEXT,
|
||||||
!currentTimeline.isEmpty
|
!currentTimeline.isEmpty &&
|
||||||
&& (hasNextMediaItem() || (isCurrentMediaItemLive && isCurrentMediaItemDynamic))
|
(hasNextMediaItem() || (isCurrentMediaItemLive && isCurrentMediaItemDynamic)) &&
|
||||||
&& !isPlayingAd
|
!isPlayingAd
|
||||||
)
|
)
|
||||||
.addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd)
|
.addIf(COMMAND_SEEK_TO_MEDIA_ITEM, !isPlayingAd)
|
||||||
.addIf(COMMAND_SEEK_BACK, isCurrentMediaItemSeekable && !isPlayingAd)
|
.addIf(COMMAND_SEEK_BACK, isCurrentMediaItemSeekable && !isPlayingAd)
|
||||||
|
@ -1212,8 +1226,7 @@ class MPVPlayer(
|
||||||
MPVLib.setOptionString("panscan", "1")
|
MPVLib.setOptionString("panscan", "1")
|
||||||
MPVLib.setOptionString("sub-use-margins", "yes")
|
MPVLib.setOptionString("sub-use-margins", "yes")
|
||||||
MPVLib.setOptionString("sub-ass-force-margins", "yes")
|
MPVLib.setOptionString("sub-ass-force-margins", "yes")
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
MPVLib.setOptionString("panscan", "0")
|
MPVLib.setOptionString("panscan", "0")
|
||||||
MPVLib.setOptionString("sub-use-margins", "no")
|
MPVLib.setOptionString("sub-use-margins", "no")
|
||||||
MPVLib.setOptionString("sub-ass-force-margins", "no")
|
MPVLib.setOptionString("sub-ass-force-margins", "no")
|
||||||
|
@ -1409,7 +1422,8 @@ class MPVPlayer(
|
||||||
IntArray(this.length) { C.FORMAT_HANDLED },
|
IntArray(this.length) { C.FORMAT_HANDLED },
|
||||||
BooleanArray(this.length) { it == indexCurrentVideo }
|
BooleanArray(this.length) { it == indexCurrentVideo }
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (trackListAudio.isNotEmpty()) {
|
if (trackListAudio.isNotEmpty()) {
|
||||||
trackGroups.add(
|
trackGroups.add(
|
||||||
|
@ -1420,7 +1434,8 @@ class MPVPlayer(
|
||||||
IntArray(this.length) { C.FORMAT_HANDLED },
|
IntArray(this.length) { C.FORMAT_HANDLED },
|
||||||
BooleanArray(this.length) { it == indexCurrentAudio }
|
BooleanArray(this.length) { it == indexCurrentAudio }
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (trackListText.isNotEmpty()) {
|
if (trackListText.isNotEmpty()) {
|
||||||
trackGroups.add(
|
trackGroups.add(
|
||||||
|
@ -1431,7 +1446,8 @@ class MPVPlayer(
|
||||||
IntArray(this.length) { C.FORMAT_HANDLED },
|
IntArray(this.length) { C.FORMAT_HANDLED },
|
||||||
BooleanArray(this.length) { it == indexCurrentText }
|
BooleanArray(this.length) { it == indexCurrentText }
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (trackGroups.isNotEmpty()) {
|
if (trackGroups.isNotEmpty()) {
|
||||||
tracks = Tracks(trackGroups)
|
tracks = Tracks(trackGroups)
|
||||||
|
|
|
@ -4,11 +4,11 @@ import androidx.paging.PagingSource
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.utils.SortBy
|
import dev.jdtech.jellyfin.utils.SortBy
|
||||||
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.SortOrder
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class ItemsPagingSource(
|
class ItemsPagingSource(
|
||||||
private val jellyfinApi: JellyfinApi,
|
private val jellyfinApi: JellyfinApi,
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
package dev.jdtech.jellyfin.repository
|
package dev.jdtech.jellyfin.repository
|
||||||
|
|
||||||
|
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import dev.jdtech.jellyfin.utils.SortBy
|
import dev.jdtech.jellyfin.utils.SortBy
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.jellyfin.sdk.model.api.*
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import java.util.*
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
import org.jellyfin.sdk.model.api.ItemFields
|
||||||
|
import org.jellyfin.sdk.model.api.MediaSourceInfo
|
||||||
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
|
|
||||||
interface JellyfinRepository {
|
interface JellyfinRepository {
|
||||||
suspend fun getUserViews(): List<BaseItemDto>
|
suspend fun getUserViews(): List<BaseItemDto>
|
||||||
|
|
|
@ -5,12 +5,25 @@ import androidx.paging.PagingConfig
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import dev.jdtech.jellyfin.api.JellyfinApi
|
import dev.jdtech.jellyfin.api.JellyfinApi
|
||||||
import dev.jdtech.jellyfin.utils.SortBy
|
import dev.jdtech.jellyfin.utils.SortBy
|
||||||
|
import java.util.UUID
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.model.api.*
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
import org.jellyfin.sdk.model.api.DeviceOptionsDto
|
||||||
|
import org.jellyfin.sdk.model.api.DeviceProfile
|
||||||
|
import org.jellyfin.sdk.model.api.DirectPlayProfile
|
||||||
|
import org.jellyfin.sdk.model.api.DlnaProfileType
|
||||||
|
import org.jellyfin.sdk.model.api.GeneralCommandType
|
||||||
|
import org.jellyfin.sdk.model.api.ItemFields
|
||||||
|
import org.jellyfin.sdk.model.api.ItemFilter
|
||||||
|
import org.jellyfin.sdk.model.api.MediaSourceInfo
|
||||||
|
import org.jellyfin.sdk.model.api.PlaybackInfoDto
|
||||||
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
|
import org.jellyfin.sdk.model.api.SubtitleDeliveryMethod
|
||||||
|
import org.jellyfin.sdk.model.api.SubtitleProfile
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRepository {
|
class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRepository {
|
||||||
override suspend fun getUserViews(): List<BaseItemDto> = withContext(Dispatchers.IO) {
|
override suspend fun getUserViews(): List<BaseItemDto> = withContext(Dispatchers.IO) {
|
||||||
|
@ -150,7 +163,8 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
|
||||||
override suspend fun getMediaSources(itemId: UUID): List<MediaSourceInfo> =
|
override suspend fun getMediaSources(itemId: UUID): List<MediaSourceInfo> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
jellyfinApi.mediaInfoApi.getPostedPlaybackInfo(
|
jellyfinApi.mediaInfoApi.getPostedPlaybackInfo(
|
||||||
itemId, PlaybackInfoDto(
|
itemId,
|
||||||
|
PlaybackInfoDto(
|
||||||
userId = jellyfinApi.userId!!,
|
userId = jellyfinApi.userId!!,
|
||||||
deviceProfile = DeviceProfile(
|
deviceProfile = DeviceProfile(
|
||||||
name = "Direct play all",
|
name = "Direct play all",
|
||||||
|
@ -220,7 +234,8 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
|
||||||
GeneralCommandType.PLAY_STATE,
|
GeneralCommandType.PLAY_STATE,
|
||||||
GeneralCommandType.PLAY_NEXT,
|
GeneralCommandType.PLAY_NEXT,
|
||||||
GeneralCommandType.PLAY_MEDIA_SOURCE
|
GeneralCommandType.PLAY_MEDIA_SOURCE
|
||||||
), supportsMediaControl = true
|
),
|
||||||
|
supportsMediaControl = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,13 +70,15 @@ internal class TvPlayerActivity : BasePlayerActivity() {
|
||||||
when {
|
when {
|
||||||
viewModel.player.isPlaying -> {
|
viewModel.player.isPlaying -> {
|
||||||
viewModel.player.pause()
|
viewModel.player.pause()
|
||||||
setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
|
setImageDrawable(
|
||||||
|
ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
viewModel.player.isLoading -> Unit
|
viewModel.player.isLoading -> Unit
|
||||||
else -> {
|
else -> {
|
||||||
viewModel.player.play()
|
viewModel.player.play()
|
||||||
setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
|
setImageDrawable(
|
||||||
|
ResourcesCompat.getDrawable(resources, R.drawable.ic_play, theme)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ import android.widget.ImageButton
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.leanback.app.BrowseSupportFragment
|
import androidx.leanback.app.BrowseSupportFragment
|
||||||
import androidx.leanback.widget.*
|
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.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
@ -146,15 +150,21 @@ internal class HomeFragment : BrowseSupportFragment() {
|
||||||
adapterMap[name]?.setItems(items, diffCallback)
|
adapterMap[name]?.setItems(items, diffCallback)
|
||||||
} else {
|
} else {
|
||||||
adapterMap[name] = when (this) {
|
adapterMap[name] = when (this) {
|
||||||
is HomeItem.Libraries -> ArrayObjectAdapter(LibaryItemPresenter { item ->
|
is HomeItem.Libraries -> ArrayObjectAdapter(
|
||||||
|
LibaryItemPresenter { item ->
|
||||||
navigateToLibraryFragment(item)
|
navigateToLibraryFragment(item)
|
||||||
}).apply { setItems(items, diffCallback) }
|
}
|
||||||
is HomeItem.Section -> ArrayObjectAdapter(DynamicMediaItemPresenter { item ->
|
).apply { setItems(items, diffCallback) }
|
||||||
|
is HomeItem.Section -> ArrayObjectAdapter(
|
||||||
|
DynamicMediaItemPresenter { item ->
|
||||||
navigateToMediaDetailFragment(item)
|
navigateToMediaDetailFragment(item)
|
||||||
}).apply { setItems(items, diffCallback) }
|
}
|
||||||
is HomeItem.ViewItem -> ArrayObjectAdapter(MediaItemPresenter { item ->
|
).apply { setItems(items, diffCallback) }
|
||||||
|
is HomeItem.ViewItem -> ArrayObjectAdapter(
|
||||||
|
MediaItemPresenter { item ->
|
||||||
navigateToMediaDetailFragment(item)
|
navigateToMediaDetailFragment(item)
|
||||||
}).apply { setItems(items, diffCallback) }
|
}
|
||||||
|
).apply { setItems(items, diffCallback) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,8 @@ internal class MediaDetailFragment : Fragment() {
|
||||||
|
|
||||||
val seasonsAdapter = ViewItemListAdapter(
|
val seasonsAdapter = ViewItemListAdapter(
|
||||||
fixedWidth = true,
|
fixedWidth = true,
|
||||||
onClickListener = ViewItemListAdapter.OnClickListener {})
|
onClickListener = ViewItemListAdapter.OnClickListener {}
|
||||||
|
)
|
||||||
|
|
||||||
binding.seasonsRow.gridView.adapter = seasonsAdapter
|
binding.seasonsRow.gridView.adapter = seasonsAdapter
|
||||||
binding.seasonsRow.gridView.verticalSpacing = 25
|
binding.seasonsRow.gridView.verticalSpacing = 25
|
||||||
|
|
|
@ -11,12 +11,12 @@ import dev.jdtech.jellyfin.models.DownloadItem
|
||||||
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
import java.io.File
|
||||||
|
import java.util.UUID
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.UserItemDataDto
|
import org.jellyfin.sdk.model.api.UserItemDataDto
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
var defaultStorage: File? = null
|
var defaultStorage: File? = null
|
||||||
|
|
||||||
|
@ -156,7 +156,6 @@ fun deleteDownloadedEpisode(downloadDatabase: DownloadDatabaseDao, itemId: UUID)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun postDownloadPlaybackProgress(
|
fun postDownloadPlaybackProgress(
|
||||||
|
@ -277,6 +276,5 @@ suspend fun syncPlaybackProgress(
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,8 +14,8 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||||
import dev.jdtech.jellyfin.PlayerActivity
|
import dev.jdtech.jellyfin.PlayerActivity
|
||||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||||
import timber.log.Timber
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class PlayerGestureHelper(
|
class PlayerGestureHelper(
|
||||||
private val appPreferences: AppPreferences,
|
private val appPreferences: AppPreferences,
|
||||||
|
@ -45,7 +45,9 @@ class PlayerGestureHelper(
|
||||||
|
|
||||||
private var lastScaleEvent: Long = 0
|
private var lastScaleEvent: Long = 0
|
||||||
|
|
||||||
private val tapGestureDetector = GestureDetector(playerView.context, object : GestureDetector.SimpleOnGestureListener() {
|
private val tapGestureDetector = GestureDetector(
|
||||||
|
playerView.context,
|
||||||
|
object : GestureDetector.SimpleOnGestureListener() {
|
||||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||||
playerView.apply {
|
playerView.apply {
|
||||||
if (!isControllerFullyVisible) showController() else hideController()
|
if (!isControllerFullyVisible) showController() else hideController()
|
||||||
|
@ -60,15 +62,17 @@ class PlayerGestureHelper(
|
||||||
|
|
||||||
if (e.x.toInt() > viewCenterX) {
|
if (e.x.toInt() > viewCenterX) {
|
||||||
playerView.player?.seekTo(currentPos + appPreferences.playerSeekForwardIncrement)
|
playerView.player?.seekTo(currentPos + appPreferences.playerSeekForwardIncrement)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
playerView.player?.seekTo((currentPos - appPreferences.playerSeekBackIncrement).coerceAtLeast(0))
|
playerView.player?.seekTo((currentPos - appPreferences.playerSeekBackIncrement).coerceAtLeast(0))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
private val gestureDetector = GestureDetector(playerView.context, object : GestureDetector.SimpleOnGestureListener() {
|
private val gestureDetector = GestureDetector(
|
||||||
|
playerView.context,
|
||||||
|
object : GestureDetector.SimpleOnGestureListener() {
|
||||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||||
playerView.apply {
|
playerView.apply {
|
||||||
if (!isControllerFullyVisible) showController() else hideController()
|
if (!isControllerFullyVisible) showController() else hideController()
|
||||||
|
@ -83,15 +87,19 @@ class PlayerGestureHelper(
|
||||||
|
|
||||||
if (e.x.toInt() > viewCenterX) {
|
if (e.x.toInt() > viewCenterX) {
|
||||||
playerView.player?.seekTo(currentPos + appPreferences.playerSeekForwardIncrement)
|
playerView.player?.seekTo(currentPos + appPreferences.playerSeekForwardIncrement)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
playerView.player?.seekTo((currentPos - appPreferences.playerSeekBackIncrement).coerceAtLeast(0))
|
playerView.player?.seekTo((currentPos - appPreferences.playerSeekBackIncrement).coerceAtLeast(0))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onScroll(firstEvent: MotionEvent, currentEvent: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
|
override fun onScroll(
|
||||||
|
firstEvent: MotionEvent,
|
||||||
|
currentEvent: MotionEvent,
|
||||||
|
distanceX: Float,
|
||||||
|
distanceY: Float
|
||||||
|
): Boolean {
|
||||||
if (firstEvent.y < playerView.resources.dip(Constants.GESTURE_EXCLUSION_AREA_TOP))
|
if (firstEvent.y < playerView.resources.dip(Constants.GESTURE_EXCLUSION_AREA_TOP))
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
@ -100,7 +108,8 @@ class PlayerGestureHelper(
|
||||||
return if ((abs(currentEvent.x - firstEvent.x) > 50 || swipeGestureProgressOpen) &&
|
return if ((abs(currentEvent.x - firstEvent.x) > 50 || swipeGestureProgressOpen) &&
|
||||||
!swipeGestureBrightnessOpen &&
|
!swipeGestureBrightnessOpen &&
|
||||||
!swipeGestureVolumeOpen &&
|
!swipeGestureVolumeOpen &&
|
||||||
(SystemClock.elapsedRealtime() - lastScaleEvent) > 200) {
|
(SystemClock.elapsedRealtime() - lastScaleEvent) > 200
|
||||||
|
) {
|
||||||
val currentPos = playerView.player?.currentPosition ?: 0
|
val currentPos = playerView.player?.currentPosition ?: 0
|
||||||
val vidDuration = (playerView.player?.duration ?: 0).coerceAtLeast(0)
|
val vidDuration = (playerView.player?.duration ?: 0).coerceAtLeast(0)
|
||||||
|
|
||||||
|
@ -171,7 +180,8 @@ class PlayerGestureHelper(
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
private val hideGestureVolumeIndicatorOverlayAction = Runnable {
|
private val hideGestureVolumeIndicatorOverlayAction = Runnable {
|
||||||
activity.binding.gestureVolumeLayout.visibility = View.GONE
|
activity.binding.gestureVolumeLayout.visibility = View.GONE
|
||||||
|
@ -191,7 +201,9 @@ class PlayerGestureHelper(
|
||||||
/**
|
/**
|
||||||
* Handles scale/zoom gesture
|
* Handles scale/zoom gesture
|
||||||
*/
|
*/
|
||||||
private val zoomGestureDetector = ScaleGestureDetector(playerView.context, object : ScaleGestureDetector.OnScaleGestureListener {
|
private val zoomGestureDetector = ScaleGestureDetector(
|
||||||
|
playerView.context,
|
||||||
|
object : ScaleGestureDetector.OnScaleGestureListener {
|
||||||
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true
|
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true
|
||||||
|
|
||||||
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
override fun onScale(detector: ScaleGestureDetector): Boolean {
|
||||||
|
@ -205,13 +217,13 @@ class PlayerGestureHelper(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onScaleEnd(detector: ScaleGestureDetector) = Unit
|
override fun onScaleEnd(detector: ScaleGestureDetector) = Unit
|
||||||
}).apply { isQuickScaleEnabled = false }
|
}
|
||||||
|
).apply { isQuickScaleEnabled = false }
|
||||||
|
|
||||||
private fun updateZoomMode(enabled: Boolean) {
|
private fun updateZoomMode(enabled: Boolean) {
|
||||||
if (playerView.player is MPVPlayer) {
|
if (playerView.player is MPVPlayer) {
|
||||||
(playerView.player as MPVPlayer).updateZoomMode(enabled)
|
(playerView.player as MPVPlayer).updateZoomMode(enabled)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
playerView.resizeMode = if (enabled) AspectRatioFrameLayout.RESIZE_MODE_ZOOM else AspectRatioFrameLayout.RESIZE_MODE_FIT
|
playerView.resizeMode = if (enabled) AspectRatioFrameLayout.RESIZE_MODE_ZOOM else AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,21 @@ 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 dev.jdtech.jellyfin.models.DiscoveredServer
|
import dev.jdtech.jellyfin.models.DiscoveredServer
|
||||||
import kotlinx.coroutines.*
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.CancellationException
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.discovery.RecommendedServerInfo
|
import org.jellyfin.sdk.discovery.RecommendedServerInfo
|
||||||
import org.jellyfin.sdk.discovery.RecommendedServerInfoScore
|
import org.jellyfin.sdk.discovery.RecommendedServerInfoScore
|
||||||
import org.jellyfin.sdk.discovery.RecommendedServerIssue
|
import org.jellyfin.sdk.discovery.RecommendedServerIssue
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class AddServerViewModel
|
class AddServerViewModel
|
||||||
|
@ -54,11 +62,13 @@ constructor(
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
val servers = jellyfinApi.jellyfin.discovery.discoverLocalServers()
|
val servers = jellyfinApi.jellyfin.discovery.discoverLocalServers()
|
||||||
servers.collect { serverDiscoveryInfo ->
|
servers.collect { serverDiscoveryInfo ->
|
||||||
discoveredServers.add(DiscoveredServer(
|
discoveredServers.add(
|
||||||
|
DiscoveredServer(
|
||||||
serverDiscoveryInfo.id,
|
serverDiscoveryInfo.id,
|
||||||
serverDiscoveryInfo.name,
|
serverDiscoveryInfo.name,
|
||||||
serverDiscoveryInfo.address
|
serverDiscoveryInfo.address
|
||||||
))
|
)
|
||||||
|
)
|
||||||
_discoveredServersState.emit(
|
_discoveredServersState.emit(
|
||||||
DiscoveredServersState.Servers(ArrayList(discoveredServers))
|
DiscoveredServersState.Servers(ArrayList(discoveredServers))
|
||||||
)
|
)
|
||||||
|
@ -131,7 +141,6 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (_: CancellationException) {
|
} catch (_: CancellationException) {
|
||||||
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
_uiState.emit(
|
_uiState.emit(
|
||||||
UiState.Error(
|
UiState.Error(
|
||||||
|
|
|
@ -5,10 +5,10 @@ import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.adapters.DownloadEpisodeItem
|
import dev.jdtech.jellyfin.adapters.DownloadEpisodeItem
|
||||||
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class DownloadSeriesViewModel
|
class DownloadSeriesViewModel
|
||||||
|
@ -36,8 +36,11 @@ constructor() : ViewModel() {
|
||||||
|
|
||||||
private fun getEpisodes(seriesMetadata: DownloadSeriesMetadata): List<DownloadEpisodeItem> {
|
private fun getEpisodes(seriesMetadata: DownloadSeriesMetadata): List<DownloadEpisodeItem> {
|
||||||
val episodes = seriesMetadata.episodes
|
val episodes = seriesMetadata.episodes
|
||||||
return listOf(DownloadEpisodeItem.Header) + episodes.sortedWith(compareBy(
|
return listOf(DownloadEpisodeItem.Header) + episodes.sortedWith(
|
||||||
|
compareBy(
|
||||||
{ it.item!!.parentIndexNumber },
|
{ it.item!!.parentIndexNumber },
|
||||||
{ it.item!!.indexNumber })).map { DownloadEpisodeItem.Episode(it) }
|
{ it.item!!.indexNumber }
|
||||||
|
)
|
||||||
|
).map { DownloadEpisodeItem.Episode(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.DownloadSection
|
import dev.jdtech.jellyfin.models.DownloadSection
|
||||||
|
@ -9,13 +10,14 @@ import dev.jdtech.jellyfin.models.DownloadSeriesMetadata
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.utils.checkDownloadStatus
|
import dev.jdtech.jellyfin.utils.checkDownloadStatus
|
||||||
import dev.jdtech.jellyfin.utils.loadDownloadedEpisodes
|
import dev.jdtech.jellyfin.utils.loadDownloadedEpisodes
|
||||||
import kotlinx.coroutines.*
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class DownloadViewModel
|
class DownloadViewModel
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.*
|
import dev.jdtech.jellyfin.utils.canRetryDownload
|
||||||
|
import dev.jdtech.jellyfin.utils.deleteDownloadedEpisode
|
||||||
|
import dev.jdtech.jellyfin.utils.downloadMetadataToBaseItemDto
|
||||||
|
import dev.jdtech.jellyfin.utils.isItemAvailable
|
||||||
|
import dev.jdtech.jellyfin.utils.isItemDownloaded
|
||||||
|
import dev.jdtech.jellyfin.utils.requestDownload
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -14,11 +25,6 @@ import org.jellyfin.sdk.api.client.exception.ApiClientException
|
||||||
import org.jellyfin.sdk.model.DateTime
|
import org.jellyfin.sdk.model.DateTime
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.text.DateFormat
|
|
||||||
import java.time.ZoneOffset
|
|
||||||
import java.util.Date
|
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class EpisodeBottomSheetViewModel
|
class EpisodeBottomSheetViewModel
|
||||||
|
|
|
@ -9,13 +9,13 @@ import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.models.FavoriteSection
|
import dev.jdtech.jellyfin.models.FavoriteSection
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.Constants
|
import dev.jdtech.jellyfin.utils.Constants
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class FavoriteViewModel
|
class FavoriteViewModel
|
||||||
|
@ -56,7 +56,8 @@ constructor(
|
||||||
FavoriteSection(
|
FavoriteSection(
|
||||||
Constants.FAVORITE_TYPE_MOVIES,
|
Constants.FAVORITE_TYPE_MOVIES,
|
||||||
resources.getString(R.string.movies_label),
|
resources.getString(R.string.movies_label),
|
||||||
items.filter { it.type == BaseItemKind.MOVIE }).let {
|
items.filter { it.type == BaseItemKind.MOVIE }
|
||||||
|
).let {
|
||||||
if (it.items.isNotEmpty()) favoriteSections.add(
|
if (it.items.isNotEmpty()) favoriteSections.add(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
@ -64,7 +65,8 @@ constructor(
|
||||||
FavoriteSection(
|
FavoriteSection(
|
||||||
Constants.FAVORITE_TYPE_SHOWS,
|
Constants.FAVORITE_TYPE_SHOWS,
|
||||||
resources.getString(R.string.shows_label),
|
resources.getString(R.string.shows_label),
|
||||||
items.filter { it.type == BaseItemKind.SERIES }).let {
|
items.filter { it.type == BaseItemKind.SERIES }
|
||||||
|
).let {
|
||||||
if (it.items.isNotEmpty()) favoriteSections.add(
|
if (it.items.isNotEmpty()) favoriteSections.add(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
@ -72,7 +74,8 @@ constructor(
|
||||||
FavoriteSection(
|
FavoriteSection(
|
||||||
Constants.FAVORITE_TYPE_EPISODES,
|
Constants.FAVORITE_TYPE_EPISODES,
|
||||||
resources.getString(R.string.episodes_label),
|
resources.getString(R.string.episodes_label),
|
||||||
items.filter { it.type == BaseItemKind.EPISODE }).let {
|
items.filter { it.type == BaseItemKind.EPISODE }
|
||||||
|
).let {
|
||||||
if (it.items.isNotEmpty()) favoriteSections.add(
|
if (it.items.isNotEmpty()) favoriteSections.add(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,13 +14,13 @@ import dev.jdtech.jellyfin.models.HomeSection
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.syncPlaybackProgress
|
import dev.jdtech.jellyfin.utils.syncPlaybackProgress
|
||||||
import dev.jdtech.jellyfin.utils.toView
|
import dev.jdtech.jellyfin.utils.toView
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class HomeViewModel @Inject internal constructor(
|
class HomeViewModel @Inject internal constructor(
|
||||||
|
@ -117,5 +117,3 @@ class HomeViewModel @Inject internal constructor(
|
||||||
.map { (view, latest) -> view.toView().apply { items = latest } }
|
.map { (view, latest) -> view.toView().apply { items = latest } }
|
||||||
.map { ViewItem(it) }
|
.map { ViewItem(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.SortBy
|
import dev.jdtech.jellyfin.utils.SortBy
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
@ -13,8 +16,6 @@ import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.SortOrder
|
import org.jellyfin.sdk.model.api.SortOrder
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LibraryViewModel
|
class LibraryViewModel
|
||||||
|
|
|
@ -2,7 +2,8 @@ package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.BaseApplication
|
import dev.jdtech.jellyfin.BaseApplication
|
||||||
import dev.jdtech.jellyfin.R
|
import dev.jdtech.jellyfin.R
|
||||||
|
@ -10,6 +11,8 @@ 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 dev.jdtech.jellyfin.models.User
|
import dev.jdtech.jellyfin.models.User
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.Exception
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
@ -18,8 +21,6 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.model.api.AuthenticateUserByName
|
import org.jellyfin.sdk.model.api.AuthenticateUserByName
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.Exception
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LoginViewModel
|
class LoginViewModel
|
||||||
|
|
|
@ -7,7 +7,14 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
import dev.jdtech.jellyfin.models.PlayerItem
|
import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.*
|
import dev.jdtech.jellyfin.utils.canRetryDownload
|
||||||
|
import dev.jdtech.jellyfin.utils.deleteDownloadedEpisode
|
||||||
|
import dev.jdtech.jellyfin.utils.downloadMetadataToBaseItemDto
|
||||||
|
import dev.jdtech.jellyfin.utils.isItemAvailable
|
||||||
|
import dev.jdtech.jellyfin.utils.isItemDownloaded
|
||||||
|
import dev.jdtech.jellyfin.utils.requestDownload
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
@ -18,8 +25,6 @@ import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.BaseItemPerson
|
import org.jellyfin.sdk.model.api.BaseItemPerson
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MediaInfoViewModel
|
class MediaInfoViewModel
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.models.CollectionType
|
import dev.jdtech.jellyfin.models.CollectionType
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MediaViewModel
|
class MediaViewModel
|
||||||
|
|
|
@ -4,13 +4,13 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class PersonDetailViewModel @Inject internal constructor(
|
internal class PersonDetailViewModel @Inject internal constructor(
|
||||||
|
|
|
@ -8,7 +8,11 @@ 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 androidx.preference.PreferenceManager
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.C
|
||||||
|
import com.google.android.exoplayer2.DefaultRenderersFactory
|
||||||
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
|
import com.google.android.exoplayer2.MediaItem
|
||||||
|
import com.google.android.exoplayer2.Player
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
import dev.jdtech.jellyfin.database.DownloadDatabaseDao
|
||||||
|
@ -18,11 +22,11 @@ import dev.jdtech.jellyfin.mpv.TrackType
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.AppPreferences
|
import dev.jdtech.jellyfin.utils.AppPreferences
|
||||||
import dev.jdtech.jellyfin.utils.postDownloadPlaybackProgress
|
import dev.jdtech.jellyfin.utils.postDownloadPlaybackProgress
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PlayerActivityViewModel
|
class PlayerActivityViewModel
|
||||||
|
|
|
@ -14,6 +14,7 @@ import dev.jdtech.jellyfin.models.PlayerItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.getDownloadPlayerItem
|
import dev.jdtech.jellyfin.utils.getDownloadPlayerItem
|
||||||
import dev.jdtech.jellyfin.utils.isItemAvailable
|
import dev.jdtech.jellyfin.utils.isItemAvailable
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.channels.BufferOverflow
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -24,7 +25,6 @@ import org.jellyfin.sdk.model.api.LocationType.VIRTUAL
|
||||||
import org.jellyfin.sdk.model.api.MediaProtocol
|
import org.jellyfin.sdk.model.api.MediaProtocol
|
||||||
import org.jellyfin.sdk.model.api.MediaStreamType
|
import org.jellyfin.sdk.model.api.MediaStreamType
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class PlayerViewModel @Inject internal constructor(
|
class PlayerViewModel @Inject internal constructor(
|
||||||
|
|
|
@ -9,13 +9,13 @@ import dev.jdtech.jellyfin.R
|
||||||
import dev.jdtech.jellyfin.models.FavoriteSection
|
import dev.jdtech.jellyfin.models.FavoriteSection
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
import dev.jdtech.jellyfin.utils.Constants
|
import dev.jdtech.jellyfin.utils.Constants
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SearchResultViewModel
|
class SearchResultViewModel
|
||||||
|
@ -52,7 +52,8 @@ constructor(
|
||||||
FavoriteSection(
|
FavoriteSection(
|
||||||
Constants.FAVORITE_TYPE_MOVIES,
|
Constants.FAVORITE_TYPE_MOVIES,
|
||||||
resources.getString(R.string.movies_label),
|
resources.getString(R.string.movies_label),
|
||||||
items.filter { it.type == BaseItemKind.MOVIE }).let {
|
items.filter { it.type == BaseItemKind.MOVIE }
|
||||||
|
).let {
|
||||||
if (it.items.isNotEmpty()) sections.add(
|
if (it.items.isNotEmpty()) sections.add(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
@ -60,7 +61,8 @@ constructor(
|
||||||
FavoriteSection(
|
FavoriteSection(
|
||||||
Constants.FAVORITE_TYPE_SHOWS,
|
Constants.FAVORITE_TYPE_SHOWS,
|
||||||
resources.getString(R.string.shows_label),
|
resources.getString(R.string.shows_label),
|
||||||
items.filter { it.type == BaseItemKind.SERIES }).let {
|
items.filter { it.type == BaseItemKind.SERIES }
|
||||||
|
).let {
|
||||||
if (it.items.isNotEmpty()) sections.add(
|
if (it.items.isNotEmpty()) sections.add(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
@ -68,7 +70,8 @@ constructor(
|
||||||
FavoriteSection(
|
FavoriteSection(
|
||||||
Constants.FAVORITE_TYPE_EPISODES,
|
Constants.FAVORITE_TYPE_EPISODES,
|
||||||
resources.getString(R.string.episodes_label),
|
resources.getString(R.string.episodes_label),
|
||||||
items.filter { it.type == BaseItemKind.EPISODE }).let {
|
items.filter { it.type == BaseItemKind.EPISODE }
|
||||||
|
).let {
|
||||||
if (it.items.isNotEmpty()) sections.add(
|
if (it.items.isNotEmpty()) sections.add(
|
||||||
it
|
it
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
package dev.jdtech.jellyfin.viewmodels
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.adapters.EpisodeItem
|
import dev.jdtech.jellyfin.adapters.EpisodeItem
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.jellyfin.sdk.model.api.ItemFields
|
import org.jellyfin.sdk.model.api.ItemFields
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SeasonViewModel
|
class SeasonViewModel
|
||||||
|
|
|
@ -7,12 +7,12 @@ 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
|
||||||
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
import dev.jdtech.jellyfin.database.ServerDatabaseDao
|
||||||
|
import java.util.UUID
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ServerSelectViewModel
|
class ServerSelectViewModel
|
||||||
|
|
|
@ -4,9 +4,9 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
internal class SettingsDeviceViewModel
|
internal class SettingsDeviceViewModel
|
||||||
|
|
Loading…
Reference in a new issue