diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 6c9fadd8..9937f183 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -177,6 +177,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val isNavVisible = listOf( R.id.navigation_home, R.id.navigation_search, + R.id.navigation_library, R.id.navigation_downloads, R.id.navigation_settings, R.id.navigation_download_child, diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt index 5aa56a02..ddb9f660 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.ui.library.LibraryItem interface SyncAPI : OAuth2API { val mainUrl: String @@ -22,6 +23,8 @@ interface SyncAPI : OAuth2API { suspend fun search(name: String): List? + suspend fun getPersonalLibrary(): List? + fun getIdFromUrl(url : String) : String data class SyncSearchResult( diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt index b621e81a..04e15f55 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt @@ -4,6 +4,7 @@ import com.lagradost.cloudstream3.ErrorLoadingException import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.mvvm.safeApiCall +import com.lagradost.cloudstream3.ui.library.LibraryItem class SyncRepo(private val repo: SyncAPI) { val idPrefix = repo.idPrefix @@ -16,21 +17,25 @@ class SyncRepo(private val repo: SyncAPI) { return safeApiCall { repo.score(id, status) } } - suspend fun getStatus(id : String) : Resource { + suspend fun getStatus(id: String): Resource { return safeApiCall { repo.getStatus(id) ?: throw ErrorLoadingException("No data") } } - suspend fun getResult(id : String) : Resource { + suspend fun getResult(id: String): Resource { return safeApiCall { repo.getResult(id) ?: throw ErrorLoadingException("No data") } } - suspend fun search(query : String) : Resource> { + suspend fun search(query: String): Resource> { return safeApiCall { repo.search(query) ?: throw ErrorLoadingException() } } - fun hasAccount() : Boolean { + suspend fun getPersonalLibrary(): Resource> { + return safeApiCall { repo.getPersonalLibrary() ?: throw ErrorLoadingException() } + } + + fun hasAccount(): Boolean { return normalSafeApiCall { repo.loginInfo() != null } ?: false } - fun getIdFromUrl(url : String) : String = repo.getIdFromUrl(url) + fun getIdFromUrl(url: String): String = repo.getIdFromUrl(url) } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt index 606fee97..b4f0f790 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt @@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.library.LibraryItem import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -595,7 +596,26 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("score") val score: Int, @JsonProperty("private") val private: Boolean, @JsonProperty("media") val media: Media - ) + ) { + fun toLibraryItem(listName: String?): LibraryItem? { + return LibraryItem( + // English title first + this.media.title.english ?: this.media.title.romaji ?: this.media.synonyms.firstOrNull() + ?: "", + this.media.id.toString(), + listName ?: return null, + this.progress, + this.media.episodes, + this.score, + "AniList", + TvType.Anime, + this.media.coverImage.large ?: this.media.coverImage.medium, + null, + null, + null + ) + } + } data class Lists( @JsonProperty("status") val status: String?, @@ -630,6 +650,10 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } + override suspend fun getPersonalLibrary(): List? { + return getAnilistAnimeListSmart()?.map { it.entries.mapNotNull { entry -> entry.toLibraryItem(entry.status ?: it.status) } }?.flatten() + } + private suspend fun getFullAnilistList(): FullAnilistList? { var userID: Int? = null /** WARNING ASSUMES ONE USER! **/ diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index ea27720a..d9011a1b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -7,11 +7,13 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ShowStatus +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.library.LibraryItem import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject @@ -381,7 +383,22 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { data class Data( @JsonProperty("node") val node: Node, @JsonProperty("list_status") val list_status: ListStatus?, - ) + ) { + fun toLibraryItem(): LibraryItem { + return LibraryItem( + this.node.title, + this.node.id.toString(), + this.list_status?.status?.lowercase()?.capitalize()?.replace("_", " ") ?: "NONE", + this.list_status?.num_episodes_watched, + this.node.num_episodes, + this.list_status?.score, + "MAL", + TvType.Anime, + this.node.main_picture?.large ?: this.node.main_picture?.medium, + null, null, null + ) + } + } data class Paging( @JsonProperty("next") val next: String? @@ -424,6 +441,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { } } + override suspend fun getPersonalLibrary(): List? { + return getMalAnimeListSmart()?.map { it.toLibraryItem() } + } + private suspend fun getMalAnimeList(): Array { checkMalToken() var offset = 0 diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt new file mode 100644 index 00000000..932d3916 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -0,0 +1,106 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.Context +import androidx.lifecycle.ViewModelProvider +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.appcompat.widget.SearchView +import androidx.fragment.app.activityViewModels +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.mvvm.observe +import com.lagradost.cloudstream3.syncproviders.AccountManager +import com.lagradost.cloudstream3.syncproviders.providers.MALApi +import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import kotlinx.android.synthetic.main.fragment_library.* +import kotlinx.android.synthetic.main.library_viewpager_page.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +class LibraryFragment : Fragment() { + + companion object { + fun newInstance() = LibraryFragment() + } + + private val libraryViewModel: LibraryViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_library, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context?.fixPaddingStatusbar(library_root) + + val sortView = + menu_toolbar?.menu?.findItem(R.id.sort_button) + sortView?.setOnMenuItemClickListener { + val methods = libraryViewModel.sortingMethods + .map { txt(it.stringRes).asString(context ?: view.context) } + + activity?.showBottomDialog( + methods, + libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod), + "Sort by", + false, + {}, + { + val method = libraryViewModel.sortingMethods[it] + libraryViewModel.sort(method) + } + ) + true + } + + val searchView = + menu_toolbar?.menu?.findItem(R.id.search_button)?.actionView as? MenuSearchView + searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + return true + } + + override fun onQueryTextChange(newText: String?): Boolean { + libraryViewModel.sort(ListSorting.Query, newText) + return true + } + }) + + libraryViewModel.loadPages() + + viewpager?.setPageTransformer(HomeScrollTransformer()) + viewpager?.adapter = viewpager.adapter ?: ViewpagerAdapter(emptyList()) + viewpager?.offscreenPageLimit = 10 + + observe(libraryViewModel.pages) { pages -> + (viewpager.adapter as? ViewpagerAdapter)?.pages = pages + viewpager.adapter?.notifyItemChanged(viewpager?.currentItem ?: 0) + + TabLayoutMediator( + library_tab_layout, + viewpager, + ) { tab, position -> + tab.text = pages.getOrNull(position)?.title + }.attach() + } + } +} + +class MenuSearchView(context: Context) : SearchView(context) { + override fun onActionViewCollapsed() { + super.onActionViewCollapsed() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt new file mode 100644 index 00000000..167290e1 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -0,0 +1,74 @@ +package com.lagradost.cloudstream3.ui.library + +import androidx.annotation.StringRes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import me.xdrop.fuzzywuzzy.FuzzySearch + +enum class ListSorting(@StringRes val stringRes: Int) { + Query(R.string.none), + RatingHigh(R.string.sort_rating_desc), + RatingLow(R.string.sort_rating_asc), + UpdatedNew(R.string.sort_updated_new), + UpdatedOld(R.string.sort_updated_old), + AlphabeticalA(R.string.sort_alphabetical_a), + AlphabeticalZ(R.string.sort_alphabetical_z), +} + +class LibraryViewModel : ViewModel() { + private val _pages: MutableLiveData> = MutableLiveData(emptyList()) + val pages: LiveData> = _pages + + private val _currentApiName: MutableLiveData = MutableLiveData("") + val currentApiName: LiveData = _currentApiName + + private val listApis = SyncApis.filter { it.hasAccount() } + private var currentApi = listApis.firstOrNull() + + val sortingMethods = listOf( + ListSorting.RatingHigh, + ListSorting.RatingLow, +// ListSorting.UpdatedNew, +// ListSorting.UpdatedOld, + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ) + + var currentSortingMethod: ListSorting = sortingMethods.first() + private set + + fun switchList() { + currentApi = listApis[(listApis.indexOf(currentApi) + 1) % listApis.size] + loadPages() + } + + fun sort(method: ListSorting, query: String? = null) { + val currentList = pages.value ?: return + currentSortingMethod = method + currentList.forEachIndexed { index, page -> + page.sort(method, query) + } + _pages.postValue(currentList) + } + + fun loadPages() { + ioSafe { + currentApi?.let { repo -> + val list = (repo.getPersonalLibrary() as? Resource.Success)?.value + val pages = (list ?: emptyList()).groupBy { it.listName }.map { + Page( + it.key, + it.value + ) + } + _pages.postValue(pages) + _currentApiName.postValue(repo.name) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt new file mode 100644 index 00000000..7db40f88 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -0,0 +1,58 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.ui.search.SearchResultBuilder +import com.lagradost.cloudstream3.utils.AppUtils +import kotlinx.android.synthetic.main.search_result_grid_expanded.view.* + +class PageAdapter( + override val items: MutableList, +) : + AppUtils.DiffAdapter(items) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return LibraryItemViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.search_result_grid_expanded, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is LibraryItemViewHolder -> { + holder.bind(items[position], position) + } + } + } + + inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun bind(item: LibraryItem, position: Int) { + SearchResultBuilder.bind( + { println("CLICKED ${it.action}") }, + item, + position, + itemView, + ) + + // Set watch progress bar +// val showProgress = item.episodesCompleted != null && item.episodesTotal != null +// itemView.watchProgress.isVisible = showProgress +// +// if (showProgress) { +// itemView.watchProgress.max = item.episodesTotal!! +// itemView.watchProgress.progress = item.episodesCompleted!! +// } + itemView.imageText.text = item.name + val showRating = (item.personalRating ?: 0) != 0 + itemView.text_rating.isVisible = showRating + if (showRating) { + itemView.text_rating.text = item.personalRating.toString() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt new file mode 100644 index 00000000..3939f50e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -0,0 +1,85 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.SearchQuality +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.TvType +import kotlinx.android.synthetic.main.library_viewpager_page.view.* +import me.xdrop.fuzzywuzzy.FuzzySearch + +data class Page( + val title: String, var items: List +) { + fun sort(method: ListSorting?, query: String? = null) { + items = when (method) { + ListSorting.Query -> + if (query != null) { + items.sortedBy { + -FuzzySearch.partialRatio( + query.lowercase(), it.name.lowercase() + ) + } + } else items + ListSorting.RatingHigh -> items.sortedBy { -(it.personalRating ?: 0) } + ListSorting.RatingLow -> items.sortedBy { (it.personalRating ?: 0) } + ListSorting.AlphabeticalA -> items.sortedBy { it.name } + ListSorting.AlphabeticalZ -> items.sortedBy { it.name }.reversed() + else -> items + } + } +} + +data class LibraryItem( + override val name: String, + override val url: String, + val listName: String, + val episodesCompleted: Int?, + val episodesTotal: Int?, + /** Out of 100 */ + val personalRating: Int?, + override val apiName: String, + override var type: TvType?, + override var posterUrl: String?, + override var posterHeaders: Map?, + override var id: Int?, + override var quality: SearchQuality?, +) : SearchResponse + + +class ViewpagerAdapter(var pages: List) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return PageViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.library_viewpager_page, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is PageViewHolder -> { + holder.bind(pages[position]) + } + } + } + + inner class PageViewHolder(private val itemViewTest: View) : + RecyclerView.ViewHolder(itemViewTest) { + fun bind(page: Page) { + if (itemViewTest.page_recyclerview?.adapter == null) { + itemViewTest.page_recyclerview?.adapter = PageAdapter(page.items.toMutableList()) + itemView.page_recyclerview?.spanCount = 4 + } else { + (itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items) + itemViewTest.page_recyclerview?.scrollToPosition(0) + } + } + } + + override fun getItemCount(): Int { + return pages.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt new file mode 100644 index 00000000..9eabac47 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/test.kt @@ -0,0 +1,11 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.View +import androidx.viewpager2.widget.ViewPager2 + +class HomeScrollTransformer : ViewPager2.PageTransformer { + + override fun transformPage(view: View, position: Float) { + } +} + diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 1c7bb214..2e9e0c88 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -32,6 +32,7 @@ import androidx.core.text.HtmlCompat import androidx.core.text.toSpanned import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.tvprovider.media.tv.PreviewChannelHelper @@ -64,6 +65,7 @@ import okhttp3.Cache import java.io.* import java.net.URL import java.net.URLDecoder +import kotlin.system.measureTimeMillis object AppUtils { fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) { @@ -263,6 +265,50 @@ object AppUtils { } } + abstract class DiffAdapter( + open val items: MutableList, + val comparison: (first: T, second: T) -> Boolean = { first, second -> + first.hashCode() == second.hashCode() + } + ) : + RecyclerView.Adapter() { + override fun getItemCount(): Int { + return items.size + } + + fun updateList(newList: List) { + val time = measureTimeMillis { + + val diffResult = DiffUtil.calculateDiff( + GenericDiffCallback(this.items, newList) + ) + + items.clear() + items.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + println("TIME TAKEn $time") + } + + inner class GenericDiffCallback( + private val oldList: List, + private val newList: List + ) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + comparison(oldList[oldItemPosition], newList[newItemPosition]) + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] + } + } + + fun Activity.downloadAllPluginsDialog(repositoryUrl: String, repositoryName: String) { runOnUiThread { val context = this diff --git a/app/src/main/res/color/item_select_color.xml b/app/src/main/res/color/item_select_color.xml index 0d2834dd..3d69c540 100644 --- a/app/src/main/res/color/item_select_color.xml +++ b/app/src/main/res/color/item_select_color.xml @@ -1,5 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml new file mode 100644 index 00000000..fc90e300 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_baseline_sort_24.xml b/app/src/main/res/drawable/ic_baseline_sort_24.xml new file mode 100644 index 00000000..96d46231 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_sort_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_star_24.xml b/app/src/main/res/drawable/ic_baseline_star_24.xml index ab099425..2dcadb7f 100644 --- a/app/src/main/res/drawable/ic_baseline_star_24.xml +++ b/app/src/main/res/drawable/ic_baseline_star_24.xml @@ -1,5 +1,5 @@ - + android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/main/res/drawable/indicator_background.xml b/app/src/main/res/drawable/indicator_background.xml new file mode 100644 index 00000000..ef44fb7c --- /dev/null +++ b/app/src/main/res/drawable/indicator_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/rating_bg_color.xml b/app/src/main/res/drawable/rating_bg_color.xml new file mode 100644 index 00000000..60e62bab --- /dev/null +++ b/app/src/main/res/drawable/rating_bg_color.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml new file mode 100644 index 00000000..9c5cbf04 --- /dev/null +++ b/app/src/main/res/layout/fragment_library.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/library_viewpager_page.xml b/app/src/main/res/layout/library_viewpager_page.xml new file mode 100644 index 00000000..940b5a69 --- /dev/null +++ b/app/src/main/res/layout/library_viewpager_page.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 710c6cf8..afa75393 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -1,90 +1,111 @@ - + + android:id="@+id/background_card" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_margin="2dp" + android:layout_marginBottom="2dp" + android:elevation="10dp" + app:cardBackgroundColor="?attr/primaryGrayBackground" + app:cardCornerRadius="@dimen/rounded_image_radius" + app:layout_constraintBottom_toTopOf="@+id/imageText" + app:layout_constraintDimensionRatio="0.6333:1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + android:layout_height="match_parent" + android:contentDescription="@string/search_poster_img_des" + android:duplicateParentState="true" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> + android:id="@+id/text_quality" + style="@style/SearchBox" + android:background="@drawable/type_bg_color" + android:textColor="@color/textColor" + tools:text="@string/quality_hd" /> + + + android:background="@drawable/dub_bg_color" + android:text="@string/app_dubbed_text" /> + android:id="@+id/text_is_sub" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@drawable/sub_bg_color" + android:text="@string/app_subbed_text" /> + android:id="@+id/text_flag" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="πŸ‡ΈπŸ‡ͺ" + tools:visibility="visible" /> + android:id="@+id/text_rating" + android:visibility="gone" + tools:visibility="visible" + tools:text="7.7" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@drawable/rating_bg_color" + android:text="@string/app_subbed_text" + app:drawableStartCompat="@drawable/ic_baseline_star_24" /> + + - \ No newline at end of file + android:id="@+id/imageText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:layout_weight="0" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:minLines="2" + android:paddingStart="5dp" + android:paddingTop="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:textColor="?attr/textColor" + android:textSize="13sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + tools:text="The Perfect Run\nThe Perfect Run" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index e3fd3d22..ba171654 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -2,19 +2,23 @@ + android:id="@+id/navigation_home" + android:icon="@drawable/ic_outline_home_24" + android:title="@string/title_home" /> + android:id="@+id/navigation_search" + android:icon="@drawable/search_icon" + android:title="@string/title_search" /> + android:id="@+id/navigation_library" + android:icon="@drawable/ic_baseline_collections_bookmark_24" + android:title="@string/library" /> + android:id="@+id/navigation_downloads" + android:icon="@drawable/netflix_download" + android:title="@string/title_downloads" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/library_menu.xml b/app/src/main/res/menu/library_menu.xml new file mode 100644 index 00000000..f21d998d --- /dev/null +++ b/app/src/main/res/menu/library_menu.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 3c45ee70..0afca83b 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -144,6 +144,15 @@ app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" /> + + #F53B66 #3BF585 ?attr/colorPrimaryDark + #3F51B5 #FF6F63 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 11c90d58..e1af1e0e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,6 +253,7 @@ Error backing up %s Search + Library Accounts Updates and backup @@ -615,4 +616,10 @@ Safe Mode enabled An unrecoverable crash occurred and we\'ve automatically disabled all extensions, so you can find and remove the extension which is causing trouble. View crash info + Rating (High to Low) + Rating (Low to High) + Updated (New to Old) + Updated (Old to New) + Alphabetical (A to Z) + Alphabetical (Z to A) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 2fb9b5b4..f758213f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -299,6 +299,10 @@ false + +