mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	subs
This commit is contained in:
		
							parent
							
								
									6b27db036b
								
							
						
					
					
						commit
						3f8229756d
					
				
					 17 changed files with 450 additions and 101 deletions
				
			
		|  | @ -113,6 +113,8 @@ abstract class MainAPI { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| class ErrorLoadingException(message: String? = null) : Exception(message) | ||||
| 
 | ||||
| fun parseRating(ratingString: String?): Int? { | ||||
|     if (ratingString == null) return null | ||||
|     val floatRating = ratingString.toFloatOrNull() ?: return null | ||||
|  | @ -149,6 +151,19 @@ fun sortSubs(urls: List<SubtitleFile>): List<SubtitleFile> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| /** https://www.imdb.com/title/tt2861424/ -> tt2861424 */ | ||||
| fun imdbUrlToId(url: String): String { | ||||
|     return url | ||||
|         .removePrefix("https://www.imdb.com/title/") | ||||
|         .removePrefix("https://imdb.com/title/tt2861424/") | ||||
|         .replace("/", "") | ||||
| } | ||||
| 
 | ||||
| fun imdbUrlToIdNullable(url: String?): String? { | ||||
|     if(url == null) return null | ||||
|     return imdbUrlToId(url) | ||||
| } | ||||
| 
 | ||||
| enum class ShowStatus { | ||||
|     Completed, | ||||
|     Ongoing, | ||||
|  | @ -301,7 +316,7 @@ data class MovieLoadResponse( | |||
|     override val year: Int?, | ||||
|     override val plot: String?, | ||||
| 
 | ||||
|     val imdbUrl: String?, | ||||
|     val imdbId: String?, | ||||
|     override val rating: Int? = null, | ||||
|     override val tags: ArrayList<String>? = null, | ||||
|     override val duration: String? = null, | ||||
|  | @ -331,7 +346,7 @@ data class TvSeriesLoadResponse( | |||
|     override val plot: String?, | ||||
| 
 | ||||
|     val showStatus: ShowStatus?, | ||||
|     val imdbUrl: String?, | ||||
|     val imdbId: String?, | ||||
|     override val rating: Int? = null, | ||||
|     override val tags: ArrayList<String>? = null, | ||||
|     override val duration: String? = null, | ||||
|  |  | |||
|  | @ -31,10 +31,8 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey | |||
| import com.lagradost.cloudstream3.utils.DataStore.removeKey | ||||
| import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos | ||||
| import com.lagradost.cloudstream3.utils.Event | ||||
| import com.lagradost.cloudstream3.utils.SubtitleHelper.createISO | ||||
| import kotlinx.android.synthetic.main.activity_main.* | ||||
| import kotlinx.android.synthetic.main.fragment_result.* | ||||
| import kotlin.concurrent.thread | ||||
| 
 | ||||
| const val VLC_PACKAGE = "org.videolan.vlc" | ||||
| const val VLC_INTENT_ACTION_RESULT = "org.videolan.vlc.player.result" | ||||
|  | @ -58,7 +56,7 @@ class MainActivity : AppCompatActivity() { | |||
|             return appViewModelStore | ||||
|         }*/ | ||||
|     companion object { | ||||
|         var isInPlayer: Boolean = false | ||||
|         var canEnterPipMode: Boolean = false | ||||
|         var canShowPipMode: Boolean = false | ||||
|         var isInPIPMode: Boolean = false | ||||
| 
 | ||||
|  | @ -67,7 +65,7 @@ class MainActivity : AppCompatActivity() { | |||
|     } | ||||
| 
 | ||||
|     private fun enterPIPMode() { | ||||
|         if (!shouldShowPIPMode(isInPlayer) || !canShowPipMode) return | ||||
|         if (!shouldShowPIPMode(canEnterPipMode) || !canShowPipMode) return | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             try { | ||||
|                 enterPictureInPictureMode(PictureInPictureParams.Builder().build()) | ||||
|  | @ -83,7 +81,7 @@ class MainActivity : AppCompatActivity() { | |||
| 
 | ||||
|     override fun onUserLeaveHint() { | ||||
|         super.onUserLeaveHint() | ||||
|         if (isInPlayer && canShowPipMode) { | ||||
|         if (canEnterPipMode && canShowPipMode) { | ||||
|             enterPIPMode() | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -132,7 +132,7 @@ class MeloMovieProvider : MainAPI() { | |||
|         val plot = document.selectFirst("div.col-lg-12 > p").text() | ||||
| 
 | ||||
|         if (type == 1) { // MOVIE | ||||
|             val serialize = document.selectFirst("table.accordion__list") | ||||
|             val serialize = document.selectFirst("table.accordion__list") ?: throw ErrorLoadingException("No links found") | ||||
|             return MovieLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|  | @ -142,11 +142,11 @@ class MeloMovieProvider : MainAPI() { | |||
|                 poster, | ||||
|                 year, | ||||
|                 plot, | ||||
|                 imdbUrl | ||||
|                 imdbUrlToIdNullable(imdbUrl) | ||||
|             ) | ||||
|         } else if (type == 2) { | ||||
|             val episodes = ArrayList<TvSeriesEpisode>() | ||||
|             val seasons = document.select("div.accordion__card") | ||||
|             val seasons = document.select("div.accordion__card") ?: throw ErrorLoadingException("No episodes found") | ||||
|             for (s in seasons) { | ||||
|                 val season = | ||||
|                     s.selectFirst("> div.card-header > button > span").text().replace("Season: ", "").toIntOrNull() | ||||
|  | @ -154,7 +154,7 @@ class MeloMovieProvider : MainAPI() { | |||
|                 for (e in localEpisodes) { | ||||
|                     val episode = | ||||
|                         e.selectFirst("> div.card-header > button > span").text().replace("Episode: ", "").toIntOrNull() | ||||
|                     val links = e.selectFirst("> div.collapse > div > table.accordion__list") | ||||
|                     val links = e.selectFirst("> div.collapse > div > table.accordion__list") ?: continue | ||||
|                     val data = serializeData(links) | ||||
|                     episodes.add(TvSeriesEpisode(null, season, episode, data)) | ||||
|                 } | ||||
|  | @ -170,7 +170,7 @@ class MeloMovieProvider : MainAPI() { | |||
|                 year, | ||||
|                 plot, | ||||
|                 null, | ||||
|                 imdbUrl | ||||
|                 imdbUrlToIdNullable(imdbUrl) | ||||
|             ) | ||||
|         } | ||||
|         return null | ||||
|  |  | |||
|  | @ -2,7 +2,6 @@ package com.lagradost.cloudstream3.movieproviders | |||
| 
 | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.imdbUrlToId | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.Qualities | ||||
| import com.lagradost.cloudstream3.utils.SubtitleHelper | ||||
|  | @ -255,7 +254,7 @@ class TrailersToProvider : MainAPI() { | |||
|                 year, | ||||
|                 descript, | ||||
|                 null, | ||||
|                 imdbUrl, | ||||
|                 imdbUrlToIdNullable(imdbUrl), | ||||
|                 rating, | ||||
|                 tags, | ||||
|                 duration, | ||||
|  | @ -283,7 +282,7 @@ class TrailersToProvider : MainAPI() { | |||
|                 poster, | ||||
|                 year, | ||||
|                 descript, | ||||
|                 imdbUrl, | ||||
|                 imdbUrlToIdNullable(imdbUrl), | ||||
|                 rating, | ||||
|                 tags, | ||||
|                 duration, | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import androidx.lifecycle.LifecycleOwner | |||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.Observer | ||||
| import com.bumptech.glide.load.HttpException | ||||
| import com.lagradost.cloudstream3.ui.ErrorLoadingException | ||||
| import com.lagradost.cloudstream3.ErrorLoadingException | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.withContext | ||||
| import java.net.SocketTimeoutException | ||||
|  | @ -30,7 +30,8 @@ sealed class Resource<out T> { | |||
|         val errorResponse: Any?, //ResponseBody | ||||
|         val errorString: String, | ||||
|     ) : Resource<Nothing>() | ||||
|     data class Loading(val url : String? = null)  : Resource<Nothing>() | ||||
| 
 | ||||
|     data class Loading(val url: String? = null) : Resource<Nothing>() | ||||
| } | ||||
| 
 | ||||
| fun logError(throwable: Throwable) { | ||||
|  | @ -41,7 +42,7 @@ fun logError(throwable: Throwable) { | |||
|     Log.d("ApiError", "-------------------------------------------------------------------") | ||||
| } | ||||
| 
 | ||||
| fun<T> normalSafeApiCall(apiCall : () -> T) : T? { | ||||
| fun <T> normalSafeApiCall(apiCall: () -> T): T? { | ||||
|     return try { | ||||
|         apiCall.invoke() | ||||
|     } catch (throwable: Throwable) { | ||||
|  | @ -69,7 +70,7 @@ suspend fun <T> safeApiCall( | |||
|                     Resource.Failure(true, null, null, "Cannot connect to server, try again later.") | ||||
|                 } | ||||
|                 is ErrorLoadingException -> { | ||||
|                     Resource.Failure(true, null, null, "Error loading, try again later.") | ||||
|                     Resource.Failure(true, null, null, throwable.message ?: "Error loading, try again later.") | ||||
|                 } | ||||
|                 else -> { | ||||
|                     val stackTraceMsg = throwable.localizedMessage + "\n\n" + throwable.stackTrace.joinToString( | ||||
|  |  | |||
|  | @ -6,8 +6,6 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | |||
| import com.lagradost.cloudstream3.mvvm.safeApiCall | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| 
 | ||||
| class ErrorLoadingException(message: String) : Exception(message) | ||||
| 
 | ||||
| class APIRepository(val api: MainAPI) { | ||||
|     val name: String get() = api.name | ||||
|     val mainUrl: String get() = api.mainUrl | ||||
|  | @ -15,25 +13,25 @@ class APIRepository(val api: MainAPI) { | |||
|     suspend fun load(url: String): Resource<LoadResponse> { | ||||
|         return safeApiCall { | ||||
|             // remove suffix for some slugs to handle correctly | ||||
|             api.load(url.removeSuffix("/")) ?: throw ErrorLoadingException("Error Loading") | ||||
|             api.load(url.removeSuffix("/")) ?: throw ErrorLoadingException() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     suspend fun search(query: String): Resource<ArrayList<SearchResponse>> { | ||||
|         return safeApiCall { | ||||
|             api.search(query) ?: throw ErrorLoadingException("Error Loading") | ||||
|             api.search(query) ?: throw ErrorLoadingException() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     suspend fun quickSearch(query: String): Resource<ArrayList<SearchResponse>> { | ||||
|         return safeApiCall { | ||||
|             api.quickSearch(query) ?: throw ErrorLoadingException("Error Loading") | ||||
|             api.quickSearch(query) ?: throw ErrorLoadingException() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     suspend fun getMainPage(): Resource<HomePageResponse> { | ||||
|         return safeApiCall { | ||||
|             api.getMainPage() ?: throw ErrorLoadingException("Error Loading") | ||||
|             api.getMainPage() ?: throw ErrorLoadingException() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -23,8 +23,6 @@ import com.google.android.gms.cast.framework.media.widget.ExpandedControllerActi | |||
| import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.SubtitleFile | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.safeApiCall | ||||
| import com.lagradost.cloudstream3.sortUrls | ||||
| import com.lagradost.cloudstream3.ui.result.ResultEpisode | ||||
| import com.lagradost.cloudstream3.utils.CastHelper.awaitLinks | ||||
|  | @ -97,7 +95,6 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | |||
|             //  lateinit var dialog: AlertDialog | ||||
|             val holder = getCurrentMetaData() | ||||
| 
 | ||||
| 
 | ||||
|             if (holder != null) { | ||||
|                 val items = holder.currentLinks | ||||
|                 if (items.isNotEmpty() && remoteMediaClient?.currentItem != null) { | ||||
|  | @ -251,7 +248,6 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | |||
|         ) VISIBLE else INVISIBLE | ||||
|         try { | ||||
|             if (meta != null && meta.episodes.size > meta.currentEpisodeIndex + 1) { | ||||
| 
 | ||||
|                 val currentIdIndex = remoteMediaClient?.getItemIndex() ?: return | ||||
|                 val itemCount = remoteMediaClient?.mediaQueue?.itemCount | ||||
| 
 | ||||
|  | @ -264,8 +260,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | |||
|                         val links = ArrayList<ExtractorLink>() | ||||
|                         val subs = ArrayList<SubtitleFile>() | ||||
| 
 | ||||
|                         val res = safeApiCall { | ||||
|                             getApiFromName(meta.apiName).loadLinks(epData.data, true, { subtitleFile -> | ||||
|                         val isSuccessful = | ||||
|                             APIRepository(getApiFromName(meta.apiName)).loadLinks(epData.data, true, { subtitleFile -> | ||||
|                                 if (!subs.any { it.url == subtitleFile.url }) { | ||||
|                                     subs.add(subtitleFile) | ||||
|                                 } | ||||
|  | @ -274,9 +270,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi | |||
|                                     links.add(link) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         if (res is Resource.Success) { | ||||
|                         if (isSuccessful) { | ||||
|                             val sorted = sortUrls(links) | ||||
|                             if (sorted.isNotEmpty()) { | ||||
|                                 val jsonCopy = meta.copy( | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ import android.content.pm.ActivityInfo | |||
| import android.content.res.Resources | ||||
| import android.database.ContentObserver | ||||
| import android.graphics.Color | ||||
| import android.graphics.Typeface | ||||
| import android.graphics.drawable.Icon | ||||
| import android.media.AudioManager | ||||
| import android.net.Uri | ||||
|  | @ -25,8 +26,7 @@ import android.view.animation.AccelerateInterpolator | |||
| import android.view.animation.AlphaAnimation | ||||
| import android.view.animation.Animation | ||||
| import android.view.animation.AnimationUtils | ||||
| import android.widget.ProgressBar | ||||
| import android.widget.Toast | ||||
| import android.widget.* | ||||
| import android.widget.Toast.LENGTH_SHORT | ||||
| import androidx.appcompat.app.AlertDialog | ||||
| import androidx.core.content.ContextCompat | ||||
|  | @ -45,6 +45,8 @@ import com.google.android.exoplayer2.C.TIME_UNSET | |||
| import com.google.android.exoplayer2.source.DefaultMediaSourceFactory | ||||
| import com.google.android.exoplayer2.trackselection.DefaultTrackSelector | ||||
| import com.google.android.exoplayer2.ui.AspectRatioFrameLayout | ||||
| import com.google.android.exoplayer2.ui.CaptionStyleCompat | ||||
| import com.google.android.exoplayer2.ui.SubtitleView | ||||
| import com.google.android.exoplayer2.upstream.DataSource | ||||
| import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory | ||||
| import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory | ||||
|  | @ -53,17 +55,11 @@ import com.google.android.exoplayer2.util.Util | |||
| import com.google.android.gms.cast.framework.CastButtonFactory | ||||
| import com.google.android.gms.cast.framework.CastContext | ||||
| import com.google.android.gms.cast.framework.CastState | ||||
| import com.google.android.material.button.MaterialButton | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.isInPIPMode | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.isInPlayer | ||||
| import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode | ||||
| import com.lagradost.cloudstream3.R | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getNavigationBarHeight | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.toPx | ||||
| import com.lagradost.cloudstream3.mvvm.Resource | ||||
| import com.lagradost.cloudstream3.mvvm.observe | ||||
| import com.lagradost.cloudstream3.mvvm.observeDirectly | ||||
|  | @ -79,7 +75,13 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey | |||
| import com.lagradost.cloudstream3.utils.DataStore.setKey | ||||
| import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.UIHelper | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getNavigationBarHeight | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI | ||||
| import com.lagradost.cloudstream3.utils.UIHelper.toPx | ||||
| import com.lagradost.cloudstream3.utils.VIDEO_PLAYER_BRIGHTNESS | ||||
| import com.lagradost.cloudstream3.utils.getId | ||||
| import kotlinx.android.synthetic.main.fragment_player.* | ||||
|  | @ -791,6 +793,14 @@ class PlayerFragment : Fragment() { | |||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
| 
 | ||||
|         val subs = player_view.findViewById<SubtitleView>(R.id.exo_subtitles) | ||||
|         subs.setStyle( | ||||
|             CaptionStyleCompat( | ||||
|                 Color.WHITE, Color.TRANSPARENT, Color.TRANSPARENT, CaptionStyleCompat.EDGE_TYPE_OUTLINE, Color.BLACK, | ||||
|                 Typeface.SANS_SERIF | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|         settingsManager = PreferenceManager.getDefaultSharedPreferences(activity) | ||||
|         swipeEnabled = settingsManager.getBoolean("swipe_enabled", true) | ||||
|         swipeVerticalEnabled = settingsManager.getBoolean("swipe_vertical_enabled", true) | ||||
|  | @ -800,8 +810,6 @@ class PlayerFragment : Fragment() { | |||
| 
 | ||||
|         brightness_overlay?.alpha = context?.getKey(VIDEO_PLAYER_BRIGHTNESS, 0f) ?: 0f | ||||
| 
 | ||||
|         isInPlayer = true // NEED REFERENCE TO MAIN ACTIVITY FOR PIP | ||||
| 
 | ||||
|         navigationBarHeight = requireContext().getNavigationBarHeight() | ||||
|         statusBarHeight = requireContext().getStatusBarHeight() | ||||
| 
 | ||||
|  | @ -898,9 +906,9 @@ class PlayerFragment : Fragment() { | |||
|         } | ||||
| 
 | ||||
|         sources_btt.visibility = | ||||
|             if (isDownloadedFile) View.GONE else View.VISIBLE | ||||
|             if (isDownloadedFile) GONE else VISIBLE | ||||
|         player_media_route_button.visibility = | ||||
|             if (isDownloadedFile) View.GONE else View.VISIBLE | ||||
|             if (isDownloadedFile) GONE else VISIBLE | ||||
|         if (savedInstanceState != null) { | ||||
|             currentWindow = savedInstanceState.getInt(STATE_RESUME_WINDOW) | ||||
|             playbackPosition = savedInstanceState.getLong(STATE_RESUME_POSITION) | ||||
|  | @ -1138,27 +1146,126 @@ class PlayerFragment : Fragment() { | |||
|             lateinit var dialog: AlertDialog | ||||
|             getUrls()?.let { it1 -> | ||||
|                 sortUrls(it1).let { sources -> | ||||
|                     val isPlaying = exoPlayer.isPlaying | ||||
|                     exoPlayer.pause() | ||||
|                     val currentSubtitles = activeSubtitles | ||||
| 
 | ||||
|                     val sourceBuilder = AlertDialog.Builder(view.context, R.style.AlertDialogCustomBlack) | ||||
|                         .setView(R.layout.player_select_source_and_subs) | ||||
| 
 | ||||
|                     val sourceDialog = sourceBuilder.create() | ||||
|                     sourceDialog.show() | ||||
|                     //  bottomSheetDialog.setContentView(R.layout.sort_bottom_sheet) | ||||
|                     val providerList = sourceDialog.findViewById<ListView>(R.id.sort_providers)!! | ||||
|                     val subtitleList = sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!! | ||||
|                     val applyButton = sourceDialog.findViewById<MaterialButton>(R.id.pick_source_apply)!! | ||||
|                     val cancelButton = sourceDialog.findViewById<MaterialButton>(R.id.pick_source_cancel)!! | ||||
| 
 | ||||
|                     val startSource = sources.indexOf(getCurrentUrl()) | ||||
|                     var sourceIndex = startSource | ||||
|                     val startSubtitle = currentSubtitles.indexOf(preferredSubtitles) + 1 | ||||
|                     var subtitleIndex = startSubtitle | ||||
| 
 | ||||
|                     if (currentSubtitles.isEmpty()) { | ||||
|                         sourceDialog.findViewById<LinearLayout>(R.id.sort_subtitles_holder)?.visibility = GONE | ||||
|                     } else { | ||||
|                         val subsArrayAdapter = ArrayAdapter<String>(view.context, R.layout.sort_bottom_single_choice) | ||||
|                         subsArrayAdapter.add("No Subtitles") | ||||
|                         subsArrayAdapter.addAll(currentSubtitles) | ||||
| 
 | ||||
|                         subtitleList.adapter = subsArrayAdapter | ||||
|                         subtitleList.choiceMode = AbsListView.CHOICE_MODE_SINGLE | ||||
| 
 | ||||
|                         subtitleList.setSelection(subtitleIndex) | ||||
|                         subtitleList.setItemChecked(subtitleIndex, true) | ||||
| 
 | ||||
|                         subtitleList.setOnItemClickListener { _, _, which, _ -> | ||||
|                             subtitleIndex = which | ||||
|                             subtitleList.setItemChecked(which, true) | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     val sourcesArrayAdapter = ArrayAdapter<String>(view.context, R.layout.sort_bottom_single_choice) | ||||
|                     sourcesArrayAdapter.addAll(sources.map { it.name }) | ||||
| 
 | ||||
|                     providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE | ||||
|                     providerList.adapter = sourcesArrayAdapter | ||||
|                     providerList.setSelection(sourceIndex) | ||||
|                     providerList.setItemChecked(sourceIndex, true) | ||||
| 
 | ||||
|                     providerList.setOnItemClickListener { _, _, which, _ -> | ||||
|                         sourceIndex = which | ||||
|                         providerList.setItemChecked(which, true) | ||||
|                     } | ||||
| 
 | ||||
|                     sourceDialog.setOnDismissListener { | ||||
|                         activity?.hideSystemUI() | ||||
|                     } | ||||
| 
 | ||||
|                     cancelButton.setOnClickListener { | ||||
|                         sourceDialog.dismiss() | ||||
|                     } | ||||
| 
 | ||||
|                     applyButton.setOnClickListener { | ||||
|                         if (sourceIndex != startSource) { | ||||
|                             playbackPosition = if (this::exoPlayer.isInitialized) exoPlayer.currentPosition else 0 | ||||
|                             setMirrorId(sources[sourceIndex].getId()) | ||||
|                             initPlayer(getCurrentUrl()) | ||||
|                         } else { | ||||
|                             if (isPlaying) { | ||||
|                                 // exoPlayer.play() | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         if (subtitleIndex != startSubtitle) { | ||||
|                             val textRendererIndex = getRendererIndex(C.TRACK_TYPE_TEXT) ?: return@setOnClickListener | ||||
|                             (exoPlayer.trackSelector as DefaultTrackSelector?)?.let { trackSelector -> | ||||
|                                 if (subtitleIndex <= 0) { | ||||
|                                     preferredSubtitles = "" | ||||
|                                     trackSelector.setParameters( | ||||
|                                         trackSelector.buildUponParameters() | ||||
|                                             .setPreferredTextLanguage("") | ||||
|                                             .setRendererDisabled(textRendererIndex, true) | ||||
|                                     ) | ||||
|                                 } else { | ||||
|                                     val currentPreferredSub = currentSubtitles[subtitleIndex - 1] | ||||
|                                     preferredSubtitles = currentPreferredSub | ||||
|                                     trackSelector.setParameters( | ||||
|                                         trackSelector.buildUponParameters() | ||||
|                                             .setPreferredTextLanguage(currentPreferredSub) | ||||
|                                             .setRendererDisabled(textRendererIndex, false) | ||||
|                                     ) | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         sourceDialog.dismiss() | ||||
|                     } | ||||
|                     /* | ||||
| 
 | ||||
|                      */ | ||||
|                     /* | ||||
| 
 | ||||
|                     val sourcesText = sources.map { it.name } | ||||
|                     val builder = AlertDialog.Builder(requireContext(), R.style.AlertDialogCustom) | ||||
|                     builder.setTitle("Pick source") | ||||
|                     builder.setOnDismissListener { | ||||
|                         activity?.hideSystemUI() | ||||
|                     } | ||||
|                     builder.setSingleChoiceItems( | ||||
|                         sourcesText.toTypedArray(), | ||||
|                         sources.indexOf(getCurrentUrl()) | ||||
|                     ) { _, which -> | ||||
|                         //val speed = speedsText[which] | ||||
|                         //Toast.makeText(requireContext(), "$speed selected.", Toast.LENGTH_SHORT).show() | ||||
|                         playbackPosition = if (this::exoPlayer.isInitialized) exoPlayer.currentPosition else 0 | ||||
|                         setMirrorId(sources[which].getId()) | ||||
|                         initPlayer(getCurrentUrl()) | ||||
|                         builder.setTitle("Pick source") | ||||
|                         builder.setOnDismissListener { | ||||
|                             activity?.hideSystemUI() | ||||
|                         } | ||||
|                         builder.setSingleChoiceItems( | ||||
|                             sourcesText.toTypedArray(), | ||||
|                             sources.indexOf(getCurrentUrl()) | ||||
|                         ) { _, which -> | ||||
|                             //val speed = speedsText[which] | ||||
|                             //Toast.makeText(requireContext(), "$speed selected.", Toast.LENGTH_SHORT).show() | ||||
|                             playbackPosition = if (this::exoPlayer.isInitialized) exoPlayer.currentPosition else 0 | ||||
|                             setMirrorId(sources[which].getId()) | ||||
|                             initPlayer(getCurrentUrl()) | ||||
| 
 | ||||
|                         dialog.dismiss() | ||||
|                         activity?.hideSystemUI() | ||||
|                     } | ||||
|                     dialog = builder.create() | ||||
|                     dialog.show() | ||||
|                             dialog.dismiss() | ||||
|                             activity?.hideSystemUI() | ||||
|                         } | ||||
|                         dialog = builder.create() | ||||
|                         dialog.show()*/ | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -1192,6 +1299,18 @@ class PlayerFragment : Fragment() { | |||
|         // initPlayer() | ||||
|     } | ||||
| 
 | ||||
|     private fun getRendererIndex(trackIndex: Int): Int? { | ||||
|         if (!this::exoPlayer.isInitialized) return null | ||||
| 
 | ||||
|         for (renderIndex in 0 until exoPlayer.rendererCount) { | ||||
|             if (exoPlayer.getRendererType(renderIndex) == renderIndex) { | ||||
|                 return renderIndex | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
|     private fun getCurrentUrl(): ExtractorLink? { | ||||
|         val urls = getUrls() ?: return null | ||||
|         for (i in urls) { | ||||
|  | @ -1201,12 +1320,6 @@ class PlayerFragment : Fragment() { | |||
|         } | ||||
| 
 | ||||
|         return null | ||||
|         /*ExtractorLink("", | ||||
|                 "TEST", | ||||
|                 "https://v6.4animu.me/Overlord/Overlord-Episode-01-1080p.mp4", | ||||
|                 //"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", | ||||
|                 "", | ||||
|                 0)*/ | ||||
|     } | ||||
| 
 | ||||
|     private fun getUrls(): List<ExtractorLink>? { | ||||
|  | @ -1306,7 +1419,7 @@ class PlayerFragment : Fragment() { | |||
|         savePos() | ||||
| 
 | ||||
|         super.onDestroy() | ||||
|         isInPlayer = false | ||||
|         canEnterPipMode = false | ||||
| 
 | ||||
|         savePositionInPlayer() | ||||
|         safeReleasePlayer() | ||||
|  | @ -1377,6 +1490,18 @@ class PlayerFragment : Fragment() { | |||
| 
 | ||||
|     private val updateProgressAction = Runnable { updateProgressBar() }*/ | ||||
| 
 | ||||
|     private fun String.toSubtitleMimeType(): String { | ||||
|         return when { | ||||
|             endsWith("vtt", true) -> MimeTypes.TEXT_VTT | ||||
|             endsWith("srt", true) -> MimeTypes.APPLICATION_SUBRIP | ||||
|             endsWith("xml", true) || endsWith("ttml", true) -> MimeTypes.APPLICATION_TTML | ||||
|             else -> MimeTypes.TEXT_VTT | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     var activeSubtitles: List<String> = listOf() | ||||
|     var preferredSubtitles: String = "" | ||||
| 
 | ||||
|     @SuppressLint("SetTextI18n") | ||||
|     fun initPlayer(currentUrl: ExtractorLink?, uri: String? = null) { | ||||
|         if (currentUrl == null && uri == null) return | ||||
|  | @ -1384,7 +1509,6 @@ class PlayerFragment : Fragment() { | |||
|         hasUsedFirstRender = false | ||||
| 
 | ||||
|         try { | ||||
|             if (!isInPlayer) return | ||||
|             if (this::exoPlayer.isInitialized) { | ||||
|                 savePos() | ||||
|                 exoPlayer.release() | ||||
|  | @ -1446,11 +1570,35 @@ class PlayerFragment : Fragment() { | |||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             val subs = getSubs() | ||||
|             if (subs != null) { | ||||
|                 val subItems = ArrayList<MediaItem.Subtitle>() | ||||
|                 val subItemsId = ArrayList<String>() | ||||
| 
 | ||||
|                 for (sub in sortSubs(subs)) { | ||||
|                     val langId = sub.lang //SubtitleHelper.fromLanguageToTwoLetters(it.lang) ?: it.lang | ||||
|                     subItemsId.add(langId) | ||||
|                     subItems.add( | ||||
|                         MediaItem.Subtitle( | ||||
|                             Uri.parse(sub.url), | ||||
|                             sub.url.toSubtitleMimeType(), | ||||
|                             langId, | ||||
|                             C.SELECTION_FLAG_DEFAULT | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 activeSubtitles = subItemsId | ||||
|                 mediaItemBuilder.setSubtitles(subItems) | ||||
|             } | ||||
| 
 | ||||
| //might add https://github.com/ed828a/Aihua/blob/1896f46888b5a954b367e83f40b845ce174a2328/app/src/main/java/com/dew/aihua/player/playerUI/VideoPlayer.kt#L287 toggle caps | ||||
| 
 | ||||
|             val mediaItem = mediaItemBuilder.build() | ||||
|             val trackSelector = DefaultTrackSelector(requireContext()) | ||||
|             // Disable subtitles | ||||
|             trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(requireContext()) | ||||
|                 .setRendererDisabled(C.TRACK_TYPE_VIDEO, true) | ||||
|                 // .setRendererDisabled(C.TRACK_TYPE_VIDEO, true) | ||||
|                 .setRendererDisabled(C.TRACK_TYPE_TEXT, true) | ||||
|                 .setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT) | ||||
|                 .clearSelectionOverrides() | ||||
|  | @ -1530,6 +1678,18 @@ class PlayerFragment : Fragment() { | |||
| 
 | ||||
|             */ | ||||
| 
 | ||||
|             /*exoPlayer.addTextOutput { list -> | ||||
|                 if (list.size == 0) return@addTextOutput | ||||
| 
 | ||||
|                 val textBuilder = StringBuilder() | ||||
|                 for (cue in list) { | ||||
|                     textBuilder.append(cue.text).append("\n") | ||||
|                 } | ||||
|                 val subtitleText = if (textBuilder.isNotEmpty()) | ||||
|                     textBuilder.substring(0, textBuilder.length - 1) | ||||
|                 else | ||||
|                     textBuilder.toString() | ||||
|             }*/ | ||||
| 
 | ||||
|             //https://stackoverflow.com/questions/47731779/detect-pause-resume-in-exoplayer | ||||
|             exoPlayer.addListener(object : Player.Listener { | ||||
|  | @ -1573,6 +1733,7 @@ class PlayerFragment : Fragment() { | |||
|                 } | ||||
| 
 | ||||
|                 override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { | ||||
|                     canEnterPipMode = exoPlayer.isPlaying | ||||
|                     updatePIPModeActions() | ||||
|                     if (activity == null) return | ||||
|                     if (playWhenReady) { | ||||
|  |  | |||
|  | @ -1,11 +1,13 @@ | |||
| package com.lagradost.cloudstream3.ui.result | ||||
| 
 | ||||
| import android.annotation.SuppressLint | ||||
| import android.view.Gravity | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.widget.ImageView | ||||
| import android.widget.TextView | ||||
| import android.widget.Toast | ||||
| import androidx.annotation.LayoutRes | ||||
| import androidx.core.widget.ContentLoadingProgressBar | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
|  | @ -174,6 +176,15 @@ class EpisodeAdapter( | |||
|                 episodeDescript?.visibility = View.GONE | ||||
|             } | ||||
| 
 | ||||
|             episodePoster?.setOnClickListener { | ||||
|                 clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) | ||||
|             } | ||||
| 
 | ||||
|             episodePoster?.setOnLongClickListener { | ||||
|                 Toast.makeText(it.context, R.string.play_episode_toast, Toast.LENGTH_SHORT).show() | ||||
|                 return@setOnLongClickListener true | ||||
|             } | ||||
| 
 | ||||
|             episodeHolder.setOnClickListener { | ||||
|                 clickCallback.invoke(EpisodeClickEvent(ACTION_CLICK_DEFAULT, card)) | ||||
|             } | ||||
|  | @ -197,7 +208,15 @@ class EpisodeAdapter( | |||
|                 downloadButton.setUpButton( | ||||
|                     downloadInfo?.fileLength, downloadInfo?.totalBytes, episodeDownloadBar, episodeDownloadImage, null, | ||||
|                     VideoDownloadHelper.DownloadEpisodeCached( | ||||
|                         card.name, card.poster, card.episode, card.season, card.id, 0, card.rating, card.descript, System.currentTimeMillis(), | ||||
|                         card.name, | ||||
|                         card.poster, | ||||
|                         card.episode, | ||||
|                         card.season, | ||||
|                         card.id, | ||||
|                         0, | ||||
|                         card.rating, | ||||
|                         card.descript, | ||||
|                         System.currentTimeMillis(), | ||||
|                     ) | ||||
|                 ) { | ||||
|                     if (it.action == DOWNLOAD_ACTION_DOWNLOAD) { | ||||
|  |  | |||
|  | @ -155,12 +155,4 @@ object AppUtils { | |||
|         } | ||||
|         return currentAudioFocusRequest | ||||
|     } | ||||
| 
 | ||||
|     /** https://www.imdb.com/title/tt2861424/ -> tt2861424 */ | ||||
|     fun imdbUrlToId(url: String): String { | ||||
|         return url | ||||
|             .removePrefix("https://www.imdb.com/title/") | ||||
|             .removePrefix("https://imdb.com/title/tt2861424/") | ||||
|             .replace("/", "") | ||||
|     } | ||||
| } | ||||
							
								
								
									
										5
									
								
								app/src/main/res/color/check_selection_color.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/color/check_selection_color.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item android:state_activated="true" android:color="?attr/textColor"/> | ||||
|     <item android:color="@color/transparent"/> | ||||
| </selector> | ||||
							
								
								
									
										5
									
								
								app/src/main/res/color/text_selection_color.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/color/text_selection_color.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <selector xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <item android:state_activated="true" android:color="?attr/textColor"/> | ||||
|     <item android:color="?attr/grayTextColor"/> | ||||
| </selector> | ||||
|  | @ -1,17 +1,19 @@ | |||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|              xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|              xmlns:tools="http://schemas.android.com/tools" | ||||
|              android:layout_width="match_parent" | ||||
|              android:layout_height="match_parent" | ||||
|              android:orientation="vertical" | ||||
|              android:id="@+id/player_holder" | ||||
|              android:screenOrientation="landscape" | ||||
|              tools:orientation="vertical" | ||||
| <FrameLayout | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|         xmlns:tools="http://schemas.android.com/tools" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:orientation="vertical" | ||||
|         android:id="@+id/player_holder" | ||||
|         android:screenOrientation="landscape" | ||||
|         tools:orientation="vertical" | ||||
| > | ||||
|     <View android:layout_width="match_parent" | ||||
|           android:layout_height="match_parent" | ||||
|           android:id="@+id/shadow_overlay" | ||||
|           android:background="@color/black_overlay" | ||||
|     <View | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:id="@+id/shadow_overlay" | ||||
|             android:background="@color/black_overlay" | ||||
|     /> | ||||
|     <!-- | ||||
|         <LinearLayout android:layout_width="match_parent" | ||||
|  | @ -383,7 +385,7 @@ | |||
|                         android:textColor="@android:color/white" | ||||
|                         android:textSize="14sp" | ||||
|                         android:textStyle="normal"/> | ||||
| <!--app:buffered_color="@color/videoCache"--> | ||||
|                 <!--app:buffered_color="@color/videoCache"--> | ||||
|                 <com.google.android.exoplayer2.ui.DefaultTimeBar | ||||
|                         android:id="@id/exo_progress" | ||||
|                         android:layout_width="0dp" | ||||
|  | @ -471,7 +473,8 @@ | |||
| 
 | ||||
|                     </LinearLayout> | ||||
|                 </androidx.cardview.widget.CardView> | ||||
|                 <LinearLayout android:id="@+id/lock_holder" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="match_parent"> | ||||
|                 <LinearLayout android:id="@+id/lock_holder" android:orientation="horizontal" | ||||
|                               android:layout_width="wrap_content" android:layout_height="match_parent"> | ||||
|                     <androidx.cardview.widget.CardView | ||||
|                             xmlns:card_view="http://schemas.android.com/apk/res-auto" | ||||
|                             android:layout_width="wrap_content" | ||||
|  |  | |||
							
								
								
									
										131
									
								
								app/src/main/res/layout/player_select_source_and_subs.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								app/src/main/res/layout/player_select_source_and_subs.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|         android:orientation="vertical" | ||||
|         android:layout_width="match_parent" | ||||
|         android:background="@null" | ||||
|         android:layout_height="match_parent"> | ||||
|     <LinearLayout | ||||
|             android:orientation="horizontal" | ||||
|             android:layout_marginBottom="60dp" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:baselineAligned="false"> | ||||
| 
 | ||||
|         <LinearLayout | ||||
|                 android:layout_width="0dp" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:orientation="vertical" | ||||
|                 android:layout_weight="50"> | ||||
|             <TextView | ||||
|                     android:paddingStart="?android:attr/listPreferredItemPaddingStart" | ||||
|                     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" | ||||
|                     android:layout_marginTop="20dp" | ||||
|                     android:layout_marginBottom="10dp" | ||||
|                     android:textStyle="bold" | ||||
|                     android:text="@string/pick_source" | ||||
|                     android:textSize="20sp" | ||||
|                     android:textColor="?attr/textColor" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_rowWeight="1" | ||||
|                     android:layout_height="wrap_content"> | ||||
|             </TextView> | ||||
|             <ListView | ||||
|                     android:layout_marginTop="-10dp" | ||||
|                     android:paddingTop="10dp" | ||||
|                     android:id="@+id/sort_providers" | ||||
|                     android:background="?attr/bitDarkerGrayBackground" | ||||
|                     tools:listitem="@layout/sort_bottom_single_choice" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="match_parent" | ||||
|                     android:layout_rowWeight="1" | ||||
|             /> | ||||
|         </LinearLayout> | ||||
|         <LinearLayout | ||||
|                 android:id="@+id/sort_subtitles_holder" | ||||
|                 android:layout_width="0dp" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:orientation="vertical" | ||||
|                 android:layout_weight="50"> | ||||
|             <TextView | ||||
|                     android:paddingStart="?android:attr/listPreferredItemPaddingStart" | ||||
|                     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" | ||||
|                     android:layout_marginTop="20dp" | ||||
|                     android:layout_marginBottom="10dp" | ||||
|                     android:textStyle="bold" | ||||
|                     android:text="@string/pick_subtitle" | ||||
|                     android:textSize="20sp" | ||||
|                     android:textColor="?attr/textColor" | ||||
|                     android:layout_rowWeight="1" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_height="wrap_content"> | ||||
|             </TextView> | ||||
|             <ListView | ||||
|                     android:layout_marginTop="-10dp" | ||||
|                     android:paddingTop="10dp" | ||||
|                     android:id="@+id/sort_subtitles" | ||||
|                     android:background="?attr/bitDarkerGrayBackground" | ||||
|                     tools:listitem="@layout/sort_bottom_single_choice" | ||||
|                     android:layout_width="match_parent" | ||||
|                     android:layout_rowWeight="1" | ||||
|                     android:layout_height="match_parent"> | ||||
|             </ListView> | ||||
|         </LinearLayout> | ||||
|     </LinearLayout> | ||||
| 
 | ||||
|     <LinearLayout | ||||
|             android:orientation="horizontal" | ||||
|             android:layout_gravity="bottom" | ||||
|             android:gravity="bottom|end" | ||||
|             android:layout_marginTop="-60dp" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="60dp"> | ||||
| 
 | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|                 android:layout_gravity="center_vertical|end" | ||||
|                 android:layout_height="50dp" | ||||
|                 android:layout_margin="5dp" | ||||
| 
 | ||||
|                 android:visibility="visible" | ||||
|                 android:textStyle="bold" | ||||
|                 app:rippleColor="?attr/grayBackground" | ||||
|                 android:textColor="?attr/grayBackground" | ||||
|                 app:iconTint="?attr/grayBackground" | ||||
|                 android:textAllCaps="false" | ||||
|                 app:iconGravity="textStart" | ||||
|                 app:strokeColor="?attr/grayBackground" | ||||
|                 app:backgroundTint="?attr/textColor" | ||||
| 
 | ||||
|                 app:iconSize="20dp" | ||||
|                 android:text="@string/sort_apply" | ||||
|                 android:id="@+id/pick_source_apply" | ||||
|                 android:textSize="15sp" | ||||
|                 app:cornerRadius="5dp" | ||||
|                 android:layout_width="wrap_content" | ||||
|         > | ||||
|         </com.google.android.material.button.MaterialButton> | ||||
|         <com.google.android.material.button.MaterialButton | ||||
|                 android:layout_gravity="center_vertical|end" | ||||
|                 android:layout_height="50dp" | ||||
|                 android:layout_margin="5dp" | ||||
| 
 | ||||
|                 app:iconGravity="textStart" | ||||
|                 app:strokeColor="?attr/textColor" | ||||
|                 android:backgroundTint="?attr/grayBackground" | ||||
|                 app:rippleColor="?attr/textColor" | ||||
|                 android:textColor="?attr/textColor" | ||||
|                 app:iconTint="?attr/textColor" | ||||
|                 android:textAllCaps="false" | ||||
|                 android:textStyle="bold" | ||||
| 
 | ||||
|                 app:iconSize="20dp" | ||||
|                 android:text="@string/sort_cancel" | ||||
|                 android:id="@+id/pick_source_cancel" | ||||
|                 android:textSize="15sp" | ||||
|                 app:cornerRadius="5dp" | ||||
|                 android:layout_width="wrap_content" | ||||
|         > | ||||
|         </com.google.android.material.button.MaterialButton> | ||||
|     </LinearLayout> | ||||
| </LinearLayout> | ||||
|  | @ -22,11 +22,11 @@ | |||
|                 android:layout_height="wrap_content"> | ||||
|             <!--app:cardCornerRadius="@dimen/roundedImageRadius"--> | ||||
|             <androidx.cardview.widget.CardView | ||||
| 
 | ||||
|                     android:layout_width="126dp" | ||||
|                     android:layout_height="72dp" | ||||
|             > | ||||
|                 <ImageView | ||||
|                         android:foreground="?android:attr/selectableItemBackground" | ||||
|                         android:id="@+id/episode_poster" | ||||
|                         tools:src="@drawable/example_poster" | ||||
|                         android:scaleType="centerCrop" | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| <CheckedTextView | ||||
| <!--<CheckedTextView | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" | ||||
|         android:id="@android:id/text1" | ||||
|         android:layout_width="match_parent" | ||||
|  | @ -11,3 +11,27 @@ | |||
|         android:checkMark="?android:attr/listChoiceIndicatorSingle" | ||||
|         android:paddingStart="?android:attr/listPreferredItemPaddingStart" | ||||
|         android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/> | ||||
| --> | ||||
| 
 | ||||
| <TextView | ||||
|         xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" | ||||
|         style="@style/AppTextViewStyle" | ||||
|         android:id="@android:id/text1" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:minHeight="?android:attr/listPreferredItemHeightSmall" | ||||
|         android:textAppearance="?android:attr/textAppearanceSmall" | ||||
|         android:textColor="@color/text_selection_color" | ||||
|         android:textSize="16sp" | ||||
|         android:textStyle="bold" | ||||
|         android:gravity="center_vertical" | ||||
|         android:paddingStart="12dp" | ||||
|         android:paddingEnd="7dip" | ||||
|         tools:text="TEST" | ||||
|         android:checkMark="?android:attr/listChoiceIndicatorSingle" | ||||
|         android:ellipsize="marquee" | ||||
|         android:drawableStart="@drawable/ic_baseline_check_24" | ||||
|         android:drawableTint="@color/check_selection_color" | ||||
|         tools:drawableTint="?attr/textColor" | ||||
|         android:drawablePadding="20dp" | ||||
| /> | ||||
|  |  | |||
|  | @ -66,4 +66,7 @@ | |||
|     <string name="filter_bookmarks">Filter Bookmarks</string> | ||||
|     <string name="error_bookmarks_text">Bookmarks</string> | ||||
|     <string name="action_remove_from_bookmarks">Remove</string> | ||||
|     <string name="play_episode_toast">Play Episode</string> | ||||
|     <string name="sort_apply">Apply</string> | ||||
|     <string name="sort_cancel">Cancel</string> | ||||
| </resources> | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue