forked from recloudstream/cloudstream
More library fixes
This commit is contained in:
parent
c4afb5e673
commit
daaab9d35b
11 changed files with 93 additions and 32 deletions
|
@ -13,6 +13,10 @@ enum class SyncIdName {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SyncAPI : OAuth2API {
|
interface SyncAPI : OAuth2API {
|
||||||
|
/**
|
||||||
|
* Set this to true if the user updates something on the list like watch status or score
|
||||||
|
**/
|
||||||
|
var requireLibraryRefresh: Boolean
|
||||||
val mainUrl: String
|
val mainUrl: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -12,6 +12,11 @@ class SyncRepo(private val repo: SyncAPI) {
|
||||||
val mainUrl = repo.mainUrl
|
val mainUrl = repo.mainUrl
|
||||||
val requiresLogin = repo.requiresLogin
|
val requiresLogin = repo.requiresLogin
|
||||||
val syncIdName = repo.syncIdName
|
val syncIdName = repo.syncIdName
|
||||||
|
var requireLibraryRefresh: Boolean
|
||||||
|
get() = repo.requireLibraryRefresh
|
||||||
|
set(value) {
|
||||||
|
repo.requireLibraryRefresh = value
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
|
suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource<Boolean> {
|
||||||
return safeApiCall { repo.score(id, status) }
|
return safeApiCall { repo.score(id, status) }
|
||||||
|
|
|
@ -28,6 +28,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
override val key = "6871"
|
override val key = "6871"
|
||||||
override val redirectUrl = "anilistlogin"
|
override val redirectUrl = "anilistlogin"
|
||||||
override val idPrefix = "anilist"
|
override val idPrefix = "anilist"
|
||||||
|
override var requireLibraryRefresh = true
|
||||||
override var mainUrl = "https://anilist.co"
|
override var mainUrl = "https://anilist.co"
|
||||||
override val icon = R.drawable.ic_anilist_icon
|
override val icon = R.drawable.ic_anilist_icon
|
||||||
override val requiresLogin = false
|
override val requiresLogin = false
|
||||||
|
@ -47,6 +48,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logOut() {
|
override fun logOut() {
|
||||||
|
requireLibraryRefresh = true
|
||||||
removeAccountKeys()
|
removeAccountKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +70,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
setKey(accountId, ANILIST_TOKEN_KEY, token)
|
setKey(accountId, ANILIST_TOKEN_KEY, token)
|
||||||
setKey(ANILIST_SHOULD_UPDATE_LIST, true)
|
setKey(ANILIST_SHOULD_UPDATE_LIST, true)
|
||||||
val user = getUser()
|
val user = getUser()
|
||||||
|
requireLibraryRefresh = true
|
||||||
return user != null
|
return user != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +176,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
fromIntToAnimeStatus(status.status),
|
fromIntToAnimeStatus(status.status),
|
||||||
status.score,
|
status.score,
|
||||||
status.watchedEpisodes
|
status.watchedEpisodes
|
||||||
)
|
).also {
|
||||||
|
requireLibraryRefresh = requireLibraryRefresh || it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -295,15 +300,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
val shows = searchShows(name.replace(blackListRegex, ""))
|
val shows = searchShows(name.replace(blackListRegex, ""))
|
||||||
|
|
||||||
shows?.data?.Page?.media?.find {
|
shows?.data?.Page?.media?.find {
|
||||||
malId ?: "NONE" == it.idMal.toString()
|
(malId ?: "NONE") == it.idMal.toString()
|
||||||
}?.let { return it }
|
}?.let { return it }
|
||||||
|
|
||||||
val filtered =
|
val filtered =
|
||||||
shows?.data?.Page?.media?.filter {
|
shows?.data?.Page?.media?.filter {
|
||||||
(
|
(((it.startDate.year ?: year.toString()) == year.toString()
|
||||||
it.startDate.year ?: year.toString() == year.toString()
|
|| year == null))
|
||||||
|| year == null
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
filtered?.forEach {
|
filtered?.forEach {
|
||||||
it.title.romaji?.let { romaji ->
|
it.title.romaji?.let { romaji ->
|
||||||
|
@ -529,7 +532,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
app.post(
|
app.post(
|
||||||
"https://graphql.anilist.co/",
|
"https://graphql.anilist.co/",
|
||||||
headers = mapOf(
|
headers = mapOf(
|
||||||
"Authorization" to "Bearer " + (getAuth() ?: return@suspendSafeApiCall null),
|
"Authorization" to "Bearer " + (getAuth()
|
||||||
|
?: return@suspendSafeApiCall null),
|
||||||
if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache"
|
if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache"
|
||||||
),
|
),
|
||||||
cacheTime = 0,
|
cacheTime = 0,
|
||||||
|
@ -722,9 +726,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val text = postApi(query).also {
|
val text = postApi(query)
|
||||||
println("REPONSE $it")
|
|
||||||
}
|
|
||||||
return text?.toKotlinObject()
|
return text?.toKotlinObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ class LocalList : SyncAPI {
|
||||||
override val requiresLogin = false
|
override val requiresLogin = false
|
||||||
override val createAccountUrl: Nothing? = null
|
override val createAccountUrl: Nothing? = null
|
||||||
override val idPrefix = "local"
|
override val idPrefix = "local"
|
||||||
|
override var requireLibraryRefresh = true
|
||||||
|
|
||||||
override fun loginInfo(): AuthAPI.LoginInfo {
|
override fun loginInfo(): AuthAPI.LoginInfo {
|
||||||
return AuthAPI.LoginInfo(
|
return AuthAPI.LoginInfo(
|
||||||
|
|
|
@ -33,14 +33,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
override val redirectUrl = "mallogin"
|
override val redirectUrl = "mallogin"
|
||||||
override val idPrefix = "mal"
|
override val idPrefix = "mal"
|
||||||
override var mainUrl = "https://myanimelist.net"
|
override var mainUrl = "https://myanimelist.net"
|
||||||
val apiUrl = "https://api.myanimelist.net"
|
private val apiUrl = "https://api.myanimelist.net"
|
||||||
override val icon = R.drawable.mal_logo
|
override val icon = R.drawable.mal_logo
|
||||||
override val requiresLogin = false
|
override val requiresLogin = false
|
||||||
override val syncIdName = SyncIdName.MyAnimeList
|
override val syncIdName = SyncIdName.MyAnimeList
|
||||||
|
override var requireLibraryRefresh = true
|
||||||
override val createAccountUrl = "$mainUrl/register.php"
|
override val createAccountUrl = "$mainUrl/register.php"
|
||||||
|
|
||||||
override fun logOut() {
|
override fun logOut() {
|
||||||
|
requireLibraryRefresh = true
|
||||||
removeAccountKeys()
|
removeAccountKeys()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +94,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
fromIntToAnimeStatus(status.status),
|
fromIntToAnimeStatus(status.status),
|
||||||
status.score,
|
status.score,
|
||||||
status.watchedEpisodes
|
status.watchedEpisodes
|
||||||
)
|
).also {
|
||||||
|
requireLibraryRefresh = requireLibraryRefresh || it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MalAnime(
|
data class MalAnime(
|
||||||
|
@ -311,9 +314,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
setKey(accountId, MAL_UNIXTIME_KEY, (token.expires_in + unixTime))
|
setKey(accountId, MAL_UNIXTIME_KEY, (token.expires_in + unixTime))
|
||||||
setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refresh_token)
|
setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refresh_token)
|
||||||
setKey(accountId, MAL_TOKEN_KEY, token.access_token)
|
setKey(accountId, MAL_TOKEN_KEY, token.access_token)
|
||||||
|
requireLibraryRefresh = true
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
logError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +336,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
).text
|
).text
|
||||||
storeToken(res)
|
storeToken(res)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
logError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,7 +437,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
return getKey(MAL_CACHED_LIST) as? Array<Data>
|
return getKey(MAL_CACHED_LIST) as? Array<Data>
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getMalAnimeListSmart(): Array<Data>? {
|
private suspend fun getMalAnimeListSmart(): Array<Data>? {
|
||||||
if (getAuth() == null) return null
|
if (getAuth() == null) return null
|
||||||
return if (getKey(MAL_SHOULD_UPDATE_LIST, true) == true) {
|
return if (getKey(MAL_SHOULD_UPDATE_LIST, true) == true) {
|
||||||
val list = getMalAnimeList()
|
val list = getMalAnimeList()
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import com.lagradost.cloudstream3.APIHolder
|
import com.lagradost.cloudstream3.APIHolder
|
||||||
|
@ -26,6 +27,7 @@ import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||||
import kotlinx.android.synthetic.main.fragment_library.*
|
import kotlinx.android.synthetic.main.fragment_library.*
|
||||||
|
@ -264,8 +266,11 @@ class LibraryFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
viewpager?.offscreenPageLimit = 2
|
viewpager?.offscreenPageLimit = 2
|
||||||
|
viewpager?.reduceDragSensitivity()
|
||||||
|
|
||||||
observe(libraryViewModel.pages) { pages ->
|
observe(libraryViewModel.pages) { pages ->
|
||||||
|
empty_list_textview?.isVisible = pages.all { it.items.isEmpty() }
|
||||||
|
|
||||||
(viewpager.adapter as? ViewpagerAdapter)?.pages = pages
|
(viewpager.adapter as? ViewpagerAdapter)?.pages = pages
|
||||||
// Using notifyItemRangeChanged keeps the animations when sorting
|
// Using notifyItemRangeChanged keeps the animations when sorting
|
||||||
viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0)
|
viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0)
|
||||||
|
|
|
@ -4,6 +4,8 @@ import androidx.annotation.StringRes
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.mvvm.Resource
|
import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
|
||||||
|
@ -20,20 +22,29 @@ enum class ListSorting(@StringRes val stringRes: Int) {
|
||||||
AlphabeticalZ(R.string.sort_alphabetical_z),
|
AlphabeticalZ(R.string.sort_alphabetical_z),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val LAST_SYNC_API_KEY = "last_sync_api"
|
||||||
|
|
||||||
class LibraryViewModel : ViewModel() {
|
class LibraryViewModel : ViewModel() {
|
||||||
private val _pages: MutableLiveData<List<SyncAPI.Page>> = MutableLiveData(emptyList())
|
private val _pages: MutableLiveData<List<SyncAPI.Page>> = MutableLiveData(null)
|
||||||
val pages: LiveData<List<SyncAPI.Page>> = _pages
|
val pages: LiveData<List<SyncAPI.Page>> = _pages
|
||||||
|
|
||||||
private val _currentApiName: MutableLiveData<String> = MutableLiveData("")
|
private val _currentApiName: MutableLiveData<String> = MutableLiveData("")
|
||||||
val currentApiName: LiveData<String> = _currentApiName
|
val currentApiName: LiveData<String> = _currentApiName
|
||||||
|
|
||||||
private val availableSyncApis = SyncApis.filter { it.hasAccount() }
|
private val availableSyncApis
|
||||||
|
get() = SyncApis.filter { it.hasAccount() }
|
||||||
|
|
||||||
// TODO REMEMBER SELECTION
|
var currentSyncApi = availableSyncApis.let { allApis ->
|
||||||
var currentSyncApi = availableSyncApis.firstOrNull()
|
val lastSelection = getKey<String>(LAST_SYNC_API_KEY)
|
||||||
private set
|
availableSyncApis.firstOrNull { it.name == lastSelection } ?: allApis.firstOrNull()
|
||||||
|
}
|
||||||
|
private set(value) {
|
||||||
|
field = value
|
||||||
|
setKey(LAST_SYNC_API_KEY, field?.name)
|
||||||
|
}
|
||||||
|
|
||||||
val availableApiNames: List<String> = availableSyncApis.map { it.name }
|
val availableApiNames: List<String>
|
||||||
|
get() = availableSyncApis.map { it.name }
|
||||||
|
|
||||||
val sortingMethods = listOf(
|
val sortingMethods = listOf(
|
||||||
ListSorting.RatingHigh,
|
ListSorting.RatingHigh,
|
||||||
|
@ -64,15 +75,19 @@ class LibraryViewModel : ViewModel() {
|
||||||
|
|
||||||
fun reloadPages(forceReload: Boolean) {
|
fun reloadPages(forceReload: Boolean) {
|
||||||
// Only skip loading if its not forced and pages is not empty
|
// Only skip loading if its not forced and pages is not empty
|
||||||
if (!forceReload && pages.value?.isNotEmpty() == true) return
|
if (!forceReload && pages.value?.isNotEmpty() == true &&
|
||||||
|
currentSyncApi?.requireLibraryRefresh != true
|
||||||
|
) return
|
||||||
|
|
||||||
ioSafe {
|
ioSafe {
|
||||||
currentSyncApi?.let { repo ->
|
currentSyncApi?.let { repo ->
|
||||||
_currentApiName.postValue(repo.name)
|
_currentApiName.postValue(repo.name)
|
||||||
val library = (repo.getPersonalLibrary() as? Resource.Success)?.value ?: return@let
|
val library = (repo.getPersonalLibrary() as? Resource.Success)?.value ?: return@let
|
||||||
|
repo.requireLibraryRefresh = false
|
||||||
|
|
||||||
val listSubset = library.allLibraryItems.groupBy { it.listName }
|
val listSubset = library.allLibraryItems.groupBy { it.listName }
|
||||||
val allLists = library.allListNames.associateWith { emptyList<SyncAPI.LibraryItem>() }
|
val allLists =
|
||||||
|
library.allListNames.associateWith { emptyList<SyncAPI.LibraryItem>() }
|
||||||
|
|
||||||
val filledLists = allLists + listSubset
|
val filledLists = allLists + listSubset
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.tvprovider.media.tv.*
|
import androidx.tvprovider.media.tv.*
|
||||||
import androidx.tvprovider.media.tv.WatchNextProgram.fromCursor
|
import androidx.tvprovider.media.tv.WatchNextProgram.fromCursor
|
||||||
|
import androidx.viewpager2.widget.ViewPager2
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.cast.framework.CastState
|
import com.google.android.gms.cast.framework.CastState
|
||||||
|
@ -166,6 +167,18 @@ object AppUtils {
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/67441735/13746422
|
||||||
|
fun ViewPager2.reduceDragSensitivity(f: Int = 4) {
|
||||||
|
val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
|
||||||
|
recyclerViewField.isAccessible = true
|
||||||
|
val recyclerView = recyclerViewField.get(this) as RecyclerView
|
||||||
|
|
||||||
|
val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
|
||||||
|
touchSlopField.isAccessible = true
|
||||||
|
val touchSlop = touchSlopField.get(recyclerView) as Int
|
||||||
|
touchSlopField.set(recyclerView, touchSlop * f) // "8" was obtained experimentally
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun getAllWatchNextPrograms(context: Context): Set<Long> {
|
fun getAllWatchNextPrograms(context: Context): Set<Long> {
|
||||||
val COLUMN_WATCH_NEXT_ID_INDEX = 0
|
val COLUMN_WATCH_NEXT_ID_INDEX = 0
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.lagradost.cloudstream3.DubStatus
|
||||||
import com.lagradost.cloudstream3.SearchQuality
|
import com.lagradost.cloudstream3.SearchQuality
|
||||||
import com.lagradost.cloudstream3.SearchResponse
|
import com.lagradost.cloudstream3.SearchResponse
|
||||||
import com.lagradost.cloudstream3.TvType
|
import com.lagradost.cloudstream3.TvType
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
||||||
|
@ -195,6 +196,7 @@ object DataStoreHelper {
|
||||||
fun setBookmarkedData(id: Int?, data: BookmarkedData) {
|
fun setBookmarkedData(id: Int?, data: BookmarkedData) {
|
||||||
if (id == null) return
|
if (id == null) return
|
||||||
setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
|
setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data)
|
||||||
|
AccountManager.localListApi.requireLibraryRefresh = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBookmarkedData(id: Int?): BookmarkedData? {
|
fun getBookmarkedData(id: Int?): BookmarkedData? {
|
||||||
|
|
|
@ -7,6 +7,17 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/empty_list_textview"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_margin="30dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/empty_library_message"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/search_bar"
|
android:id="@+id/search_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -16,13 +27,14 @@
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/provider_selector"
|
android:id="@+id/provider_selector"
|
||||||
android:layout_width="25dp"
|
android:layout_width="25dp"
|
||||||
android:layout_height="25dp"
|
android:layout_height="25dp"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_gravity="end|center_vertical"
|
||||||
|
|
||||||
android:layout_marginStart="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/change_providers_img_des"
|
android:contentDescription="@string/change_providers_img_des"
|
||||||
|
@ -62,15 +74,14 @@
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/list_selector"
|
android:id="@+id/list_selector"
|
||||||
android:layout_width="25dp"
|
android:layout_width="45dp"
|
||||||
android:layout_height="25dp"
|
android:layout_height="45dp"
|
||||||
android:layout_gravity="end|center_vertical"
|
android:layout_gravity="end|center_vertical"
|
||||||
|
|
||||||
android:layout_margin="10dp"
|
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/change_providers_img_des"
|
android:contentDescription="@string/change_providers_img_des"
|
||||||
android:nextFocusLeft="@id/main_search"
|
android:nextFocusLeft="@id/main_search"
|
||||||
android:nextFocusRight="@id/main_search"
|
android:nextFocusRight="@id/main_search"
|
||||||
|
android:padding="10dp"
|
||||||
android:src="@drawable/ic_baseline_filter_list_24"
|
android:src="@drawable/ic_baseline_filter_list_24"
|
||||||
app:tint="?attr/textColor" />
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
|
@ -123,10 +134,8 @@
|
||||||
android:background="?attr/primaryGrayBackground"
|
android:background="?attr/primaryGrayBackground"
|
||||||
android:descendantFocusability="blocksDescendants"
|
android:descendantFocusability="blocksDescendants"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
|
|
||||||
android:paddingHorizontal="5dp"
|
android:paddingHorizontal="5dp"
|
||||||
app:layout_scrollFlags="noScroll"
|
app:layout_scrollFlags="noScroll"
|
||||||
app:tabBackground="?attr/primaryGrayBackground"
|
|
||||||
app:tabGravity="center"
|
app:tabGravity="center"
|
||||||
app:tabIndicator="@drawable/indicator_background"
|
app:tabIndicator="@drawable/indicator_background"
|
||||||
app:tabIndicatorColor="@color/textColor"
|
app:tabIndicatorColor="@color/textColor"
|
||||||
|
|
|
@ -627,4 +627,5 @@
|
||||||
<string name="sort_alphabetical_z">Alphabetical (Z to A)</string>
|
<string name="sort_alphabetical_z">Alphabetical (Z to A)</string>
|
||||||
<string name="select_library">Select Library</string>
|
<string name="select_library">Select Library</string>
|
||||||
<string name="open_with">Open with</string>
|
<string name="open_with">Open with</string>
|
||||||
|
<string name="empty_library_message">Looks like your library is empty :(\nLogin to a library account or add shows to your local library</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue