more views + MainActivity viewbindings

This commit is contained in:
LagradOst 2023-07-14 21:43:46 +02:00
parent 166a21f74e
commit 647e91bc4b
11 changed files with 237 additions and 128 deletions

View file

@ -208,7 +208,7 @@ dependencies {
implementation("com.github.discord:OverlappingPanels:0.1.3") implementation("com.github.discord:OverlappingPanels:0.1.3")
// debugImplementation because LeakCanary should only run in debug builds. // debugImplementation because LeakCanary should only run in debug builds.
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7' // debugImplementation("com.squareup.leakcanary:leakcanary-android:2.7")
// for shimmer when loading // for shimmer when loading
implementation("com.facebook.shimmer:shimmer:0.5.0") implementation("com.facebook.shimmer:shimmer:0.5.0")
@ -228,7 +228,7 @@ dependencies {
// Library/extensions searching with Levenshtein distance // Library/extensions searching with Levenshtein distance
implementation("me.xdrop:fuzzywuzzy:1.4.0") implementation("me.xdrop:fuzzywuzzy:1.4.0")
// color pallette for images -> colors // color palette for images -> colors
implementation("androidx.palette:palette-ktx:1.0.0") implementation("androidx.palette:palette-ktx:1.0.0")
} }

View file

@ -51,8 +51,8 @@ class CustomReportSender : ReportSender {
thread { // to not run it on main thread thread { // to not run it on main thread
runBlocking { runBlocking {
suspendSafeApiCall { suspendSafeApiCall {
val post = app.post(url, data = data) app.post(url, data = data)
println("Report response: $post") //println("Report response: $post")
} }
} }
} }

View file

@ -32,6 +32,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.android.gms.cast.framework.* import com.google.android.gms.cast.framework.*
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -49,6 +50,9 @@ import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
import com.lagradost.cloudstream3.CommonActivity.onUserLeaveHint import com.lagradost.cloudstream3.CommonActivity.onUserLeaveHint
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.CommonActivity.updateLocale import com.lagradost.cloudstream3.CommonActivity.updateLocale
import com.lagradost.cloudstream3.databinding.ActivityMainBinding
import com.lagradost.cloudstream3.databinding.ActivityMainTvBinding
import com.lagradost.cloudstream3.databinding.BottomResultviewPreviewBinding
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
@ -74,6 +78,7 @@ import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.setImage import com.lagradost.cloudstream3.ui.result.setImage
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.search.SearchFragment import com.lagradost.cloudstream3.ui.search.SearchFragment
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
@ -110,9 +115,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser import com.lagradost.nicehttp.ResponseParser
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.bottom_resultview_preview.*
import kotlinx.android.synthetic.main.fragment_result_swipe.*
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import java.io.File import java.io.File
@ -334,8 +336,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// Use both navigation views to support both layouts. // Use both navigation views to support both layouts.
// It might be better to use the QuickSearch. // It might be better to use the QuickSearch.
nav_view?.selectedItemId = R.id.navigation_search activity?.findViewById<BottomNavigationView>(R.id.nav_view)?.selectedItemId =
nav_rail_view?.selectedItemId = R.id.navigation_search R.id.navigation_search
activity?.findViewById<NavigationRailView>(R.id.nav_rail_view)?.selectedItemId =
R.id.navigation_search
} else if (safeURI(str)?.scheme == appStringPlayer) { } else if (safeURI(str)?.scheme == appStringPlayer) {
val uri = Uri.parse(str) val uri = Uri.parse(str)
val name = uri.getQueryParameter("name") val name = uri.getQueryParameter("name")
@ -412,7 +416,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
this.hideKeyboard() this.hideKeyboard()
// Fucks up anime info layout since that has its own layout // Fucks up anime info layout since that has its own layout
cast_mini_controller_holder?.isVisible = binding?.castMiniControllerHolder?.isVisible =
!listOf( !listOf(
R.id.navigation_results_phone, R.id.navigation_results_phone,
R.id.navigation_results_tv, R.id.navigation_results_tv,
@ -448,7 +452,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
R.id.navigation_player, R.id.navigation_player,
).contains(destination.id) ).contains(destination.id)
nav_host_fragment?.apply { binding?.navHostFragment?.apply {
val params = layoutParams as ConstraintLayout.LayoutParams val params = layoutParams as ConstraintLayout.LayoutParams
params.setMargins( params.setMargins(
@ -464,21 +468,24 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
Configuration.ORIENTATION_LANDSCAPE -> { Configuration.ORIENTATION_LANDSCAPE -> {
true true
} }
Configuration.ORIENTATION_PORTRAIT -> { Configuration.ORIENTATION_PORTRAIT -> {
false isTvSettings()
} }
else -> { else -> {
false false
} }
} }
binding?.apply {
navView.isVisible = isNavVisible && !landscape
navRailView.isVisible = isNavVisible && landscape
nav_view?.isVisible = isNavVisible && !landscape // Hide library on TV since it is not supported yet :(
nav_rail_view?.isVisible = isNavVisible && landscape val isTrueTv = isTrueTvSettings()
navView.menu.findItem(R.id.navigation_library)?.isVisible = !isTrueTv
// Hide library on TV since it is not supported yet :( navRailView.menu.findItem(R.id.navigation_library)?.isVisible = !isTrueTv
val isTrueTv = isTrueTvSettings() }
nav_view?.menu?.findItem(R.id.navigation_library)?.isVisible = !isTrueTv
nav_rail_view?.menu?.findItem(R.id.navigation_library)?.isVisible = !isTrueTv
} }
//private var mCastSession: CastSession? = null //private var mCastSession: CastSession? = null
@ -691,28 +698,37 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
private fun hidePreviewPopupDialog() { private fun hidePreviewPopupDialog() {
viewModel.clear()
bottomPreviewPopup.dismissSafe(this) bottomPreviewPopup.dismissSafe(this)
bottomPreviewPopup = null
bottomPreviewBinding = null
} }
var bottomPreviewPopup: BottomSheetDialog? = null private var bottomPreviewPopup: BottomSheetDialog? = null
private fun showPreviewPopupDialog(): BottomSheetDialog { private var bottomPreviewBinding: BottomResultviewPreviewBinding? = null
val ret = (bottomPreviewPopup ?: run { private fun showPreviewPopupDialog(): BottomResultviewPreviewBinding {
val ret = (bottomPreviewBinding ?: run {
val builder = val builder =
BottomSheetDialog(this) BottomSheetDialog(this)
builder.setContentView(R.layout.bottom_resultview_preview) val binding: BottomResultviewPreviewBinding =
BottomResultviewPreviewBinding.inflate(builder.layoutInflater, null, false)
bottomPreviewBinding = binding
builder.setContentView(binding.root)
builder.setOnDismissListener { builder.setOnDismissListener {
bottomPreviewPopup = null bottomPreviewPopup = null
bottomPreviewBinding = null
viewModel.clear() viewModel.clear()
} }
builder.setCanceledOnTouchOutside(true) builder.setCanceledOnTouchOutside(true)
builder.show() builder.show()
builder bottomPreviewPopup = builder
binding
}) })
bottomPreviewPopup = ret
return ret return ret
} }
var binding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
app.initClient(this) app.initClient(this)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
@ -744,10 +760,20 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
updateTv() updateTv()
if (isTvSettings()) { // just in case, MAIN SHOULD *NEVER* BOOT LOOP CRASH
setContentView(R.layout.activity_main_tv) binding = try {
} else { if (isTvSettings()) {
setContentView(R.layout.activity_main) val newLocalBinding = ActivityMainTvBinding.inflate(layoutInflater, null, false)
setContentView(newLocalBinding.root)
ActivityMainBinding.bind(newLocalBinding.root) // this may crash
} else {
val newLocalBinding = ActivityMainBinding.inflate(layoutInflater, null, false)
setContentView(newLocalBinding.root)
newLocalBinding
}
} catch (t: Throwable) {
showToast(this, txt(R.string.unable_to_inflate, t.message ?: ""), Toast.LENGTH_LONG)
null
} }
changeStatusBarState(isEmulatorSettings()) changeStatusBarState(isEmulatorSettings())
@ -832,41 +858,44 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
observeNullable(viewModel.page) { resource -> observeNullable(viewModel.page) { resource ->
if (resource == null) { if (resource == null) {
bottomPreviewPopup.dismissSafe(this) hidePreviewPopupDialog()
return@observeNullable return@observeNullable
} }
when (resource) { when (resource) {
is Resource.Failure -> { is Resource.Failure -> {
showToast(this, R.string.error) showToast(this, R.string.error)
viewModel.clear()
hidePreviewPopupDialog() hidePreviewPopupDialog()
} }
is Resource.Loading -> { is Resource.Loading -> {
showPreviewPopupDialog().apply { showPreviewPopupDialog().apply {
resultview_preview_loading?.isVisible = true resultviewPreviewLoading.isVisible = true
resultview_preview_result?.isVisible = false resultviewPreviewResult.isVisible = false
resultview_preview_loading_shimmer?.startShimmer() resultviewPreviewLoadingShimmer.startShimmer()
} }
} }
is Resource.Success -> { is Resource.Success -> {
val d = resource.value val d = resource.value
showPreviewPopupDialog().apply { showPreviewPopupDialog().apply {
resultview_preview_loading?.isVisible = false resultviewPreviewLoading.isVisible = false
resultview_preview_result?.isVisible = true resultviewPreviewResult.isVisible = true
resultview_preview_loading_shimmer?.stopShimmer() resultviewPreviewLoadingShimmer.stopShimmer()
resultview_preview_title?.text = d.title resultviewPreviewTitle.text = d.title
resultview_preview_meta_type.setText(d.typeText) resultviewPreviewMetaType.setText(d.typeText)
resultview_preview_meta_year.setText(d.yearText) resultviewPreviewMetaYear.setText(d.yearText)
resultview_preview_meta_duration.setText(d.durationText) resultviewPreviewMetaDuration.setText(d.durationText)
resultview_preview_meta_rating.setText(d.ratingText) resultviewPreviewMetaRating.setText(d.ratingText)
resultview_preview_description?.setText(d.plotText) resultviewPreviewDescription.setText(d.plotText)
resultview_preview_poster?.setImage( resultviewPreviewPoster.setImage(
d.posterImage ?: d.posterBackgroundImage d.posterImage ?: d.posterBackgroundImage
) )
resultview_preview_poster?.setOnClickListener { resultviewPreviewPoster.setOnClickListener {
//viewModel.updateWatchStatus(WatchType.PLANTOWATCH) //viewModel.updateWatchStatus(WatchType.PLANTOWATCH)
val value = viewModel.watchStatus.value ?: WatchType.NONE val value = viewModel.watchStatus.value ?: WatchType.NONE
@ -882,7 +911,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
if (!isTvSettings()) // dont want this clickable on tv layout if (!isTvSettings()) // dont want this clickable on tv layout
resultview_preview_description?.setOnClickListener { view -> resultviewPreviewDescription.setOnClickListener { view ->
view.context?.let { ctx -> view.context?.let { ctx ->
val builder: AlertDialog.Builder = val builder: AlertDialog.Builder =
AlertDialog.Builder(ctx, R.style.AlertDialogCustom) AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
@ -892,7 +921,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
} }
resultview_preview_more_info?.setOnClickListener { resultviewPreviewMoreInfo.setOnClickListener {
viewModel.clear()
hidePreviewPopupDialog() hidePreviewPopupDialog()
lastPopup?.let { lastPopup?.let {
loadSearchResult(it) loadSearchResult(it)
@ -964,22 +994,22 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
.setPopExitAnim(R.anim.nav_pop_exit) .setPopExitAnim(R.anim.nav_pop_exit)
.setPopUpTo(navController.graph.startDestination, false) .setPopUpTo(navController.graph.startDestination, false)
.build()*/ .build()*/
nav_view?.setupWithNavController(navController) binding?.navView?.setupWithNavController(navController)
val nav_rail = findViewById<NavigationRailView?>(R.id.nav_rail_view) val navRail = findViewById<NavigationRailView?>(R.id.nav_rail_view)
nav_rail?.setupWithNavController(navController) navRail?.setupWithNavController(navController)
if (isTvSettings()) { if (isTvSettings()) {
nav_rail?.background?.alpha = 200 navRail?.background?.alpha = 200
} else { } else {
nav_rail?.background?.alpha = 255 navRail?.background?.alpha = 255
} }
nav_rail?.setOnItemSelectedListener { item -> navRail?.setOnItemSelectedListener { item ->
onNavDestinationSelected( onNavDestinationSelected(
item, item,
navController navController
) )
} }
nav_view?.setOnItemSelectedListener { item -> binding?.navView?.setOnItemSelectedListener { item ->
onNavDestinationSelected( onNavDestinationSelected(
item, item,
navController navController
@ -1010,16 +1040,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}*/ }*/
val rippleColor = ColorStateList.valueOf(getResourceColor(R.attr.colorPrimary, 0.1f)) val rippleColor = ColorStateList.valueOf(getResourceColor(R.attr.colorPrimary, 0.1f))
nav_view?.itemRippleColor = rippleColor binding?.navView?.itemRippleColor = rippleColor
nav_rail?.itemRippleColor = rippleColor navRail?.itemRippleColor = rippleColor
nav_rail?.itemActiveIndicatorColor = rippleColor navRail?.itemActiveIndicatorColor = rippleColor
nav_view?.itemActiveIndicatorColor = rippleColor binding?.navView?.itemActiveIndicatorColor = rippleColor
if (!checkWrite()) { if (!checkWrite()) {
requestRW() requestRW()
if (checkWrite()) return if (checkWrite()) return
} }
CastButtonFactory.setUpMediaRouteButton(this, media_route_button) //CastButtonFactory.setUpMediaRouteButton(this, media_route_button)
// THIS IS CURRENTLY REMOVED BECAUSE HIGHER VERS OF ANDROID NEEDS A NOTIFICATION // THIS IS CURRENTLY REMOVED BECAUSE HIGHER VERS OF ANDROID NEEDS A NOTIFICATION
//if (!VideoDownloadManager.isMyServiceRunning(this, VideoDownloadKeepAliveService::class.java)) { //if (!VideoDownloadManager.isMyServiceRunning(this, VideoDownloadKeepAliveService::class.java)) {

View file

@ -31,6 +31,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.bookmarksUpdatedEvent import com.lagradost.cloudstream3.MainActivity.Companion.bookmarksUpdatedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent
@ -45,6 +46,7 @@ import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.search.* import com.lagradost.cloudstream3.ui.search.*
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings
@ -431,22 +433,20 @@ class HomeFragment : Fragment() {
): View? { ): View? {
//homeViewModel = //homeViewModel =
// ViewModelProvider(this).get(HomeViewModel::class.java) // ViewModelProvider(this).get(HomeViewModel::class.java)
bottomSheetDialog?.ownShow() bottomSheetDialog?.ownShow()
val layout = val layout =
if (isTvSettings()) R.layout.fragment_home_tv else R.layout.fragment_home if (isTvSettings()) R.layout.fragment_home_tv else R.layout.fragment_home
/* val binding = FragmentHomeTvBinding.inflate(layout, container, false)
binding.homeLoadingError
val binding2 = FragmentHomeBinding.inflate(layout, container, false)
binding2.homeLoadingError*/
val root = inflater.inflate(layout, container, false) val root = inflater.inflate(layout, container, false)
binding = FragmentHomeBinding.bind(root) binding = try {
//val localBinding = FragmentHomeBinding.inflate(inflater) FragmentHomeBinding.bind(root)
//binding = localBinding } catch (t : Throwable) {
return root showToast(activity, txt(R.string.unable_to_inflate, t.message ?: ""), Toast.LENGTH_LONG)
logError(t)
null
}
//return inflater.inflate(layout, container, false) return root
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -2,13 +2,13 @@ package com.lagradost.cloudstream3.ui.library
import android.view.View import android.view.View
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import kotlinx.android.synthetic.main.library_viewpager_page.view.* import com.lagradost.cloudstream3.R
import kotlin.math.roundToInt import kotlin.math.roundToInt
class LibraryScrollTransformer : ViewPager2.PageTransformer { class LibraryScrollTransformer : ViewPager2.PageTransformer {
override fun transformPage(page: View, position: Float) { override fun transformPage(page: View, position: Float) {
val padding = (-position * page.width).roundToInt() val padding = (-position * page.width).roundToInt()
page.page_recyclerview.setPadding( page.findViewById<View>(R.id.page_recyclerview).setPadding(
padding, 0, padding, 0,
-padding, 0 -padding, 0
) )

View file

@ -2,24 +2,18 @@ package com.lagradost.cloudstream3.ui.player.source_priority
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.view.View import android.view.LayoutInflater
import android.widget.EditText
import android.widget.TextView
import androidx.annotation.StyleRes import androidx.annotation.StyleRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.work.impl.constraints.controllers.ConstraintController
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.PlayerSelectSourcePriorityBinding
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import kotlinx.android.synthetic.main.player_select_source_priority.*
class SourcePriorityDialog( class SourcePriorityDialog(
ctx: Context, val ctx: Context,
@StyleRes themeRes: Int, @StyleRes themeRes: Int,
val links: List<ExtractorLink>, val links: List<ExtractorLink>,
private val profile: QualityDataHelper.QualityProfile, private val profile: QualityDataHelper.QualityProfile,
@ -30,13 +24,14 @@ class SourcePriorityDialog(
private val updatedCallback: () -> Unit private val updatedCallback: () -> Unit
) : Dialog(ctx, themeRes) { ) : Dialog(ctx, themeRes) {
override fun show() { override fun show() {
setContentView(R.layout.player_select_source_priority) val binding = PlayerSelectSourcePriorityBinding.inflate(LayoutInflater.from(ctx), null, false)
val sourcesRecyclerView: RecyclerView = sort_sources setContentView(binding.root)
val qualitiesRecyclerView: RecyclerView = sort_qualities val sourcesRecyclerView = binding.sortSources
val profileText: EditText = profile_text_editable val qualitiesRecyclerView = binding.sortQualities
val saveBtt: View = save_btt val profileText = binding.profileTextEditable
val exitBtt: View = close_btt val saveBtt = binding.saveBtt
val helpBtt: View = help_btt val exitBtt = binding.closeBtt
val helpBtt = binding.helpBtt
profileText.setText(QualityDataHelper.getProfileName(profile.id).asString(context)) profileText.setText(QualityDataHelper.getProfileName(profile.id).asString(context))
profileText.hint = txt(R.string.profile_number, profile.id).asString(context) profileText.hint = txt(R.string.profile_number, profile.id).asString(context)

View file

@ -38,15 +38,15 @@ class SearchAdapter(
var hasNext: Boolean = false var hasNext: Boolean = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
val layout = val layout =
if (parent.context.IsBottomLayout()) SearchResultGridExpandedBinding.inflate( if (parent.context.IsBottomLayout()) SearchResultGridExpandedBinding.inflate(
LayoutInflater.from(parent.context), inflater,
parent, parent,
false false
) else SearchResultGridBinding.inflate( ) else SearchResultGridBinding.inflate(
LayoutInflater.from(parent.context), inflater,
parent, parent,
false false
) //R.layout.search_result_grid_expanded else R.layout.search_result_grid ) //R.layout.search_result_grid_expanded else R.layout.search_result_grid
@ -95,9 +95,15 @@ class SearchAdapter(
private val coverHeight: Int = private val coverHeight: Int =
if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt() if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt()
private val cardView = when(binding) {
is SearchResultGridExpandedBinding -> binding.imageView
is SearchResultGridBinding -> binding.imageView
else -> null
}
fun bind(card: SearchResponse, position: Int) { fun bind(card: SearchResponse, position: Int) {
if (!compactView) { if (!compactView) {
binding.root.apply { cardView?.apply {
layoutParams = FrameLayout.LayoutParams( layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
coverHeight coverHeight

View file

@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.ui.settings.extensions
import android.text.format.Formatter.formatShortFileSize import android.text.format.Formatter.formatShortFileSize
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone import androidx.core.view.isGone
@ -13,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.result.setText
@ -26,10 +26,11 @@ import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.repository_item.view.*
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.text.DecimalFormat import java.text.DecimalFormat
import kotlin.math.floor
import kotlin.math.log10
data class PluginViewData( data class PluginViewData(
@ -45,8 +46,10 @@ class PluginAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val layout = if(isTrueTvSettings()) R.layout.repository_item_tv else R.layout.repository_item val layout = if(isTrueTvSettings()) R.layout.repository_item_tv else R.layout.repository_item
val inflated = LayoutInflater.from(parent.context).inflate(layout, parent, false)
return PluginViewHolder( return PluginViewHolder(
LayoutInflater.from(parent.context).inflate(layout, parent, false) RepositoryItemBinding.bind(inflated) // may crash
) )
} }
@ -82,8 +85,10 @@ class PluginAdapter(
// Clear glide image because setImageResource doesn't override // Clear glide image because setImageResource doesn't override
override fun onViewRecycled(holder: RecyclerView.ViewHolder) { override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
holder.itemView.entry_icon?.let { pluginIcon -> if (holder is PluginViewHolder) {
GlideApp.with(pluginIcon).clear(pluginIcon) holder.binding.entryIcon.let { pluginIcon ->
GlideApp.with(pluginIcon).clear(pluginIcon)
}
} }
super.onViewRecycled(holder) super.onViewRecycled(holder)
} }
@ -112,7 +117,7 @@ class PluginAdapter(
fun prettyCount(number: Number): String? { fun prettyCount(number: Number): String? {
val suffix = charArrayOf(' ', 'k', 'M', 'B', 'T', 'P', 'E') val suffix = charArrayOf(' ', 'k', 'M', 'B', 'T', 'P', 'E')
val numValue = number.toLong() val numValue = number.toLong()
val value = Math.floor(Math.log10(numValue.toDouble())).toInt() val value = floor(log10(numValue.toDouble())).toInt()
val base = value / 3 val base = value / 3
return if (value >= 3 && base < suffix.size) { return if (value >= 3 && base < suffix.size) {
DecimalFormat("#0.00").format( DecimalFormat("#0.00").format(
@ -127,8 +132,8 @@ class PluginAdapter(
} }
} }
inner class PluginViewHolder(itemView: View) : inner class PluginViewHolder(val binding: RepositoryItemBinding) :
RecyclerView.ViewHolder(itemView) { RecyclerView.ViewHolder(binding.root) {
fun bind( fun bind(
data: PluginViewData, data: PluginViewData,
@ -138,17 +143,17 @@ class PluginAdapter(
val name = metadata.name.removeSuffix("Provider") val name = metadata.name.removeSuffix("Provider")
val alpha = if (disabled) 0.6f else 1f val alpha = if (disabled) 0.6f else 1f
val isLocal = !data.plugin.second.url.startsWith("http") val isLocal = !data.plugin.second.url.startsWith("http")
itemView.main_text?.alpha = alpha binding.mainText.alpha = alpha
itemView.sub_text?.alpha = alpha binding.subText.alpha = alpha
val drawableInt = if (data.isDownloaded) val drawableInt = if (data.isDownloaded)
R.drawable.ic_baseline_delete_outline_24 R.drawable.ic_baseline_delete_outline_24
else R.drawable.netflix_download else R.drawable.netflix_download
itemView.nsfw_marker?.isVisible = metadata.tvTypes?.contains("NSFW") ?: false binding.nsfwMarker.isVisible = metadata.tvTypes?.contains("NSFW") ?: false
itemView.action_button?.setImageResource(drawableInt) binding.actionButton.setImageResource(drawableInt)
itemView.action_button?.setOnClickListener { binding.actionButton.setOnClickListener {
iconClickCallback.invoke(data.plugin) iconClickCallback.invoke(data.plugin)
} }
itemView.setOnClickListener { itemView.setOnClickListener {
@ -169,10 +174,11 @@ class PluginAdapter(
if (data.isDownloaded) { if (data.isDownloaded) {
// On local plugins page the filepath is provided instead of url. // On local plugins page the filepath is provided instead of url.
val plugin = PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url] val plugin =
PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]
if (plugin?.openSettings != null) { if (plugin?.openSettings != null) {
itemView.action_settings?.isVisible = true binding.actionSettings.isVisible = true
itemView.action_settings.setOnClickListener { binding.actionSettings.setOnClickListener {
try { try {
plugin.openSettings!!.invoke(itemView.context) plugin.openSettings!!.invoke(itemView.context)
} catch (e: Throwable) { } catch (e: Throwable) {
@ -185,13 +191,13 @@ class PluginAdapter(
} }
} }
} else { } else {
itemView.action_settings?.isVisible = false binding.actionSettings.isVisible = false
} }
} else { } else {
itemView.action_settings?.isVisible = false binding.actionSettings.isVisible = false
} }
if (itemView.entry_icon?.setImage(//itemView.entry_icon?.height ?: if (!binding.entryIcon.setImage(//itemView.entry_icon?.height ?:
metadata.iconUrl?.replace( metadata.iconUrl?.replace(
"%size%", "%size%",
"$iconSize" "$iconSize"
@ -201,41 +207,47 @@ class PluginAdapter(
), ),
null, null,
errorImageDrawable = R.drawable.ic_baseline_extension_24 errorImageDrawable = R.drawable.ic_baseline_extension_24
) != true )
) { ) {
itemView.entry_icon?.setImageResource(R.drawable.ic_baseline_extension_24) binding.entryIcon.setImageResource(R.drawable.ic_baseline_extension_24)
} }
itemView.ext_version?.isVisible = true binding.extVersion.isVisible = true
itemView.ext_version?.text = "v${metadata.version}" binding.extVersion.text = "v${metadata.version}"
if (metadata.language.isNullOrBlank()) { if (metadata.language.isNullOrBlank()) {
itemView.lang_icon?.isVisible = false binding.langIcon.isVisible = false
} else { } else {
itemView.lang_icon?.isVisible = true binding.langIcon.isVisible = true
itemView.lang_icon.text = "${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}" binding.langIcon.text =
"${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}"
} }
itemView.ext_votes?.isVisible = false binding.extVotes.isVisible = false
if (!isLocal) { if (!isLocal) {
ioSafe { ioSafe {
metadata.getVotes().main { metadata.getVotes().main {
itemView.ext_votes?.setText(txt(R.string.extension_rating, prettyCount(it))) binding.extVotes.setText(txt(R.string.extension_rating, prettyCount(it)))
itemView.ext_votes?.isVisible = true binding.extVotes.isVisible = true
} }
} }
} }
if (metadata.fileSize != null) { if (metadata.fileSize != null) {
itemView.ext_filesize?.isVisible = true binding.extFilesize.isVisible = true
itemView.ext_filesize?.text = formatShortFileSize(itemView.context, metadata.fileSize) binding.extFilesize.text = formatShortFileSize(itemView.context, metadata.fileSize)
} else { } else {
itemView.ext_filesize?.isVisible = false binding.extFilesize.isVisible = false
} }
itemView.main_text.setText(if(disabled) txt(R.string.single_plugin_disabled, name) else txt(name)) binding.mainText.setText(
itemView.sub_text?.isGone = metadata.description.isNullOrBlank() if (disabled) txt(
itemView.sub_text?.text = metadata.description.html() R.string.single_plugin_disabled,
name
) else txt(name)
)
binding.subText.isGone = metadata.description.isNullOrBlank()
binding.subText.text = metadata.description.html()
} }
} }
} }

View file

@ -181,6 +181,50 @@ object UIHelper {
} }
} }
/*inline fun <reified T : ViewBinding> bindViewBinding(
inflater: LayoutInflater?,
container: ViewGroup?,
layout: Int
): Pair<T?, UiText?> {
return try {
val localInflater = inflater ?: container?.context?.let { LayoutInflater.from(it) }
?: return null to txt(
R.string.unable_to_inflate,
"Requires inflater OR container"
)//throw IllegalArgumentException("Requires inflater OR container"))
//println("methods: ${T::class.java.methods.map { it.name }}")
val bind = T::class.java.methods.first { it.name == "bind" }
//val inflate = T::class.java.methods.first { it.name == "inflate" }
val root = localInflater.inflate(layout, container, false)
bind.invoke(null, root) as T to null
} catch (t: Throwable) {
logError(t)
val message = txt(R.string.unable_to_inflate, t.message ?: "Primary constructor")
// if the desired layout is not found then we inflate the casted layout
/*try {
val localInflater = inflater ?: container?.context?.let { LayoutInflater.from(it) }
?: return null to txt(
R.string.unable_to_inflate,
"Requires inflater OR container"
)//throw IllegalArgumentException("Requires inflater OR container"))
// we don't know what method to use as there are 2, but first *should* always be true
return try {
val inflate = T::class.java.methods.first { it.name == "inflate" }
inflate.invoke(null, localInflater, container, false) as T
} catch (_: Throwable) {
val inflate = T::class.java.methods.last { it.name == "inflate" }
inflate.invoke(null, localInflater, container, false) as T
} to message
} catch (t: Throwable) {
logError(t)
}*/
null to message
}
}*/
fun ImageView?.setImage( fun ImageView?.setImage(
url: String?, url: String?,
headers: Map<String, String>? = null, headers: Map<String, String>? = null,
@ -190,7 +234,12 @@ object UIHelper {
colorCallback: ((Palette) -> Unit)? = null colorCallback: ((Palette) -> Unit)? = null
): Boolean { ): Boolean {
if (url.isNullOrBlank()) return false if (url.isNullOrBlank()) return false
this.setImage(UiImage.Image(url, headers, errorImageDrawable), errorImageDrawable, fadeIn, colorCallback) this.setImage(
UiImage.Image(url, headers, errorImageDrawable),
errorImageDrawable,
fadeIn,
colorCallback
)
return true return true
} }

View file

@ -41,6 +41,22 @@
</com.google.android.material.navigationrail.NavigationRailView> </com.google.android.material.navigationrail.NavigationRailView>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_height="70dp"
android:layout_width="0dp"
app:labelVisibilityMode="unlabeled"
android:visibility="gone"
android:background="?attr/primaryGrayBackground"
app:itemIconTint="@color/item_select_color"
app:itemTextColor="@color/item_select_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<LinearLayout <LinearLayout
android:id="@+id/cast_mini_controller_holder" android:id="@+id/cast_mini_controller_holder"
android:layout_width="0dp" android:layout_width="0dp"

View file

@ -677,4 +677,5 @@
</string> </string>
<string name="qualities">Qualities</string> <string name="qualities">Qualities</string>
<string name="profile_background_des">Profile background</string> <string name="profile_background_des">Profile background</string>
<string name="unable_to_inflate">UI was unable to be created correctly, this is a MAJOR BUG and should be reported immediately %s</string>
</resources> </resources>