From 2a1e0d98a31df4b01660c2478a3088faa4d8ca9a Mon Sep 17 00:00:00 2001 From: LagradOst Date: Sun, 26 Dec 2021 01:05:10 +0100 Subject: [PATCH] change to UI --- .../com/lagradost/cloudstream3/MainAPI.kt | 4 + .../VidstreamProviderTemplate.kt | 11 -- .../cloudstream3/ui/APIRepository.kt | 3 + .../cloudstream3/ui/home/HomeFragment.kt | 166 +++++++++++++++++- .../cloudstream3/ui/search/SearchFragment.kt | 2 +- .../lagradost/cloudstream3/utils/UIHelper.kt | 6 + app/src/main/res/drawable/rounded_dialog.xml | 3 +- .../res/layout/bottom_selection_dialog.xml | 3 +- app/src/main/res/layout/fragment_home.xml | 30 ++-- app/src/main/res/layout/fragment_result.xml | 13 +- .../main/res/layout/home_select_mainpage.xml | 109 ++++++++++++ .../layout/player_select_source_and_subs.xml | 2 + app/src/main/res/layout/provider_list.xml | 2 + app/src/main/res/layout/sort_bottom_sheet.xml | 2 + app/src/main/res/values/styles.xml | 53 +++++- 15 files changed, 355 insertions(+), 54 deletions(-) create mode 100644 app/src/main/res/layout/home_select_mainpage.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 831b4271..0f57343c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -268,6 +268,10 @@ fun parseRating(ratingString: String?): Int? { return (floatRating * 10).toInt() } +fun MainAPI.fixUrlNull(url : String?) : String? { + return fixUrl(url ?: return null) +} + fun MainAPI.fixUrl(url: String): String { if (url.startsWith("http")) { return url diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt index a851e19c..da4aaaa6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/VidstreamProviderTemplate.kt @@ -31,17 +31,6 @@ open class VidstreamProviderTemplate : MainAPI() { // If getMainPage() is functional, used to display the homepage in app, an optional, but highly encouraged endevour. override val hasMainPage = true - // Sometimes on sites the urls can be something like "/movie.html" which translates to "*full site url*/movie.html" in the browser - private fun fixUrl(url: String): String { - return if (url.startsWith("//")) { - "https:$url" - } else if (url.startsWith("/")) { - "$mainUrl$url" - } else { - url - } - } - // Searching returns a SearchResponse, which can be one of the following: AnimeSearchResponse, MovieSearchResponse, TorrentSearchResponse, TvSeriesSearchResponse // Each of the classes requires some different data, but always has some critical things like name, poster and url. override fun search(query: String): ArrayList { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt index 99884f36..0f36a99f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -14,13 +14,16 @@ class APIRepository(val api: MainAPI) { val noneApi = object : MainAPI() { override val name = "None" + override val supportedTypes = emptySet() } val randomApi = object : MainAPI() { override val name = "Random" + override val supportedTypes = emptySet() } val noneRepo = APIRepository(noneApi) } + val hasMainPage: Boolean get() = api.hasMainPage val name: String get() = api.name val mainUrl: String get() = api.mainUrl diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 230afa39..ac63ed41 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.ui.home import android.annotation.SuppressLint import android.app.Activity +import android.content.Context import android.content.Intent import android.content.res.Configuration import android.net.Uri @@ -9,9 +10,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.TextView +import android.widget.* import androidx.core.view.isVisible +import androidx.core.widget.NestedScrollView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.GridLayoutManager @@ -19,6 +20,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.button.MaterialButton import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia @@ -58,6 +60,7 @@ import kotlinx.android.synthetic.main.fragment_home.* import java.util.* const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list" +const val HOME_PREF_HOMEPAGE = "home_pref_homepage" class HomeFragment : Fragment() { companion object { @@ -102,6 +105,126 @@ class HomeFragment : Fragment() { bottomSheetDialogBuilder.show() } + + fun Context.selectHomepage(selectedApiName: String?, callback: (String) -> Unit) { + println("CURRENT $selectedApiName") + val validAPIs = filterProviderByPreferredMedia().toMutableList() + + validAPIs.add(0, randomApi) + validAPIs.add(0, noneApi) + //val builder: AlertDialog.Builder = AlertDialog.Builder(this) + //builder.setView(R.layout.home_select_mainpage) + val builder = + BottomSheetDialog(this) + + builder.setContentView(R.layout.home_select_mainpage) + builder.show() + builder.let { dialog -> + //dialog.window?.setGravity(Gravity.BOTTOM) + + var currentApiName = selectedApiName + + var currentValidApis: MutableList = mutableListOf() + val preSelectedTypes = this.getKey>(HOME_PREF_HOMEPAGE) + ?.mapNotNull { listName -> TvType.values().firstOrNull { it.name == listName } }?.toMutableList() + ?: mutableListOf(TvType.Movie, TvType.TvSeries) + + val anime = dialog.findViewById(R.id.home_select_anime) + val cartoons = dialog.findViewById(R.id.home_select_cartoons) + val tvs = dialog.findViewById(R.id.home_select_tv_series) + val docs = dialog.findViewById(R.id.home_select_documentaries) + val movies = dialog.findViewById(R.id.home_select_movies) + val cancelBtt = dialog.findViewById(R.id.cancel_btt) + val applyBtt = dialog.findViewById(R.id.apply_btt) + + cancelBtt?.setOnClickListener { + dialog.dismissSafe() + } + + applyBtt?.setOnClickListener { + if (currentApiName != selectedApiName) { + currentApiName?.let(callback) + } + dialog.dismissSafe() + } + + val listView = dialog.findViewById(R.id.listview1) + val arrayAdapter = ArrayAdapter(this, R.layout.sort_bottom_single_choice) + listView?.adapter = arrayAdapter + listView?.choiceMode = AbsListView.CHOICE_MODE_SINGLE + + listView?.setOnItemClickListener { _, _, i, _ -> + if (!currentValidApis.isNullOrEmpty()) { + currentApiName = currentValidApis[i].name + //to switch to apply simply remove this + currentApiName?.let(callback) + dialog.dismissSafe() + } + } + + val pairList = listOf( + Pair(anime, listOf(TvType.Anime, TvType.ONA, TvType.AnimeMovie)), + Pair(cartoons, listOf(TvType.Cartoon)), + Pair(tvs, listOf(TvType.TvSeries)), + Pair(docs, listOf(TvType.Documentary)), + Pair(movies, listOf(TvType.Movie, TvType.Torrent)) + ) + + fun updateList() { + this.setKey(HOME_PREF_HOMEPAGE, preSelectedTypes) + + arrayAdapter.clear() + currentValidApis = validAPIs.filter { api -> + api.hasMainPage && api.supportedTypes.any { + preSelectedTypes.contains(it) + } + }.toMutableList() + currentValidApis.addAll(0, validAPIs.subList(0, 2)) + + val names = currentValidApis.map { it.name } + val index = names.indexOf(currentApiName) + println("INDEX: $index") + listView?.setItemChecked(index, true) + arrayAdapter.notifyDataSetChanged() + arrayAdapter.addAll(names) + arrayAdapter.notifyDataSetChanged() + } + + for ((button, validTypes) in pairList) { + val isValid = validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } } + button?.isVisible = isValid + if (isValid) { + fun buttonContains(): Boolean { + return preSelectedTypes.any { validTypes.contains(it) } + } + + button?.isSelected = buttonContains() + button?.setOnClickListener { + preSelectedTypes.clear() + preSelectedTypes.addAll(validTypes) + for ((otherButton, _) in pairList) { + otherButton?.isSelected = false + } + button.isSelected = true + updateList() + } + + button?.setOnLongClickListener { + if (!buttonContains()) { + button.isSelected = true + preSelectedTypes.addAll(validTypes) + } else { + button.isSelected = false + preSelectedTypes.removeAll(validTypes) + } + updateList() + return@setOnLongClickListener true + } + } + } + updateList() + } + } } private val homeViewModel: HomeViewModel by activityViewModels() @@ -131,13 +254,16 @@ class HomeFragment : Fragment() { } private val apiChangeClickListener = View.OnClickListener { view -> - val validAPIs = view.context?.filterProviderByPreferredMedia()?.toMutableList() ?: mutableListOf() + view.context.selectHomepage(currentApiName) { api -> + homeViewModel.loadAndCancel(api) + } + /*val validAPIs = view.context?.filterProviderByPreferredMedia()?.toMutableList() ?: mutableListOf() validAPIs.add(0, randomApi) validAPIs.add(0, noneApi) view.popupMenuNoIconsAndNoStringRes(validAPIs.mapIndexed { index, api -> Pair(index, api.name) }) { homeViewModel.loadAndCancel(validAPIs[itemId].name) - } + }*/ } override fun onConfigurationChanged(newConfig: Configuration) { @@ -170,6 +296,8 @@ class HomeFragment : Fragment() { } }*/ + var currentApiName: String? = null + @SuppressLint("SetTextI18n") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -177,8 +305,10 @@ class HomeFragment : Fragment() { home_change_api.setOnClickListener(apiChangeClickListener) home_change_api_loading.setOnClickListener(apiChangeClickListener) + home_api_fab.setOnClickListener(apiChangeClickListener) observe(homeViewModel.apiName) { apiName -> + currentApiName = apiName setKey(HOMEPAGE_API, apiName) home_provider_name?.text = apiName home_provider_meta_info?.isVisible = false @@ -312,7 +442,11 @@ class HomeFragment : Fragment() { for (item in toggleList) { val watch = item.second - item.first?.setOnClickListener { itemView -> + item.first?.setOnClickListener { + homeViewModel.loadStoredData(EnumSet.of(watch)) + } + + item.first?.setOnLongClickListener { itemView -> val list = EnumSet.noneOf(WatchType::class.java) itemView.context.getKey(HOME_BOOKMARK_VALUE_LIST)?.map { WatchType.fromInternalId(it) }?.let { list.addAll(it) @@ -324,10 +458,6 @@ class HomeFragment : Fragment() { list.add(watch) } homeViewModel.loadStoredData(list) - } - - item.first?.setOnLongClickListener { - homeViewModel.loadStoredData(EnumSet.of(watch)) return@setOnLongClickListener true } } @@ -478,17 +608,35 @@ class HomeFragment : Fragment() { homeViewModel.loadAndCancel(apiName) } + home_loaded.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { view, _, scrollY, _, oldScrollY -> + val dy = scrollY - oldScrollY + if (dy > 0) { //check for scroll down + home_api_fab?.hide() + } else if (dy < -5) { + if (view?.context?.isTvSettings() == false) { + home_api_fab?.show() + } + } + }) + // nice profile pic on homepage home_profile_picture_holder?.isVisible = false context?.let { ctx -> // just in case if (ctx.isTvSettings()) { + home_api_fab?.isVisible = false + home_change_api?.isVisible = true + home_change_api_loading?.isVisible = true home_change_api_loading?.isFocusable = true home_change_api_loading?.isFocusableInTouchMode = true home_change_api?.isFocusable = true home_change_api?.isFocusableInTouchMode = true // home_bookmark_select?.isFocusable = true // home_bookmark_select?.isFocusableInTouchMode = true + } else { + home_api_fab?.isVisible = true + home_change_api?.isVisible = false + home_change_api_loading?.isVisible = false } for (syncApi in OAuth2API.OAuth2Apis) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index c09fd9c5..5b2f704e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -118,7 +118,7 @@ class SearchFragment : Fragment() { if (apiNamesSetting != null && langs != null) { val apiNames = apis.filter { langs.contains(it.lang) }.map { it.name } val builder = - AlertDialog.Builder(searchView.context, R.style.AlertDialogCustom).setView(R.layout.provider_list) + AlertDialog.Builder(searchView.context).setView(R.layout.provider_list) val dialog = builder.create() dialog.show() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index bbfa328e..304e3bd8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -345,6 +345,12 @@ object UIHelper { } } + fun Dialog?.dismissSafe() { + if (this?.isShowing == true) { + this.dismiss() + } + } + /**id, stringRes */ @SuppressLint("RestrictedApi") fun View.popupMenuNoIcons( diff --git a/app/src/main/res/drawable/rounded_dialog.xml b/app/src/main/res/drawable/rounded_dialog.xml index d3b48839..a5391a87 100644 --- a/app/src/main/res/drawable/rounded_dialog.xml +++ b/app/src/main/res/drawable/rounded_dialog.xml @@ -1,8 +1,7 @@ - + - diff --git a/app/src/main/res/layout/bottom_selection_dialog.xml b/app/src/main/res/layout/bottom_selection_dialog.xml index 7c05e8e2..c23cf7f2 100644 --- a/app/src/main/res/layout/bottom_selection_dialog.xml +++ b/app/src/main/res/layout/bottom_selection_dialog.xml @@ -4,7 +4,6 @@ xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" - android:background="?attr/primaryBlackBackground" android:layout_height="match_parent"> - + @@ -484,4 +484,12 @@ /> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index 2ccd5776..50ee60a1 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -654,20 +654,9 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/player_select_source_and_subs.xml b/app/src/main/res/layout/player_select_source_and_subs.xml index c7f26a06..d30de1f9 100644 --- a/app/src/main/res/layout/player_select_source_and_subs.xml +++ b/app/src/main/res/layout/player_select_source_and_subs.xml @@ -39,6 +39,7 @@ android:id="@+id/sort_providers" android:background="?attr/primaryBlackBackground" + android:requiresFadingEdge="vertical" tools:listitem="@layout/sort_bottom_single_choice" android:layout_width="match_parent" android:layout_height="match_parent" @@ -95,6 +96,7 @@ android:id="@+id/sort_subtitles" android:background="?attr/primaryBlackBackground" + android:requiresFadingEdge="vertical" tools:listitem="@layout/sort_bottom_single_choice" android:layout_width="match_parent" android:layout_rowWeight="1" diff --git a/app/src/main/res/layout/provider_list.xml b/app/src/main/res/layout/provider_list.xml index 4300b8d8..07c2978d 100644 --- a/app/src/main/res/layout/provider_list.xml +++ b/app/src/main/res/layout/provider_list.xml @@ -40,6 +40,7 @@ android:layout_marginTop="-10dp" android:layout_marginBottom="60dp" android:paddingTop="10dp" + android:requiresFadingEdge="vertical" tools:listitem="@layout/sort_bottom_single_choice" android:layout_width="match_parent" android:layout_height="match_parent" @@ -56,6 +57,7 @@ android:layout_marginTop="-10dp" android:layout_marginBottom="60dp" android:paddingTop="10dp" + android:requiresFadingEdge="vertical" tools:listitem="@layout/sort_bottom_single_choice" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/app/src/main/res/layout/sort_bottom_sheet.xml b/app/src/main/res/layout/sort_bottom_sheet.xml index 7b13146f..38cf7f7b 100644 --- a/app/src/main/res/layout/sort_bottom_sheet.xml +++ b/app/src/main/res/layout/sort_bottom_sheet.xml @@ -30,6 +30,7 @@ android:paddingTop="10dp" android:id="@+id/sort_providers" android:background="?attr/primaryBlackBackground" + android:requiresFadingEdge="vertical" tools:listitem="@layout/sort_bottom_single_choice" android:layout_width="match_parent" android:layout_height="match_parent" @@ -60,6 +61,7 @@ android:paddingTop="10dp" android:id="@+id/sort_subtitles" android:background="?attr/primaryBlackBackground" + android:requiresFadingEdge="vertical" tools:listitem="@layout/sort_bottom_single_choice" android:layout_width="match_parent" android:layout_rowWeight="1" diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 790e6b83..1435f531 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -22,6 +22,11 @@ @style/AppBottomSheetDialogTheme @style/AppSearchViewStyle @style/Theme.Widget.Tabs + @color/transparent + @color/transparent + 0dp + none + @style/ListViewStyle @style/CustomCastExpandedController @@ -57,6 +62,10 @@ #FFF + + @@ -160,6 +169,7 @@ ?attr/primaryGrayBackground ?attr/primaryBlackBackground + - - - @@ -305,7 +322,31 @@ ?attr/textColor ?attr/textColor - + + + + +