From 997420a942bd643f17d0fb00d221af9a1faa2ef4 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Fri, 28 Oct 2022 03:51:27 +0200 Subject: [PATCH] phone UI changes + cache --- .../cloudstream3/ui/APIRepository.kt | 17 +- .../lagradost/cloudstream3/ui/WatchType.kt | 13 +- .../cloudstream3/ui/home/HomeFragment.kt | 235 ++--- .../cloudstream3/ui/home/HomeViewModel.kt | 39 +- .../ui/result/ResultViewModel2.kt | 47 +- .../cloudstream3/ui/search/SearchFragment.kt | 241 ++---- .../ui/settings/extensions/PluginsFragment.kt | 59 +- app/src/main/res/color/chip_color.xml | 5 + app/src/main/res/layout/activity_main.xml | 118 +-- app/src/main/res/layout/fragment_home.xml | 814 +++++++++--------- app/src/main/res/layout/fragment_plugins.xml | 103 +-- app/src/main/res/layout/fragment_search.xml | 81 +- .../main/res/layout/home_select_mainpage.xml | 156 +--- app/src/main/res/layout/tvtypes_chips.xml | 68 ++ .../main/res/layout/tvtypes_chips_scroll.xml | 10 + app/src/main/res/values/styles.xml | 4 + 16 files changed, 905 insertions(+), 1105 deletions(-) create mode 100644 app/src/main/res/color/chip_color.xml create mode 100644 app/src/main/res/layout/tvtypes_chips.xml create mode 100644 app/src/main/res/layout/tvtypes_chips_scroll.xml 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 0e5e544b..ce695873 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -26,6 +26,8 @@ class APIRepository(val api: MainAPI) { fun isInvalidData(data: String): Boolean { return data.isEmpty() || data == "[]" || data == "about:blank" } + + private val cacheHash: HashMap, LoadResponse> = hashMapOf() } val hasMainPage = api.hasMainPage @@ -39,7 +41,13 @@ class APIRepository(val api: MainAPI) { suspend fun load(url: String): Resource { return safeApiCall { if (isInvalidData(url)) throw ErrorLoadingException() - api.load(api.fixUrl(url)) ?: throw ErrorLoadingException() + val fixedUrl = api.fixUrl(url) + val key = Pair(api.name,url) + cacheHash[key] ?: api.load(fixedUrl)?.also { + // we cache 20 responses because ppl often go back to the same shit + 20 because I dont want to cause too much memory leak + if (cacheHash.size > 20) cacheHash.remove(cacheHash.keys.random()) + cacheHash[key] = it + } ?: throw ErrorLoadingException() } } @@ -75,7 +83,12 @@ class APIRepository(val api: MainAPI) { api.lastHomepageRequest = unixTimeMS nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data -> - listOf(api.getMainPage(page, MainPageRequest(data.name, data.data, data.horizontalImages))) + listOf( + api.getMainPage( + page, + MainPageRequest(data.name, data.data, data.horizontalImages) + ) + ) } ?: run { if (api.sequentialMainPage) { var first = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt index 417b4408..eb4eb666 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt @@ -5,13 +5,12 @@ import androidx.annotation.StringRes import com.lagradost.cloudstream3.R enum class WatchType(val internalId: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) { - // FIX ICONS - WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_remove_red_eye_24), - COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_check_24), - ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_pause_24), - DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_close_24), - PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_close_24), - NONE(5, R.string.type_none, R.drawable.ic_baseline_remove_red_eye_24); + WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_bookmark_24), + COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_bookmark_24), + ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_bookmark_24), + DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_bookmark_24), + PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_bookmark_24), + NONE(5, R.string.type_none, R.drawable.ic_baseline_add_24); companion object { fun fromInternalId(id: Int?) = values().find { value -> value.internalId == id } ?: NONE 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 0d571b76..2f539a33 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 @@ -14,6 +14,8 @@ import android.view.ViewGroup import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getDrawable import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.widget.NestedScrollView @@ -26,11 +28,14 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipGroup import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings +import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent @@ -43,6 +48,7 @@ import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.AutofitRecyclerView import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment +import com.lagradost.cloudstream3.ui.result.ResultViewModel2.Companion.updateWatchStatus import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.search.* @@ -50,6 +56,7 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallba import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable +import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.setMaxViewPoolSize import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -61,11 +68,11 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState import com.lagradost.cloudstream3.utils.Event +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar -import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.setImage @@ -82,14 +89,12 @@ import kotlinx.android.synthetic.main.fragment_home.home_loading import kotlinx.android.synthetic.main.fragment_home.home_loading_error import kotlinx.android.synthetic.main.fragment_home.home_loading_shimmer import kotlinx.android.synthetic.main.fragment_home.home_loading_statusbar -import kotlinx.android.synthetic.main.fragment_home.home_main_poster_recyclerview import kotlinx.android.synthetic.main.fragment_home.home_master_recycler import kotlinx.android.synthetic.main.fragment_home.home_plan_to_watch_btt import kotlinx.android.synthetic.main.fragment_home.home_provider_meta_info import kotlinx.android.synthetic.main.fragment_home.home_provider_name import kotlinx.android.synthetic.main.fragment_home.home_reload_connection_open_in_browser import kotlinx.android.synthetic.main.fragment_home.home_reload_connectionerror -import kotlinx.android.synthetic.main.fragment_home.home_statusbar import kotlinx.android.synthetic.main.fragment_home.home_type_completed_btt import kotlinx.android.synthetic.main.fragment_home.home_type_dropped_btt import kotlinx.android.synthetic.main.fragment_home.home_type_on_hold_btt @@ -100,6 +105,8 @@ import kotlinx.android.synthetic.main.fragment_home.home_watch_parent_item_title import kotlinx.android.synthetic.main.fragment_home.result_error_text import kotlinx.android.synthetic.main.fragment_home_tv.* import kotlinx.android.synthetic.main.home_episodes_expanded.* +import kotlinx.android.synthetic.main.tvtypes_chips.* +import kotlinx.android.synthetic.main.tvtypes_chips.view.* import java.util.* const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list" @@ -247,16 +254,16 @@ class HomeFragment : Fragment() { } fun getPairList( - anime: MaterialButton?, - cartoons: MaterialButton?, - tvs: MaterialButton?, - docs: MaterialButton?, - movies: MaterialButton?, - asian: MaterialButton?, - livestream: MaterialButton?, - nsfw: MaterialButton?, - others: MaterialButton?, - ): List>> { + anime: Chip?, + cartoons: Chip?, + tvs: Chip?, + docs: Chip?, + movies: Chip?, + asian: Chip?, + livestream: Chip?, + nsfw: Chip?, + others: Chip?, + ): List>> { // This list should be same order as home screen to aid navigation return listOf( Pair(movies, listOf(TvType.Movie, TvType.Torrent)), @@ -271,6 +278,59 @@ class HomeFragment : Fragment() { ) } + private fun getPairList(header: ChipGroup) = getPairList( + header.home_select_anime, + header.home_select_cartoons, + header.home_select_tv_series, + header.home_select_documentaries, + header.home_select_movies, + header.home_select_asian, + header.home_select_livestreams, + header.home_select_nsfw, + header.home_select_others + ) + + fun validateChips(header: ChipGroup?, validTypes: List) { + if (header == null) return + val pairList = getPairList(header) + for ((button, types) in pairList) { + val isValid = validTypes.any { types.contains(it) } + button?.isVisible = isValid + } + } + + fun updateChips(header: ChipGroup?, selectedTypes: List) { + if (header == null) return + val pairList = getPairList(header) + for ((button, types) in pairList) { + button?.isChecked = + button?.isVisible == true && selectedTypes.any { types.contains(it) } + } + } + + fun bindChips( + header: ChipGroup?, + selectedTypes: List, + validTypes: List, + callback: (List) -> Unit + ) { + if (header == null) return + val pairList = getPairList(header) + for ((button, types) in pairList) { + val isValid = validTypes.any { types.contains(it) } + button?.isVisible = isValid + button?.isChecked = isValid && selectedTypes.any { types.contains(it) } + button?.setOnCheckedChangeListener { _, _ -> + val list = ArrayList() + for ((sbutton, vvalidTypes) in pairList) { + if (sbutton?.isChecked == true) + list.addAll(vvalidTypes) + } + callback(list) + } + } + } + fun Context.selectHomepage(selectedApiName: String?, callback: (String) -> Unit) { val validAPIs = filterProviderByPreferredMedia().toMutableList() @@ -296,21 +356,9 @@ class HomeFragment : Fragment() { ?.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 asian = dialog.findViewById(R.id.home_select_asian) - val livestream = dialog.findViewById(R.id.home_select_livestreams) - val nsfw = dialog.findViewById(R.id.home_select_nsfw) - val others = dialog.findViewById(R.id.home_select_others) val cancelBtt = dialog.findViewById(R.id.cancel_btt) val applyBtt = dialog.findViewById(R.id.apply_btt) - val pairList = - getPairList(anime, cartoons, tvs, docs, movies, asian, livestream, nsfw, others) - cancelBtt?.setOnClickListener { dialog.dismissSafe() } @@ -355,52 +403,14 @@ class HomeFragment : Fragment() { arrayAdapter.notifyDataSetChanged() } - /** - * Since fire tv is fucked we need to manually define the focus layout. - * Since visible buttons are only known in runtime this is required. - **/ - var lastButton: MaterialButton? = null - - for ((button, validTypes) in pairList) { - val isValid = - validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } } - button?.isVisible = isValid - if (isValid) { - - // Set focus navigation - button?.let { currentButton -> - lastButton?.nextFocusRightId = currentButton.id - lastButton?.id?.let { currentButton.nextFocusLeftId = it } - lastButton = currentButton - } - - 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 - } - } + bindChips( + dialog.home_select_group, + preSelectedTypes, + validAPIs.flatMap { it.supportedTypes }.distinct() + ) { list -> + preSelectedTypes.clear() + preSelectedTypes.addAll(list) + updateList() } updateList() } @@ -422,7 +432,6 @@ class HomeFragment : Fragment() { } private fun toggleMainVisibility(visible: Boolean) { - home_main_holder?.isVisible = visible home_main_poster_recyclerview?.isVisible = visible } @@ -531,6 +540,51 @@ class HomeFragment : Fragment() { home_random?.visibility = View.GONE } + observe(homeViewModel.preview) { preview -> + when (preview) { + is Resource.Success -> { + home_preview?.isVisible = true + preview.value.apply { + home_preview_tags?.text = tags?.joinToString(" • ") ?: "" + home_preview_tags?.isGone = tags.isNullOrEmpty() + home_preview_image?.setImage(posterUrl, posterHeaders) + home_preview_title?.text = name + home_preview_play?.setOnClickListener { + activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST) + //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST) + } + home_preview_info?.setOnClickListener { + activity?.loadResult(url, apiName) + //activity.loadSearchResult(random) + } + // very ugly code, but I dont care + val watchType = DataStoreHelper.getResultWatchState(preview.value.getId()) + home_preview_bookmark?.setText(watchType.stringRes) + home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, watchType.iconRes),null,null) + home_preview_bookmark?.setOnClickListener { fab -> + activity?.showBottomDialog( + WatchType.values().map { fab.context.getString(it.stringRes) } + .toList(), + DataStoreHelper.getResultWatchState(preview.value.getId()).ordinal, + fab.context.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + val newValue = WatchType.values()[it] + home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, newValue.iconRes),null,null) + home_preview_bookmark?.setText(newValue.stringRes) + + updateWatchStatus(preview.value, newValue) + reloadStored() + } + } + } + } + else -> { + home_preview?.isVisible = false + } + } + } + observe(homeViewModel.apiName) { apiName -> currentApiName = apiName // setKey(USER_SELECTED_HOMEPAGE_API, apiName) @@ -563,17 +617,17 @@ class HomeFragment : Fragment() { HomeChildItemAdapter( mutableListOf(), R.layout.home_result_big_grid, - nextFocusUp = home_main_poster_recyclerview.nextFocusUpId, - nextFocusDown = home_main_poster_recyclerview.nextFocusDownId + nextFocusUp = home_main_poster_recyclerview?.nextFocusUpId, + nextFocusDown = home_main_poster_recyclerview?.nextFocusDownId ) { callback -> homeHandleSearch(callback) } - home_main_poster_recyclerview.setLinearListLayout() + home_main_poster_recyclerview?.setLinearListLayout() observe(homeViewModel.randomItems) { items -> if (items.isNullOrEmpty()) { toggleMainVisibility(false) } else { - val tempAdapter = home_main_poster_recyclerview.adapter as HomeChildItemAdapter? + val tempAdapter = home_main_poster_recyclerview?.adapter as? HomeChildItemAdapter? // no need to reload if it has the same data if (tempAdapter != null && tempAdapter.cardList == items) { toggleMainVisibility(true) @@ -938,7 +992,7 @@ class HomeFragment : Fragment() { } } - context?.fixPaddingStatusbarView(home_statusbar) + //context?.fixPaddingStatusbarView(home_statusbar) context?.fixPaddingStatusbar(home_loading_statusbar) home_master_recycler.adapter = @@ -959,33 +1013,6 @@ class HomeFragment : Fragment() { } } // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() } - if (!isTvSettings()) { - LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap - val centerLayoutManager = - CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - centerLayoutManager.setOnSizeListener { index -> - (home_main_poster_recyclerview?.adapter as HomeChildItemAdapter?)?.cardList?.get( - index - )?.let { random -> - home_main_play?.setOnClickListener { - activity.loadSearchResult(random, START_ACTION_RESUME_LATEST) - } - home_main_info?.setOnClickListener { - activity.loadSearchResult(random) - } - - home_main_text?.text = - random.name + if (random is AnimeSearchResponse && !random.dubStatus.isNullOrEmpty()) { - random.dubStatus?.joinToString( - prefix = " • ", - separator = " | " - ) { it.name } - } else "" - } - } - home_main_poster_recyclerview?.layoutManager = centerLayoutManager // scale - } - reloadStored() loadHomePage() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt index 30fd45c1..0b928a55 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt @@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.HomePageList +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.mvvm.* @@ -32,7 +33,6 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.VideoDownloadHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.* import kotlin.collections.set @@ -58,7 +58,9 @@ class HomeViewModel : ViewModel() { val bookmarks: LiveData>> = _bookmarks private val _resumeWatching = MutableLiveData>() + private val _preview = MutableLiveData>() val resumeWatching: LiveData> = _resumeWatching + val preview: LiveData> = _preview fun loadResumeWatching() = viewModelScope.launchSafe { val resumeWatching = withContext(Dispatchers.IO) { @@ -207,6 +209,7 @@ class HomeViewModel : ViewModel() { expandAndReturn(name) } + private fun load(api: MainAPI?) = viewModelScope.launchSafe { repo = if (api != null) { APIRepository(api) @@ -219,6 +222,7 @@ class HomeViewModel : ViewModel() { if (repo?.hasMainPage == true) { _page.postValue(Resource.Loading()) + _preview.postValue(Resource.Loading()) when (val data = repo?.getMainPage(1, null)) { is Resource.Success -> { @@ -232,8 +236,38 @@ class HomeViewModel : ViewModel() { ExpandableHomepageList(filteredList, 1, home.hasNext) } } - _page.postValue(Resource.Success(expandable)) val items = data.value.mapNotNull { it?.items }.flatten() + items.randomOrNull()?.list?.randomOrNull()?.url?.let { url -> + // backup request in case first fails + var first = repo?.load(url) + if(first == null ||first is Resource.Failure) { + first = repo?.load(items.random().list.random().url) + } + first?.let { + _preview.postValue(it) + } ?: run { + _preview.postValue( + Resource.Failure( + false, + null, + null, + "No repo found, this should never happen" + ) + ) + } + } ?: run { + _preview.postValue( + Resource.Failure( + false, + null, + null, + "No homepage items" + ) + ) + } + + _page.postValue(Resource.Success(expandable)) + //val home = data.value if (items.isNotEmpty()) { @@ -263,6 +297,7 @@ class HomeViewModel : ViewModel() { } } else { _page.postValue(Resource.Success(emptyMap())) + _preview.postValue(Resource.Failure(false, null, null, "No homepage")) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 906b652d..7c099793 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -412,6 +412,29 @@ class ResultViewModel2 : ViewModel() { return this?.firstOrNull { it.season == season } } + fun updateWatchStatus(currentResponse : LoadResponse, status: WatchType) { + val currentId = currentResponse.getId() + val resultPage = currentResponse + + DataStoreHelper.setResultWatchState(currentId, status.internalId) + val current = DataStoreHelper.getBookmarkedData(currentId) + val currentTime = System.currentTimeMillis() + DataStoreHelper.setBookmarkedData( + currentId, + DataStoreHelper.BookmarkedData( + currentId, + current?.bookmarkedTime ?: currentTime, + currentTime, + resultPage.name, + resultPage.url, + resultPage.apiName, + resultPage.type, + resultPage.posterUrl, + resultPage.year + ) + ) + } + private fun filterName(name: String?): String? { if (name == null) return null Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let { @@ -764,28 +787,10 @@ class ResultViewModel2 : ViewModel() { private val _selectPopup: MutableLiveData> = MutableLiveData(Some.None) val selectPopup: LiveData> get() = _selectPopup - fun updateWatchStatus(status: WatchType) { - val currentId = currentId ?: return - val resultPage = currentResponse ?: return - _watchStatus.postValue(status) - DataStoreHelper.setResultWatchState(currentId, status.internalId) - val current = DataStoreHelper.getBookmarkedData(currentId) - val currentTime = System.currentTimeMillis() - DataStoreHelper.setBookmarkedData( - currentId, - DataStoreHelper.BookmarkedData( - currentId, - current?.bookmarkedTime ?: currentTime, - currentTime, - resultPage.name, - resultPage.url, - resultPage.apiName, - resultPage.type, - resultPage.posterUrl, - resultPage.year - ) - ) + fun updateWatchStatus(status: WatchType) { + updateWatchStatus(currentResponse ?: return,status) + _watchStatus.postValue(status) } private fun startChromecast( 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 5f108cd1..8f588bf7 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 @@ -26,15 +26,19 @@ import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiSettings +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.home.HomeFragment +import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList +import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips import com.lagradost.cloudstream3.ui.home.ParentItemAdapter import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.utils.Coroutines.main @@ -46,6 +50,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import kotlinx.android.synthetic.main.fragment_search.* +import kotlinx.android.synthetic.main.tvtypes_chips.* import java.util.concurrent.locks.ReentrantLock const val SEARCH_PREF_TAGS = "search_pref_tags" @@ -130,65 +135,26 @@ class SearchFragment : Fragment() { // Null if defined as a variable // This needs to be run after view created - private fun getPairList(): List>> { - return HomeFragment.getPairList( - search_select_anime, - search_select_cartoons, - search_select_tv_series, - search_select_documentaries, - search_select_movies, - search_select_asian, - search_select_livestreams, - search_select_nsfw, - search_select_others - ) - } private fun reloadRepos(success: Boolean = false) = main { - val pairList = getPairList() - searchViewModel.reloadRepos() context?.filterProviderByPreferredMedia()?.let { validAPIs -> - 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 selectedSearchTypes.any { validTypes.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - val last = selectedSearchTypes.toSet() - selectedSearchTypes.clear() - selectedSearchTypes.addAll(validTypes) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes) - it?.isSelected = true - if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing - search(main_search?.query?.toString()) - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - it?.isSelected = true - selectedSearchTypes.addAll(validTypes) - } else { - it?.isSelected = false - selectedSearchTypes.removeAll(validTypes) - } - it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes) - search(main_search?.query?.toString()) - return@setOnLongClickListener true - } + bindChips( + home_select_group, + selectedSearchTypes, + validAPIs.flatMap { api -> api.supportedTypes }.distinct() + ) { list -> + if (selectedSearchTypes.toSet() != list.toSet()) { + setKey(SEARCH_PREF_TAGS, selectedSearchTypes) + selectedSearchTypes.clear() + selectedSearchTypes.addAll(list) + search(main_search?.query?.toString()) } } } } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -239,31 +205,67 @@ class SearchFragment : Fragment() { builder.let { dialog -> val isMultiLang = ctx.getApiProviderLangSettings().size > 1 - 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 asian = dialog.findViewById(R.id.home_select_asian) - val livestream = - dialog.findViewById(R.id.home_select_livestreams) - val nsfw = dialog.findViewById(R.id.home_select_nsfw) - val other = dialog.findViewById(R.id.home_select_others) val cancelBtt = dialog.findViewById(R.id.cancel_btt) val applyBtt = dialog.findViewById(R.id.apply_btt) - val pairList = - HomeFragment.getPairList( - anime, - cartoons, - tvs, - docs, - movies, - asian, - livestream, - nsfw, - other - ) + val listView = dialog.findViewById(R.id.listview1) + val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) + listView?.adapter = arrayAdapter + listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE + + listView?.setOnItemClickListener { _, _, i, _ -> + if (currentValidApis.isNotEmpty()) { + val api = currentValidApis[i].name + if (currentSelectedApis.contains(api)) { + listView.setItemChecked(i, false) + currentSelectedApis -= api + } else { + listView.setItemChecked(i, true) + currentSelectedApis += api + } + } + } + + fun updateList(types: List) { + setKey(SEARCH_PREF_TAGS, types.map {it.name}) + + arrayAdapter.clear() + currentValidApis = validAPIs.filter { api -> + api.supportedTypes.any { + types.contains(it) + } + }.sortedBy { it.name.lowercase() } + + val names = currentValidApis.map { + if (isMultiLang) "${ + SubtitleHelper.getFlagFromIso( + it.lang + )?.plus(" ") ?: "" + }${it.name}" else it.name + } + for ((index, api) in currentValidApis.map { it.name }.withIndex()) { + listView?.setItemChecked(index, currentSelectedApis.contains(api)) + } + + //arrayAdapter.notifyDataSetChanged() + arrayAdapter.addAll(names) + arrayAdapter.notifyDataSetChanged() + } + + val selectedSearchTypes = getKey>(SEARCH_PREF_TAGS) + ?.mapNotNull { listName -> + TvType.values().firstOrNull { it.name == listName } + } + ?.toMutableList() + ?: mutableListOf(TvType.Movie, TvType.TvSeries) + + bindChips( + dialog.home_select_group, + selectedSearchTypes, + TvType.values().toList() + ) { list -> + updateList(list) + } cancelBtt?.setOnClickListener { dialog.dismissSafe() @@ -284,90 +286,7 @@ class SearchFragment : Fragment() { context?.setKey(SEARCH_PREF_PROVIDERS, currentSelectedApis.toList()) selectedApis = currentSelectedApis } - - val selectedSearchTypes = context?.getKey>(SEARCH_PREF_TAGS) - ?.mapNotNull { listName -> - TvType.values().firstOrNull { it.name == listName } - } - ?.toMutableList() - ?: mutableListOf(TvType.Movie, TvType.TvSeries) - - val listView = dialog.findViewById(R.id.listview1) - val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) - listView?.adapter = arrayAdapter - listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE - - listView?.setOnItemClickListener { _, _, i, _ -> - if (currentValidApis.isNotEmpty()) { - val api = currentValidApis[i].name - if (currentSelectedApis.contains(api)) { - listView.setItemChecked(i, false) - currentSelectedApis -= api - } else { - listView.setItemChecked(i, true) - currentSelectedApis += api - } - } - } - - fun updateList() { - arrayAdapter.clear() - currentValidApis = validAPIs.filter { api -> - api.supportedTypes.any { - selectedSearchTypes.contains(it) - } - }.sortedBy { it.name.lowercase() } - - val names = currentValidApis.map { - if (isMultiLang) "${ - SubtitleHelper.getFlagFromIso( - it.lang - )?.plus(" ") ?: "" - }${it.name}" else it.name - } - for ((index, api) in currentValidApis.map { it.name }.withIndex()) { - listView?.setItemChecked(index, currentSelectedApis.contains(api)) - } - - //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 selectedSearchTypes.any { validTypes.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - selectedSearchTypes.clear() - selectedSearchTypes.addAll(validTypes) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - button.isSelected = true - updateList() - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - button.isSelected = true - selectedSearchTypes.addAll(validTypes) - } else { - button.isSelected = false - selectedSearchTypes.removeAll(validTypes) - } - updateList() - return@setOnLongClickListener true - } - } - } - updateList() + updateList(selectedSearchTypes.toList()) } } } @@ -380,14 +299,6 @@ class SearchFragment : Fragment() { ?.toMutableList() ?: mutableListOf(TvType.Movie, TvType.TvSeries) - val pairList = getPairList() - fun updateSelectedList(list: MutableList) { - selectedSearchTypes = list - for ((button, validTypes) in pairList) { - button?.isSelected = selectedSearchTypes.any { validTypes.contains(it) } - } - } - if (isTrueTvSettings()) { search_filter.isFocusable = true search_filter.isFocusableInTouchMode = true @@ -500,7 +411,7 @@ class SearchFragment : Fragment() { SEARCH_HISTORY_OPEN -> { searchViewModel.clearSearch() if (searchItem.type.isNotEmpty()) - updateSelectedList(searchItem.type.toMutableList()) + updateChips(home_select_group, searchItem.type.toMutableList()) main_search?.setQuery(searchItem.searchText, true) } SEARCH_HISTORY_REMOVE -> { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index aa302c5a..bacd26c8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -9,8 +9,9 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.observe -import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList +import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.appLanguages @@ -18,6 +19,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.fragment_plugins.* +import kotlinx.android.synthetic.main.tvtypes_chips.* +import kotlinx.android.synthetic.main.tvtypes_chips_scroll.* const val PLUGINS_BUNDLE_NAME = "name" const val PLUGINS_BUNDLE_URL = "url" @@ -145,56 +148,10 @@ class PluginsFragment : Fragment() { pluginViewModel.updatePluginList(context, url) tv_types_scroll_view?.isVisible = true - // 💀💀💀💀💀💀💀 Recyclerview when - val pairList = getPairList( - home_select_anime, - home_select_cartoons, - home_select_tv_series, - home_select_documentaries, - home_select_movies, - home_select_asian, - home_select_livestreams, - home_select_nsfw, - home_select_others - ) - -// val supportedTypes: Array = -// pluginViewModel.filteredPlugins.value!!.second.flatMap { it -> it.plugin.second.tvTypes ?: listOf("Other") }.distinct().toTypedArray() - - // Copy pasted code - for ((button, validTypes) in pairList) { - val validTypesMapped = validTypes.map { it.name } - val isValid = true - //validTypes.any { it -> supportedTypes.contains(it.name) } - button?.isVisible = isValid - if (isValid) { - fun buttonContains(): Boolean { - return pluginViewModel.tvTypes.any { validTypesMapped.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - pluginViewModel.tvTypes.clear() - pluginViewModel.tvTypes.addAll(validTypesMapped) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - button.isSelected = true - pluginViewModel.updateFilteredPlugins() - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - button.isSelected = true - pluginViewModel.tvTypes.addAll(validTypesMapped) - } else { - button.isSelected = false - pluginViewModel.tvTypes.removeAll(validTypesMapped) - } - pluginViewModel.updateFilteredPlugins() - return@setOnLongClickListener true - } - } + bindChips(home_select_group, emptyList(), TvType.values().toList()) { list -> + pluginViewModel.tvTypes.clear() + pluginViewModel.tvTypes.addAll(list.map { it.name }) + pluginViewModel.updateFilteredPlugins() } } } diff --git a/app/src/main/res/color/chip_color.xml b/app/src/main/res/color/chip_color.xml new file mode 100644 index 00000000..f4190551 --- /dev/null +++ b/app/src/main/res/color/chip_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f8ca8cbd..4905d454 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,79 +1,83 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/homeRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|navigation" + android:paddingTop="0dp"> + android:layout_width="match_parent" + android:layout_height="match_parent"> + app:itemTextColor="@color/item_select_color" + app:labelVisibilityMode="unlabeled" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:menu="@menu/bottom_nav_menu" + app:menuGravity="center"> + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:menu="@menu/bottom_nav_menu" /> + android:id="@+id/nav_host_fragment" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="0dp" + android:layout_height="0dp" + app:defaultNavHost="true" + app:layout_constraintBottom_toTopOf="@+id/cast_mini_controller_holder" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/nav_rail_view" + app:layout_constraintTop_toTopOf="parent" + app:navGraph="@navigation/mobile_navigation" /> + android:id="@+id/cast_mini_controller_holder" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintBottom_toTopOf="@+id/nav_view" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/nav_rail_view" + tools:layout_height="100dp"> + android:id="@+id/cast_mini_controller" + class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + + app:castControlButtons="@array/cast_mini_controller_control_buttons" + app:customCastBackgroundColor="?attr/primaryGrayBackground" + tools:ignore="FragmentTagUsage" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 4abdef0b..9d430e3d 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,87 +1,87 @@ + android:layout_height="match_parent" + tools:context=".ui.home.HomeFragment"> + android:id="@+id/home_loading" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + tools:visibility="gone"> + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_gravity="center" + android:visibility="gone" + tools:visibility="gone" /> + android:id="@+id/home_loading_shimmer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:layout_marginTop="15dp" + android:orientation="vertical" + android:paddingTop="40dp" + app:shimmer_auto_start="true" + app:shimmer_base_alpha="0.2" + app:shimmer_duration="@integer/loading_time" + app:shimmer_highlight_alpha="0.3"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + android:layout_width="125dp" + android:layout_height="200dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + android:translationX="-164dp" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_width="148dp" + android:layout_height="234dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_width="125dp" + android:layout_height="200dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + android:translationX="164dp" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_marginEnd="@dimen/result_padding" + android:orientation="vertical"> @@ -93,168 +93,170 @@ + android:id="@+id/home_loading_statusbar" + android:layout_width="match_parent" + android:layout_height="70dp"> + android:layout_margin="10dp" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/home_change_provider_img_des" + android:src="@drawable/ic_baseline_keyboard_arrow_down_24" /> + + + android:layout_margin="5dp" + android:minWidth="200dp" + android:text="@string/reload_error" + app:icon="@drawable/ic_baseline_autorenew_24" /> - - + android:layout_gravity="center" + android:layout_margin="5dp" + android:minWidth="200dp" + android:text="@string/result_open_in_browser" + app:icon="@drawable/ic_baseline_public_24" /> + android:id="@+id/result_error_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_margin="5dp" + android:gravity="center" + android:textColor="?attr/textColor" /> + android:id="@+id/home_loaded" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/primaryBlackBackground" + android:visibility="gone" + tools:visibility="visible"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:id="@+id/home_statusbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/primaryGrayBackground" /> + android:id="@+id/home_settings_bar" + + android:layout_width="match_parent" + android:layout_height="70dp" + android:background="?attr/primaryGrayBackground" + android:visibility="gone"> + android:paddingTop="10dp" + android:paddingEnd="10dp" + android:paddingBottom="10dp"> + android:id="@+id/home_profile_picture_holder" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_vertical" + android:layout_marginEnd="10dp" + app:cardCornerRadius="100dp"> + android:id="@+id/home_profile_picture" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="centerCrop" + tools:ignore="ContentDescription" /> + android:layout_width="match_parent" + android:layout_height="40dp" + android:layout_gravity="center_vertical" + android:background="@drawable/search_background" + android:visibility="visible"> + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:iconifiedByDefault="false" + android:paddingStart="-10dp" + app:iconifiedByDefault="false" + app:queryBackground="@color/transparent" + app:queryHint="@string/search_hint" + app:searchIcon="@drawable/search_icon" + tools:ignore="RtlSymmetry" /> + + + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center_vertical" + android:textColor="?attr/textColor" + android:textSize="20sp" + + tools:text="Hello World" /> - - + android:id="@+id/home_provider_meta_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center_vertical" + android:textColor="?attr/grayTextColor" + android:textSize="14sp" + tools:text="Hello World" /> @@ -278,273 +280,309 @@ --> - + + + android:layout_height="match_parent" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> - - - - - - - - - - - - - + android:layout_height="300dp" + android:layout_gravity="bottom" + android:background="@drawable/background_shadow" /> + + + + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:iconifiedByDefault="false" + android:paddingStart="-10dp" + app:iconifiedByDefault="false" + + app:queryBackground="@color/transparent" + app:queryHint="@string/search_hint" + + app:searchIcon="@drawable/search_icon" + tools:ignore="RtlSymmetry" /> + + + + + + + + + + + + + + + + + + + + + android:id="@+id/home_watch_parent_item_title" + style="@style/WatchHeaderText" + android:layout_gravity="center_vertical" + android:text="@string/continue_watching" /> + android:layout_width="30dp" + android:layout_height="match_parent" + android:layout_gravity="end|center_vertical" + android:layout_marginEnd="5dp" + android:contentDescription="@string/home_more_info" + android:src="@drawable/ic_baseline_arrow_forward_24" + app:tint="?attr/textColor" /> + android:id="@+id/home_watch_child_recyclerview" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clipToPadding="false" + android:descendantFocusability="afterDescendants" + android:orientation="horizontal" + android:paddingHorizontal="5dp" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/home_result_grid" /> + android:id="@+id/home_bookmarked_holder" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusUp="@id/home_watch_child_recyclerview" + android:nextFocusForward="@id/home_bookmarked_child_recyclerview" + android:paddingStart="12dp" + android:paddingTop="5dp" + android:paddingEnd="12dp" + android:paddingBottom="5dp"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="50dp" + android:fadingEdge="horizontal" + android:requiresFadingEdge="horizontal"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusRight="@id/home_plan_to_watch_btt" + android:text="@string/type_watching" /> + android:nextFocusLeft="@id/home_type_watching_btt" + android:nextFocusRight="@id/home_type_on_hold_btt" + android:text="@string/type_plan_to_watch" /> + android:nextFocusLeft="@id/home_plan_to_watch_btt" + android:nextFocusRight="@id/home_type_dropped_btt" + android:text="@string/type_on_hold" /> + android:nextFocusLeft="@id/home_type_on_hold_btt" + android:nextFocusRight="@id/home_type_completed_btt" + android:text="@string/type_dropped" /> + style="@style/RoundedSelectableButton" + android:nextFocusLeft="@id/home_type_dropped_btt" + android:text="@string/type_completed" /> + android:layout_width="30dp" + android:layout_height="match_parent" + android:layout_gravity="end|center_vertical" + android:layout_marginEnd="5dp" + android:contentDescription="@string/home_more_info" + android:src="@drawable/ic_baseline_arrow_forward_24" + app:tint="?attr/textColor" /> + android:clipToPadding="false" + android:descendantFocusability="afterDescendants" + android:orientation="horizontal" + android:paddingHorizontal="5dp" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/home_result_grid" /> + android:id="@+id/home_master_recycler" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:descendantFocusability="afterDescendants" + android:nextFocusLeft="@id/nav_rail_view" + tools:listitem="@layout/homepage_parent" /> + android:id="@+id/home_api_fab" + style="@style/ExtendedFloatingActionButton" + android:text="@string/home_source" + android:textColor="?attr/textColor" + android:visibility="gone" + app:icon="@drawable/ic_baseline_filter_list_24" + tools:ignore="ContentDescription" + tools:visibility="visible" /> + android:id="@+id/home_random" + style="@style/ExtendedFloatingActionButton" + android:layout_gravity="bottom|start" + android:text="@string/home_random" + android:textColor="?attr/textColor" + android:visibility="gone" + app:icon="@drawable/ic_baseline_play_arrow_24" + tools:ignore="ContentDescription" + tools:visibility="visible" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_plugins.xml b/app/src/main/res/layout/fragment_plugins.xml index 15e0d2f9..40a0299c 100644 --- a/app/src/main/res/layout/fragment_plugins.xml +++ b/app/src/main/res/layout/fragment_plugins.xml @@ -25,108 +25,7 @@ app:titleTextColor="?attr/textColor" tools:title="Overlord" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_width="match_parent" + android:layout_height="60dp"> + + - + - + + - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/tvtypes_chips.xml b/app/src/main/res/layout/tvtypes_chips.xml new file mode 100644 index 00000000..33ca7762 --- /dev/null +++ b/app/src/main/res/layout/tvtypes_chips.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/tvtypes_chips_scroll.xml b/app/src/main/res/layout/tvtypes_chips_scroll.xml new file mode 100644 index 00000000..45b27dbc --- /dev/null +++ b/app/src/main/res/layout/tvtypes_chips_scroll.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 9840cb80..9e235bf3 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -75,6 +75,10 @@ @color/amoledModeLight + +