Add favorites fragment + switch settings for favorites on bottom nav
This commit is contained in:
parent
edb0b15694
commit
c0ab909114
13 changed files with 392 additions and 4 deletions
|
@ -8,6 +8,7 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
|
||||||
import dev.jdtech.jellyfin.adapters.*
|
import dev.jdtech.jellyfin.adapters.*
|
||||||
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.FavoriteSection
|
||||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
import org.jellyfin.sdk.model.api.BaseItemPerson
|
import org.jellyfin.sdk.model.api.BaseItemPerson
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -167,3 +168,9 @@ fun bindItemPrimaryImage(imageView: ImageView, item: BaseItemDto?) {
|
||||||
imageView.contentDescription = "${item.name} poster"
|
imageView.contentDescription = "${item.name} poster"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@BindingAdapter("favoriteSections")
|
||||||
|
fun bindFavoriteSections(recyclerView: RecyclerView, data: List<FavoriteSection>?) {
|
||||||
|
val adapter = recyclerView.adapter as FavoritesListAdapter
|
||||||
|
adapter.submitList(data)
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
// menu should be considered as top level destinations.
|
// menu should be considered as top level destinations.
|
||||||
val appBarConfiguration = AppBarConfiguration(
|
val appBarConfiguration = AppBarConfiguration(
|
||||||
setOf(
|
setOf(
|
||||||
R.id.navigation_home, R.id.navigation_media, R.id.navigation_settings
|
R.id.navigation_home, R.id.navigation_media, R.id.favoriteFragment
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package dev.jdtech.jellyfin.adapters
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import dev.jdtech.jellyfin.databinding.FavoriteSectionBinding
|
||||||
|
import dev.jdtech.jellyfin.models.FavoriteSection
|
||||||
|
|
||||||
|
class FavoritesListAdapter(
|
||||||
|
private val onClickListener: ViewItemListAdapter.OnClickListener,
|
||||||
|
private val onEpisodeClickListener: HomeEpisodeListAdapter.OnClickListener
|
||||||
|
) : ListAdapter<FavoriteSection, FavoritesListAdapter.SectionViewHolder>(DiffCallback) {
|
||||||
|
class SectionViewHolder(private var binding: FavoriteSectionBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
fun bind(
|
||||||
|
section: FavoriteSection,
|
||||||
|
onClickListener: ViewItemListAdapter.OnClickListener,
|
||||||
|
onEpisodeClickListener: HomeEpisodeListAdapter.OnClickListener
|
||||||
|
) {
|
||||||
|
binding.section = section
|
||||||
|
if (section.name == "Movies" || section.name == "Shows") {
|
||||||
|
binding.itemsRecyclerView.adapter =
|
||||||
|
ViewItemListAdapter(onClickListener, fixedWidth = true)
|
||||||
|
(binding.itemsRecyclerView.adapter as ViewItemListAdapter).submitList(section.items)
|
||||||
|
} else if (section.name == "Episodes") {
|
||||||
|
binding.itemsRecyclerView.adapter =
|
||||||
|
HomeEpisodeListAdapter(onEpisodeClickListener)
|
||||||
|
(binding.itemsRecyclerView.adapter as HomeEpisodeListAdapter).submitList(section.items)
|
||||||
|
}
|
||||||
|
binding.executePendingBindings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object DiffCallback : DiffUtil.ItemCallback<FavoriteSection>() {
|
||||||
|
override fun areItemsTheSame(oldItem: FavoriteSection, newItem: FavoriteSection): Boolean {
|
||||||
|
return oldItem.id == newItem.id
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItem: FavoriteSection,
|
||||||
|
newItem: FavoriteSection
|
||||||
|
): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionViewHolder {
|
||||||
|
return SectionViewHolder(
|
||||||
|
FavoriteSectionBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: SectionViewHolder, position: Int) {
|
||||||
|
val collection = getItem(position)
|
||||||
|
holder.bind(collection, onClickListener, onEpisodeClickListener)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package dev.jdtech.jellyfin.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import dev.jdtech.jellyfin.R
|
||||||
|
import dev.jdtech.jellyfin.adapters.FavoritesListAdapter
|
||||||
|
import dev.jdtech.jellyfin.adapters.HomeEpisodeListAdapter
|
||||||
|
import dev.jdtech.jellyfin.adapters.ViewItemListAdapter
|
||||||
|
import dev.jdtech.jellyfin.databinding.FragmentFavoriteBinding
|
||||||
|
import dev.jdtech.jellyfin.viewmodels.FavoriteViewModel
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class FavoriteFragment : Fragment() {
|
||||||
|
|
||||||
|
private lateinit var binding: FragmentFavoriteBinding
|
||||||
|
private val viewModel: FavoriteViewModel by viewModels()
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
binding = FragmentFavoriteBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
val snackbar =
|
||||||
|
Snackbar.make(
|
||||||
|
binding.mainLayout,
|
||||||
|
getString(R.string.error_loading_data),
|
||||||
|
Snackbar.LENGTH_INDEFINITE
|
||||||
|
)
|
||||||
|
snackbar.setAction(getString(R.string.retry)) {
|
||||||
|
viewModel.loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
binding.viewModel = viewModel
|
||||||
|
binding.favoritesRecyclerView.adapter = FavoritesListAdapter(
|
||||||
|
ViewItemListAdapter.OnClickListener { item ->
|
||||||
|
navigateToMediaInfoFragment(item)
|
||||||
|
}, HomeEpisodeListAdapter.OnClickListener { item ->
|
||||||
|
navigateToEpisodeBottomSheetFragment(item)
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.finishedLoading.observe(viewLifecycleOwner, { isFinished ->
|
||||||
|
binding.loadingIndicator.visibility = if (isFinished) View.GONE else View.VISIBLE
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.error.observe(viewLifecycleOwner, { error ->
|
||||||
|
if (error) {
|
||||||
|
snackbar.show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
viewModel.favoriteSections.observe(viewLifecycleOwner, { sections ->
|
||||||
|
if (sections.isEmpty()) {
|
||||||
|
binding.noFavoritesText.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
binding.noFavoritesText.visibility = View.GONE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToMediaInfoFragment(item: BaseItemDto) {
|
||||||
|
findNavController().navigate(
|
||||||
|
FavoriteFragmentDirections.actionFavoriteFragmentToMediaInfoFragment(
|
||||||
|
item.id,
|
||||||
|
item.name,
|
||||||
|
item.type ?: "Unknown"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateToEpisodeBottomSheetFragment(episode: BaseItemDto) {
|
||||||
|
findNavController().navigate(
|
||||||
|
FavoriteFragmentDirections.actionFavoriteFragmentToEpisodeBottomSheetFragment(
|
||||||
|
episode.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package dev.jdtech.jellyfin.models
|
||||||
|
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
data class FavoriteSection(
|
||||||
|
val id: UUID,
|
||||||
|
val name: String,
|
||||||
|
var items: List<BaseItemDto>
|
||||||
|
)
|
|
@ -12,6 +12,8 @@ interface JellyfinRepository {
|
||||||
|
|
||||||
suspend fun getItems(parentId: UUID? = null): List<BaseItemDto>
|
suspend fun getItems(parentId: UUID? = null): List<BaseItemDto>
|
||||||
|
|
||||||
|
suspend fun getFavoriteItems(): List<BaseItemDto>
|
||||||
|
|
||||||
suspend fun getResumeItems(): List<BaseItemDto>
|
suspend fun getResumeItems(): List<BaseItemDto>
|
||||||
|
|
||||||
suspend fun getLatestMedia(parentId: UUID): List<BaseItemDto>
|
suspend fun getLatestMedia(parentId: UUID): List<BaseItemDto>
|
||||||
|
|
|
@ -36,6 +36,19 @@ class JellyfinRepositoryImpl(private val jellyfinApi: JellyfinApi) : JellyfinRep
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getFavoriteItems(): List<BaseItemDto> {
|
||||||
|
val items: List<BaseItemDto>
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
items = jellyfinApi.itemsApi.getItems(
|
||||||
|
jellyfinApi.userId!!,
|
||||||
|
filters = listOf(ItemFilter.IS_FAVORITE),
|
||||||
|
includeItemTypes = listOf("Movie", "Series", "Episode"),
|
||||||
|
recursive = true
|
||||||
|
).content.items ?: listOf()
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getResumeItems(): List<BaseItemDto> {
|
override suspend fun getResumeItems(): List<BaseItemDto> {
|
||||||
val items: List<BaseItemDto>
|
val items: List<BaseItemDto>
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
package dev.jdtech.jellyfin.viewmodels
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dev.jdtech.jellyfin.models.FavoriteSection
|
||||||
|
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class FavoriteViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val jellyfinRepository: JellyfinRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
private val _favoriteSections = MutableLiveData<List<FavoriteSection>>()
|
||||||
|
val favoriteSections: LiveData<List<FavoriteSection>> = _favoriteSections
|
||||||
|
|
||||||
|
private val _finishedLoading = MutableLiveData<Boolean>()
|
||||||
|
val finishedLoading: LiveData<Boolean> = _finishedLoading
|
||||||
|
|
||||||
|
private val _error = MutableLiveData<Boolean>()
|
||||||
|
val error: LiveData<Boolean> = _error
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadData() {
|
||||||
|
_error.value = false
|
||||||
|
_finishedLoading.value = false
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val items = jellyfinRepository.getFavoriteItems()
|
||||||
|
|
||||||
|
if (items.isEmpty()) {
|
||||||
|
_favoriteSections.value = listOf()
|
||||||
|
_finishedLoading.value = true
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val tempFavoriteSections = mutableListOf<FavoriteSection>()
|
||||||
|
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
FavoriteSection(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"Movies",
|
||||||
|
items.filter { it.type == "Movie" }).let {
|
||||||
|
if (it.items.isNotEmpty()) tempFavoriteSections.add(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FavoriteSection(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"Shows",
|
||||||
|
items.filter { it.type == "Series" }).let {
|
||||||
|
if (it.items.isNotEmpty()) tempFavoriteSections.add(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FavoriteSection(
|
||||||
|
UUID.randomUUID(),
|
||||||
|
"Episodes",
|
||||||
|
items.filter { it.type == "Episode" }).let {
|
||||||
|
if (it.items.isNotEmpty()) tempFavoriteSections.add(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_favoriteSections.value = tempFavoriteSections
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
|
_error.value = true
|
||||||
|
}
|
||||||
|
_finishedLoading.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
app/src/main/res/layout/favorite_section.xml
Normal file
41
app/src/main/res/layout/favorite_section.xml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="section"
|
||||||
|
type="dev.jdtech.jellyfin.models.FavoriteSection" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/section_name"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:text="@{section.name}"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
|
android:textSize="18sp"
|
||||||
|
tools:text="Movies" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/items_recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:layoutAnimation="@anim/overview_media_animation"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/home_episode_item" />
|
||||||
|
</LinearLayout>
|
||||||
|
</layout>
|
63
app/src/main/res/layout/fragment_favorite.xml
Normal file
63
app/src/main/res/layout/fragment_favorite.xml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
<variable
|
||||||
|
name="viewModel"
|
||||||
|
type="dev.jdtech.jellyfin.viewmodels.FavoriteViewModel" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:id="@+id/main_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".fragments.FavoriteFragment">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
|
android:id="@+id/loading_indicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:indeterminate="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:trackCornerRadius="10dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_favorites_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/no_favorites"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/favorites_recycler_view"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:favoriteSections="@{viewModel.favoriteSections}"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:listitem="@layout/favorite_section" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
</layout>
|
|
@ -12,8 +12,8 @@
|
||||||
android:title="@string/title_media" />
|
android:title="@string/title_media" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/navigation_settings"
|
android:id="@+id/favoriteFragment"
|
||||||
android:icon="@drawable/ic_settings"
|
android:icon="@drawable/ic_heart"
|
||||||
android:title="@string/title_settings" />
|
android:title="@string/title_favorite" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
|
@ -154,4 +154,16 @@
|
||||||
android:name="playbackPosition"
|
android:name="playbackPosition"
|
||||||
app:argType="long" />
|
app:argType="long" />
|
||||||
</activity>
|
</activity>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/favoriteFragment"
|
||||||
|
android:name="dev.jdtech.jellyfin.fragments.FavoriteFragment"
|
||||||
|
android:label="@string/title_favorite"
|
||||||
|
tools:layout="@layout/fragment_favorite">
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_favoriteFragment_to_episodeBottomSheetFragment"
|
||||||
|
app:destination="@id/episodeBottomSheetFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_favoriteFragment_to_mediaInfoFragment"
|
||||||
|
app:destination="@id/mediaInfoFragment" />
|
||||||
|
</fragment>
|
||||||
</navigation>
|
</navigation>
|
|
@ -17,6 +17,7 @@
|
||||||
<string name="title_activity_main">MainActivity</string>
|
<string name="title_activity_main">MainActivity</string>
|
||||||
<string name="title_home">Home</string>
|
<string name="title_home">Home</string>
|
||||||
<string name="title_media">My media</string>
|
<string name="title_media">My media</string>
|
||||||
|
<string name="title_favorite">Favorites</string>
|
||||||
<string name="title_settings">Settings</string>
|
<string name="title_settings">Settings</string>
|
||||||
<string name="view_all">View all</string>
|
<string name="view_all">View all</string>
|
||||||
<string name="error_loading_data">Error loading data</string>
|
<string name="error_loading_data">Error loading data</string>
|
||||||
|
@ -38,6 +39,7 @@
|
||||||
<string name="continue_watching">Continue Watching</string>
|
<string name="continue_watching">Continue Watching</string>
|
||||||
<string name="latest_library">Latest %1$s</string>
|
<string name="latest_library">Latest %1$s</string>
|
||||||
<string name="series_poster">Series poster</string>
|
<string name="series_poster">Series poster</string>
|
||||||
|
<string name="no_favorites">You have no favorites</string>
|
||||||
|
|
||||||
<string name="settings_category_language">Language</string>
|
<string name="settings_category_language">Language</string>
|
||||||
<string name="settings_preferred_audio_language">Preferred audio language</string>
|
<string name="settings_preferred_audio_language">Preferred audio language</string>
|
||||||
|
|
Loading…
Reference in a new issue