mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
WIP adding open with specified extension
This commit is contained in:
parent
2a2a0a26e7
commit
8cdc31ffcd
11 changed files with 203 additions and 69 deletions
|
@ -10,10 +10,10 @@ import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature
|
import com.fasterxml.jackson.databind.DeserializationFeature
|
||||||
import com.fasterxml.jackson.databind.json.JsonMapper
|
import com.fasterxml.jackson.databind.json.JsonMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||||
import com.lagradost.cloudstream3.metaproviders.SyncIdName
|
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.*
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
|
|
@ -3,27 +3,24 @@ package com.lagradost.cloudstream3.metaproviders
|
||||||
import com.lagradost.cloudstream3.MainAPI
|
import com.lagradost.cloudstream3.MainAPI
|
||||||
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
|
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi
|
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi
|
|
||||||
import com.lagradost.cloudstream3.utils.SyncUtil
|
|
||||||
|
|
||||||
enum class SyncIdName {
|
|
||||||
AniList,
|
|
||||||
MyAnimeList,
|
|
||||||
Trakt,
|
|
||||||
Imdb
|
|
||||||
}
|
|
||||||
|
|
||||||
object SyncRedirector {
|
object SyncRedirector {
|
||||||
val syncApis = SyncApis
|
val syncApis = SyncApis
|
||||||
private val syncIds =
|
private val syncIds =
|
||||||
listOf(
|
listOf(
|
||||||
SyncIdName.MyAnimeList to Regex("""myanimelist\.net\/anime\/(\d+)"""),
|
SyncIdName.MyAnimeList to Regex("""myanimelist\.net\/anime\/(\d+)"""),
|
||||||
SyncIdName.AniList to Regex("""anilist\.co\/anime\/(\d+)""")
|
SyncIdName.Anilist to Regex("""anilist\.co\/anime\/(\d+)""")
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun redirect(url: String, providerApi: MainAPI): String {
|
suspend fun redirect(
|
||||||
|
url: String,
|
||||||
|
providerApi: MainAPI
|
||||||
|
): String {
|
||||||
|
// Deprecated since providers should do this instead!
|
||||||
|
|
||||||
// Tries built in ID -> ProviderUrl
|
// Tries built in ID -> ProviderUrl
|
||||||
|
/*
|
||||||
for (api in syncApis) {
|
for (api in syncApis) {
|
||||||
if (url.contains(api.mainUrl)) {
|
if (url.contains(api.mainUrl)) {
|
||||||
val otherApi = when (api.name) {
|
val otherApi = when (api.name) {
|
||||||
|
@ -42,8 +39,10 @@ object SyncRedirector {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Tries provider solution
|
// Tries provider solution
|
||||||
|
// This goes through all sync ids and finds supported id by said provider
|
||||||
return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) ->
|
return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) ->
|
||||||
if (providerApi.supportedSyncNames.contains(syncName)) {
|
if (providerApi.supportedSyncNames.contains(syncName)) {
|
||||||
syncRegex.find(url)?.value?.let {
|
syncRegex.find(url)?.value?.let {
|
||||||
|
|
|
@ -3,9 +3,18 @@ package com.lagradost.cloudstream3.syncproviders
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.ui.library.LibraryItem
|
import com.lagradost.cloudstream3.ui.library.LibraryItem
|
||||||
|
|
||||||
|
enum class SyncIdName {
|
||||||
|
Anilist,
|
||||||
|
MyAnimeList,
|
||||||
|
Trakt,
|
||||||
|
Imdb
|
||||||
|
}
|
||||||
|
|
||||||
interface SyncAPI : OAuth2API {
|
interface SyncAPI : OAuth2API {
|
||||||
val mainUrl: String
|
val mainUrl: String
|
||||||
|
|
||||||
|
val syncIdName: SyncIdName
|
||||||
|
|
||||||
/**
|
/**
|
||||||
-1 -> None
|
-1 -> None
|
||||||
0 -> Watching
|
0 -> Watching
|
||||||
|
|
|
@ -12,6 +12,7 @@ class SyncRepo(private val repo: SyncAPI) {
|
||||||
val icon = repo.icon
|
val icon = repo.icon
|
||||||
val mainUrl = repo.mainUrl
|
val mainUrl = repo.mainUrl
|
||||||
val requiresLogin = repo.requiresLogin
|
val requiresLogin = repo.requiresLogin
|
||||||
|
val syncIdName = repo.syncIdName
|
||||||
|
|
||||||
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) }
|
||||||
|
@ -37,5 +38,7 @@ class SyncRepo(private val repo: SyncAPI) {
|
||||||
return normalSafeApiCall { repo.loginInfo() != null } ?: false
|
return normalSafeApiCall { repo.loginInfo() != null } ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIdFromUrl(url: String): String = repo.getIdFromUrl(url)
|
fun getIdFromUrl(url: String): String? = normalSafeApiCall {
|
||||||
|
repo.getIdFromUrl(url)
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.ui.library.LibraryItem
|
import com.lagradost.cloudstream3.ui.library.LibraryItem
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
||||||
|
@ -29,6 +30,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
override val icon = R.drawable.ic_anilist_icon
|
override val icon = R.drawable.ic_anilist_icon
|
||||||
override val requiresLogin = false
|
override val requiresLogin = false
|
||||||
override val createAccountUrl = "$mainUrl/signup"
|
override val createAccountUrl = "$mainUrl/signup"
|
||||||
|
override val syncIdName = SyncIdName.Anilist
|
||||||
|
|
||||||
override fun loginInfo(): AuthAPI.LoginInfo? {
|
override fun loginInfo(): AuthAPI.LoginInfo? {
|
||||||
// context.getUser(true)?.
|
// context.getUser(true)?.
|
||||||
|
@ -603,6 +605,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
?: this.media.synonyms.firstOrNull()
|
?: this.media.synonyms.firstOrNull()
|
||||||
?: "",
|
?: "",
|
||||||
"https://anilist.co/anime/${this.media.id}/",
|
"https://anilist.co/anime/${this.media.id}/",
|
||||||
|
this.media.id.toString(),
|
||||||
listName?.lowercase()?.capitalize() ?: return null,
|
listName?.lowercase()?.capitalize() ?: return null,
|
||||||
this.progress,
|
this.progress,
|
||||||
this.media.episodes,
|
this.media.episodes,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
import com.lagradost.cloudstream3.syncproviders.AuthAPI
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.ui.library.LibraryItem
|
import com.lagradost.cloudstream3.ui.library.LibraryItem
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
||||||
|
@ -35,6 +36,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
val apiUrl = "https://api.myanimelist.net"
|
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 createAccountUrl = "$mainUrl/register.php"
|
override val createAccountUrl = "$mainUrl/register.php"
|
||||||
|
|
||||||
|
@ -388,6 +390,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
return LibraryItem(
|
return LibraryItem(
|
||||||
this.node.title,
|
this.node.title,
|
||||||
"https://myanimelist.net/anime/${this.node.id}/",
|
"https://myanimelist.net/anime/${this.node.id}/",
|
||||||
|
this.node.id.toString(),
|
||||||
this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE",
|
this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE",
|
||||||
this.list_status?.num_episodes_watched,
|
this.list_status?.num_episodes_watched,
|
||||||
this.node.num_episodes,
|
this.node.num_episodes,
|
||||||
|
@ -395,7 +398,8 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI {
|
||||||
"MAL",
|
"MAL",
|
||||||
TvType.Anime,
|
TvType.Anime,
|
||||||
this.node.main_picture?.large ?: this.node.main_picture?.medium,
|
this.node.main_picture?.large ?: this.node.main_picture?.medium,
|
||||||
null, null, null
|
null,
|
||||||
|
null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,35 @@ import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
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.allProviders
|
||||||
|
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.debugAssert
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.SyncIdName
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
|
||||||
|
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
|
||||||
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.*
|
||||||
|
|
||||||
|
const val LIBRARY_FOLDER = "library_folder"
|
||||||
|
|
||||||
|
|
||||||
|
enum class LibraryOpenerType {
|
||||||
|
Provider,
|
||||||
|
Browser,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used to store how the user wants to open said poster */
|
||||||
|
class LibraryOpener(
|
||||||
|
val openType: LibraryOpenerType,
|
||||||
|
val data: String?,
|
||||||
|
)
|
||||||
|
|
||||||
class LibraryFragment : Fragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -27,8 +47,7 @@ class LibraryFragment : Fragment() {
|
||||||
private val libraryViewModel: LibraryViewModel by activityViewModels()
|
private val libraryViewModel: LibraryViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View? {
|
): View? {
|
||||||
return inflater.inflate(R.layout.fragment_library, container, false)
|
return inflater.inflate(R.layout.fragment_library, container, false)
|
||||||
}
|
}
|
||||||
|
@ -38,11 +57,13 @@ class LibraryFragment : Fragment() {
|
||||||
context?.fixPaddingStatusbar(library_root)
|
context?.fixPaddingStatusbar(library_root)
|
||||||
|
|
||||||
sort_fab?.setOnClickListener {
|
sort_fab?.setOnClickListener {
|
||||||
val methods = libraryViewModel.sortingMethods
|
val methods = libraryViewModel.sortingMethods.map {
|
||||||
.map { txt(it.stringRes).asString(context ?: view.context) }
|
txt(it.stringRes).asString(
|
||||||
|
context ?: view.context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
activity?.showBottomDialog(
|
activity?.showBottomDialog(methods,
|
||||||
methods,
|
|
||||||
libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod),
|
libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod),
|
||||||
"Sort by",
|
"Sort by",
|
||||||
false,
|
false,
|
||||||
|
@ -50,8 +71,7 @@ class LibraryFragment : Fragment() {
|
||||||
{
|
{
|
||||||
val method = libraryViewModel.sortingMethods[it]
|
val method = libraryViewModel.sortingMethods[it]
|
||||||
libraryViewModel.sort(method)
|
libraryViewModel.sort(method)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
main_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||||
|
@ -72,18 +92,75 @@ class LibraryFragment : Fragment() {
|
||||||
val items = libraryViewModel.availableApiNames
|
val items = libraryViewModel.availableApiNames
|
||||||
val currentItem = libraryViewModel.currentApiName.value
|
val currentItem = libraryViewModel.currentApiName.value
|
||||||
|
|
||||||
activity?.showBottomDialog(
|
activity?.showBottomDialog(items,
|
||||||
items,
|
|
||||||
items.indexOf(currentItem),
|
items.indexOf(currentItem),
|
||||||
"Select library",
|
"Select library",
|
||||||
false,
|
false,
|
||||||
{}
|
{}) {
|
||||||
) {
|
|
||||||
val selectedItem = items.getOrNull(it) ?: return@showBottomDialog
|
val selectedItem = items.getOrNull(it) ?: return@showBottomDialog
|
||||||
libraryViewModel.switchList(selectedItem)
|
libraryViewModel.switchList(selectedItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a plugin selection dialogue and saves the response
|
||||||
|
**/
|
||||||
|
fun showPluginSelectionDialog(key: String, syncId: SyncIdName) {
|
||||||
|
val availableProviders = allProviders.filter {
|
||||||
|
it.supportedSyncNames.contains(syncId)
|
||||||
|
}.map { it.name }
|
||||||
|
|
||||||
|
val baseOptions = listOf(LibraryOpenerType.Browser.name)
|
||||||
|
|
||||||
|
val items = baseOptions + availableProviders
|
||||||
|
|
||||||
|
val savedSelection = getKey<LibraryOpener>(LIBRARY_FOLDER, key)
|
||||||
|
val selectedIndex =
|
||||||
|
when {
|
||||||
|
savedSelection == null -> -1
|
||||||
|
// If provider
|
||||||
|
savedSelection.openType == LibraryOpenerType.Provider
|
||||||
|
&& savedSelection.data != null -> {
|
||||||
|
availableProviders.indexOf(savedSelection.data).takeIf { it != -1 }
|
||||||
|
?.plus(baseOptions.size) ?: -1
|
||||||
|
}
|
||||||
|
// Else base option
|
||||||
|
else -> baseOptions.indexOf(savedSelection.openType.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
activity?.showBottomDialog(
|
||||||
|
items,
|
||||||
|
selectedIndex,
|
||||||
|
"Open with",
|
||||||
|
true,
|
||||||
|
{},
|
||||||
|
) {
|
||||||
|
val savedData = if (it < baseOptions.size) {
|
||||||
|
LibraryOpener(
|
||||||
|
LibraryOpenerType.valueOf(baseOptions[it]),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LibraryOpener(
|
||||||
|
LibraryOpenerType.Provider,
|
||||||
|
items[it]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
setKey(
|
||||||
|
LIBRARY_FOLDER,
|
||||||
|
key,
|
||||||
|
savedData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider_selector?.setOnClickListener {
|
||||||
|
val syncName = libraryViewModel.currentSyncApi?.syncIdName ?: return@setOnClickListener
|
||||||
|
showPluginSelectionDialog(syncName.name, syncName)
|
||||||
|
}
|
||||||
|
|
||||||
viewpager?.setPageTransformer(LibraryScrollTransformer())
|
viewpager?.setPageTransformer(LibraryScrollTransformer())
|
||||||
viewpager?.adapter =
|
viewpager?.adapter =
|
||||||
viewpager.adapter ?: ViewpagerAdapter(emptyList(), { isScrollingDown: Boolean ->
|
viewpager.adapter ?: ViewpagerAdapter(emptyList(), { isScrollingDown: Boolean ->
|
||||||
|
@ -92,10 +169,27 @@ class LibraryFragment : Fragment() {
|
||||||
} else {
|
} else {
|
||||||
sort_fab?.extend()
|
sort_fab?.extend()
|
||||||
}
|
}
|
||||||
}) { searchClickCallback ->
|
}) callback@{ searchClickCallback ->
|
||||||
|
|
||||||
|
// To prevent future accidents
|
||||||
|
debugAssert({
|
||||||
|
searchClickCallback.card !is LibraryItem
|
||||||
|
}, {
|
||||||
|
"searchClickCallback ${searchClickCallback.card} is not a LibraryItem"
|
||||||
|
})
|
||||||
|
val syncId = (searchClickCallback.card as LibraryItem).syncId
|
||||||
|
|
||||||
println("SEARCH CLICK $searchClickCallback")
|
println("SEARCH CLICK $searchClickCallback")
|
||||||
if (searchClickCallback.action == SEARCH_ACTION_LOAD) {
|
when (searchClickCallback.action) {
|
||||||
activity?.loadSearchResult(searchClickCallback.card)
|
SEARCH_ACTION_SHOW_METADATA -> {
|
||||||
|
val syncName =
|
||||||
|
libraryViewModel.currentSyncApi?.syncIdName ?: return@callback
|
||||||
|
showPluginSelectionDialog(syncId, syncName)
|
||||||
|
}
|
||||||
|
SEARCH_ACTION_LOAD -> {
|
||||||
|
val savedSelection = getKey<LibraryOpener>(LIBRARY_FOLDER, syncId)
|
||||||
|
activity?.loadSearchResult(searchClickCallback.card)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewpager?.offscreenPageLimit = 2
|
viewpager?.offscreenPageLimit = 2
|
||||||
|
|
|
@ -27,7 +27,10 @@ class LibraryViewModel : ViewModel() {
|
||||||
val currentApiName: LiveData<String> = _currentApiName
|
val currentApiName: LiveData<String> = _currentApiName
|
||||||
|
|
||||||
private val availableSyncApis = SyncApis.filter { it.hasAccount() }
|
private val availableSyncApis = SyncApis.filter { it.hasAccount() }
|
||||||
private var currentSyncApi = availableSyncApis.firstOrNull()
|
|
||||||
|
// TODO REMEMBER SELECTION
|
||||||
|
var currentSyncApi = availableSyncApis.firstOrNull()
|
||||||
|
private set
|
||||||
|
|
||||||
val availableApiNames: List<String> = availableSyncApis.map { it.name }
|
val availableApiNames: List<String> = availableSyncApis.map { it.name }
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ data class Page(
|
||||||
data class LibraryItem(
|
data class LibraryItem(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val url: String,
|
override val url: String,
|
||||||
|
val syncId: String,
|
||||||
val listName: String,
|
val listName: String,
|
||||||
val episodesCompleted: Int?,
|
val episodesCompleted: Int?,
|
||||||
val episodesTotal: Int?,
|
val episodesTotal: Int?,
|
||||||
|
@ -51,8 +52,8 @@ data class LibraryItem(
|
||||||
override var type: TvType?,
|
override var type: TvType?,
|
||||||
override var posterUrl: String?,
|
override var posterUrl: String?,
|
||||||
override var posterHeaders: Map<String, String>?,
|
override var posterHeaders: Map<String, String>?,
|
||||||
override var id: Int?,
|
|
||||||
override var quality: SearchQuality?,
|
override var quality: SearchQuality?,
|
||||||
|
override var id: Int? = null,
|
||||||
) : SearchResponse
|
) : SearchResponse
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +91,6 @@ class ViewpagerAdapter(
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
itemViewTest.page_recyclerview.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
|
itemViewTest.page_recyclerview.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
|
||||||
println("DOWN ${(scrollY - oldScrollY)}")
|
|
||||||
val diff = scrollY - oldScrollY
|
val diff = scrollY - oldScrollY
|
||||||
if (diff == 0) return@setOnScrollChangeListener
|
if (diff == 0) return@setOnScrollChangeListener
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||||
import kotlinx.android.synthetic.main.search_result_compact.view.*
|
import kotlinx.android.synthetic.main.search_result_compact.view.*
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/** Click */
|
||||||
const val SEARCH_ACTION_LOAD = 0
|
const val SEARCH_ACTION_LOAD = 0
|
||||||
|
/** Long press */
|
||||||
const val SEARCH_ACTION_SHOW_METADATA = 1
|
const val SEARCH_ACTION_SHOW_METADATA = 1
|
||||||
const val SEARCH_ACTION_PLAY_FILE = 2
|
const val SEARCH_ACTION_PLAY_FILE = 2
|
||||||
const val SEARCH_ACTION_FOCUSED = 4
|
const val SEARCH_ACTION_FOCUSED = 4
|
||||||
|
|
|
@ -13,52 +13,69 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/primaryGrayBackground">
|
android:background="?attr/primaryGrayBackground">
|
||||||
|
|
||||||
<FrameLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="40dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="10dp"
|
android:orientation="horizontal">
|
||||||
android:background="@drawable/search_background"
|
|
||||||
android:visibility="visible"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.SearchView
|
|
||||||
android:id="@+id/main_search"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
|
|
||||||
android:iconifiedByDefault="false"
|
|
||||||
android:imeOptions="actionSearch"
|
|
||||||
|
|
||||||
android:inputType="text"
|
|
||||||
android:nextFocusLeft="@id/nav_rail_view"
|
|
||||||
|
|
||||||
android:nextFocusRight="@id/search_filter"
|
|
||||||
android:paddingStart="-10dp"
|
|
||||||
app:iconifiedByDefault="false"
|
|
||||||
app:queryBackground="@color/transparent"
|
|
||||||
app:queryHint="@string/search_hint"
|
|
||||||
app:searchIcon="@drawable/search_icon"
|
|
||||||
tools:ignore="RtlSymmetry">
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.SearchView>
|
|
||||||
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/list_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_margin="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"
|
||||||
android:nextFocusLeft="@id/main_search"
|
android:src="@drawable/ic_baseline_extension_24"
|
||||||
android:nextFocusRight="@id/main_search"
|
|
||||||
android:src="@drawable/ic_baseline_filter_list_24"
|
|
||||||
app:tint="?attr/textColor" />
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
</FrameLayout>
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:background="@drawable/search_background"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SearchView
|
||||||
|
android:id="@+id/main_search"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
|
||||||
|
android:iconifiedByDefault="false"
|
||||||
|
android:imeOptions="actionSearch"
|
||||||
|
|
||||||
|
android:inputType="text"
|
||||||
|
android:nextFocusLeft="@id/nav_rail_view"
|
||||||
|
|
||||||
|
android:nextFocusRight="@id/search_filter"
|
||||||
|
android:paddingStart="-10dp"
|
||||||
|
app:iconifiedByDefault="false"
|
||||||
|
app:queryBackground="@color/transparent"
|
||||||
|
app:queryHint="@string/search_hint"
|
||||||
|
app:searchIcon="@drawable/search_icon"
|
||||||
|
tools:ignore="RtlSymmetry">
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.SearchView>
|
||||||
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/list_selector"
|
||||||
|
android:layout_width="25dp"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:layout_gravity="end|center_vertical"
|
||||||
|
|
||||||
|
android:layout_margin="10dp"
|
||||||
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/change_providers_img_des"
|
||||||
|
android:nextFocusLeft="@id/main_search"
|
||||||
|
android:nextFocusRight="@id/main_search"
|
||||||
|
android:src="@drawable/ic_baseline_filter_list_24"
|
||||||
|
app:tint="?attr/textColor" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue