forked from recloudstream/cloudstream
		
	Added trailers
This commit is contained in:
		
							parent
							
								
									573b3488a3
								
							
						
					
					
						commit
						91a14dac0f
					
				
					 15 changed files with 782 additions and 107 deletions
				
			
		|  | @ -174,4 +174,7 @@ dependencies { | ||||||
| 
 | 
 | ||||||
|     // used for subtitle decoding https://github.com/albfernandez/juniversalchardet |     // used for subtitle decoding https://github.com/albfernandez/juniversalchardet | ||||||
|     implementation 'com.github.albfernandez:juniversalchardet:2.4.0' |     implementation 'com.github.albfernandez:juniversalchardet:2.4.0' | ||||||
|  | 
 | ||||||
|  |     // play yt | ||||||
|  |     implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT' | ||||||
| } | } | ||||||
|  | @ -901,6 +901,17 @@ interface LoadResponse { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         fun LoadResponse.addTrailer(trailerUrls: List<String>?) { | ||||||
|  |             if(trailerUrls == null) return | ||||||
|  |             if (this.trailers == null) { | ||||||
|  |                 this.trailers = trailerUrls | ||||||
|  |             } else { | ||||||
|  |                 val update = this.trailers?.toMutableList() | ||||||
|  |                 update?.addAll(trailerUrls) | ||||||
|  |                 this.trailers = update | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         fun LoadResponse.addImdbId(id: String?) { |         fun LoadResponse.addImdbId(id: String?) { | ||||||
|             // TODO add imdb sync |             // TODO add imdb sync | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -356,6 +356,40 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun test() { |     fun test() { | ||||||
|  |         //val youtubeLink = "https://www.youtube.com/watch?v=TxB48MEAmZw" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |         runBlocking { | ||||||
|  | 
 | ||||||
|  |             val query = """ | ||||||
|  |             query {  | ||||||
|  |                 searchShows(search: "spider", limit: 10) { | ||||||
|  |                     id | ||||||
|  |                     name | ||||||
|  |                     originalName | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             """ | ||||||
|  |             val data = | ||||||
|  |                 mapOf( | ||||||
|  |                     "query" to query, | ||||||
|  |                     //"variables" to | ||||||
|  |                     //        mapOf( | ||||||
|  |                     //            "name" to name, | ||||||
|  |                      //       ).toJson() | ||||||
|  |                 ) | ||||||
|  |             val txt = app.post( | ||||||
|  |                 "http://api.anime-skip.com/graphql", | ||||||
|  |                 headers = mapOf( | ||||||
|  |                     "X-Client-ID" to "", | ||||||
|  |                     "Content-Type" to "application/json", | ||||||
|  |                     "Accept" to "application/json", | ||||||
|  |                 ), | ||||||
|  |                 json = data | ||||||
|  |             ) | ||||||
|  |             println("TEXT: $txt") | ||||||
|  |         }*/ | ||||||
|         /*runBlocking { |         /*runBlocking { | ||||||
|             //https://test.api.anime-skip.com/graphiql |             //https://test.api.anime-skip.com/graphiql | ||||||
|             val txt = app.get( |             val txt = app.get( | ||||||
|  |  | ||||||
|  | @ -4,10 +4,12 @@ import com.fasterxml.jackson.annotation.JsonProperty | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addActors | import com.lagradost.cloudstream3.LoadResponse.Companion.addActors | ||||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId | import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId | ||||||
|  | import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | ||||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||||
| import com.uwetrottmann.tmdb2.Tmdb | import com.uwetrottmann.tmdb2.Tmdb | ||||||
| import com.uwetrottmann.tmdb2.entities.* | import com.uwetrottmann.tmdb2.entities.* | ||||||
| import com.uwetrottmann.tmdb2.enumerations.AppendToResponseItem | import com.uwetrottmann.tmdb2.enumerations.AppendToResponseItem | ||||||
|  | import com.uwetrottmann.tmdb2.enumerations.VideoType | ||||||
| import retrofit2.awaitResponse | import retrofit2.awaitResponse | ||||||
| import java.util.* | import java.util.* | ||||||
| 
 | 
 | ||||||
|  | @ -144,6 +146,7 @@ open class TmdbProvider : MainAPI() { | ||||||
|             tags = genres?.mapNotNull { it.name } |             tags = genres?.mapNotNull { it.name } | ||||||
|             duration = episode_run_time?.average()?.toInt() |             duration = episode_run_time?.average()?.toInt() | ||||||
|             rating = this@toLoadResponse.rating |             rating = this@toLoadResponse.rating | ||||||
|  |             addTrailer(videos.toTrailers()) | ||||||
| 
 | 
 | ||||||
|             recommendations = (this@toLoadResponse.recommendations |             recommendations = (this@toLoadResponse.recommendations | ||||||
|                 ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } |                 ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } | ||||||
|  | @ -151,6 +154,19 @@ open class TmdbProvider : MainAPI() { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private fun Videos?.toTrailers(): List<String>? { | ||||||
|  |         return this?.results?.filter { it.type != VideoType.OPENING_CREDITS && it.type != VideoType.FEATURETTE } | ||||||
|  |             ?.sortedBy { it.type?.ordinal ?: 10000 } | ||||||
|  |             ?.mapNotNull { | ||||||
|  |                 when (it.site?.trim()?.lowercase()) { | ||||||
|  |                     "youtube" -> { // TODO FILL SITES | ||||||
|  |                         "https://www.youtube.com/watch?v=${it.key}" | ||||||
|  |                     } | ||||||
|  |                     else -> null | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun Movie.toLoadResponse(): MovieLoadResponse { |     private fun Movie.toLoadResponse(): MovieLoadResponse { | ||||||
|         return newMovieLoadResponse( |         return newMovieLoadResponse( | ||||||
|             this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink( |             this.title ?: this.original_title, getUrl(id, false), TvType.Movie, TmdbLink( | ||||||
|  | @ -172,6 +188,7 @@ open class TmdbProvider : MainAPI() { | ||||||
|             tags = genres?.mapNotNull { it.name } |             tags = genres?.mapNotNull { it.name } | ||||||
|             duration = runtime |             duration = runtime | ||||||
|             rating = this@toLoadResponse.rating |             rating = this@toLoadResponse.rating | ||||||
|  |             addTrailer(videos.toTrailers()) | ||||||
| 
 | 
 | ||||||
|             recommendations = (this@toLoadResponse.recommendations |             recommendations = (this@toLoadResponse.recommendations | ||||||
|                 ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } |                 ?: this@toLoadResponse.similar)?.results?.map { it.toSearchResponse() } | ||||||
|  | @ -261,7 +278,16 @@ open class TmdbProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|         return if (useMetaLoadResponse) { |         return if (useMetaLoadResponse) { | ||||||
|             return if (isTvSeries) { |             return if (isTvSeries) { | ||||||
|                 val body = tmdb.tvService().tv(id, "en-US", AppendToResponse(AppendToResponseItem.EXTERNAL_IDS)).awaitResponse().body() |                 val body = tmdb.tvService() | ||||||
|  |                     .tv( | ||||||
|  |                         id, | ||||||
|  |                         "en-US", | ||||||
|  |                         AppendToResponse( | ||||||
|  |                             AppendToResponseItem.EXTERNAL_IDS, | ||||||
|  |                             AppendToResponseItem.VIDEOS | ||||||
|  |                         ) | ||||||
|  |                     ) | ||||||
|  |                     .awaitResponse().body() | ||||||
|                 val response = body?.toLoadResponse() |                 val response = body?.toLoadResponse() | ||||||
|                 if (response != null) { |                 if (response != null) { | ||||||
|                     if (response.recommendations.isNullOrEmpty()) |                     if (response.recommendations.isNullOrEmpty()) | ||||||
|  | @ -280,7 +306,16 @@ open class TmdbProvider : MainAPI() { | ||||||
| 
 | 
 | ||||||
|                 response |                 response | ||||||
|             } else { |             } else { | ||||||
|                 val body = tmdb.moviesService().summary(id, "en-US", AppendToResponse(AppendToResponseItem.EXTERNAL_IDS)).awaitResponse().body() |                 val body = tmdb.moviesService() | ||||||
|  |                     .summary( | ||||||
|  |                         id, | ||||||
|  |                         "en-US", | ||||||
|  |                         AppendToResponse( | ||||||
|  |                             AppendToResponseItem.EXTERNAL_IDS, | ||||||
|  |                             AppendToResponseItem.VIDEOS | ||||||
|  |                         ) | ||||||
|  |                     ) | ||||||
|  |                     .awaitResponse().body() | ||||||
|                 val response = body?.toLoadResponse() |                 val response = body?.toLoadResponse() | ||||||
|                 if (response != null) { |                 if (response != null) { | ||||||
|                     if (response.recommendations.isNullOrEmpty()) |                     if (response.recommendations.isNullOrEmpty()) | ||||||
|  |  | ||||||
|  | @ -93,7 +93,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubProv | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private suspend fun initLogin(username: String, password: String): Boolean { |     private suspend fun initLogin(username: String, password: String): Boolean { | ||||||
|         Log.i(TAG, "DATA = [$username] [$password]") |         //Log.i(TAG, "DATA = [$username] [$password]") | ||||||
|         val response = app.post( |         val response = app.post( | ||||||
|             url = "$host/login", |             url = "$host/login", | ||||||
|             headers = mapOf( |             headers = mapOf( | ||||||
|  | @ -105,8 +105,8 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubProv | ||||||
|                 "password" to password |                 "password" to password | ||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|         Log.i(TAG, "Responsecode = ${response.code}") |         //Log.i(TAG, "Responsecode = ${response.code}") | ||||||
|         Log.i(TAG, "Result => ${response.text}") |         //Log.i(TAG, "Result => ${response.text}") | ||||||
| 
 | 
 | ||||||
|         if (response.isSuccessful) { |         if (response.isSuccessful) { | ||||||
|             AppUtils.tryParseJson<OAuthToken>(response.text)?.let { token -> |             AppUtils.tryParseJson<OAuthToken>(response.text)?.let { token -> | ||||||
|  |  | ||||||
|  | @ -68,6 +68,8 @@ abstract class AbstractPlayerFragment( | ||||||
|     var subStyle: SaveCaptionStyle? = null |     var subStyle: SaveCaptionStyle? = null | ||||||
|     var subView: SubtitleView? = null |     var subView: SubtitleView? = null | ||||||
|     var isBuffering = true |     var isBuffering = true | ||||||
|  |     protected open var hasPipModeSupport = true | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|     @LayoutRes |     @LayoutRes | ||||||
|     protected var layout: Int = R.layout.fragment_player |     protected var layout: Int = R.layout.fragment_player | ||||||
|  | @ -154,7 +156,7 @@ abstract class AbstractPlayerFragment( | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         canEnterPipMode = isPlayingRightNow |         canEnterPipMode = isPlayingRightNow && hasPipModeSupport | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isInPIPMode) { |         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isInPIPMode) { | ||||||
|             activity?.let { act -> |             activity?.let { act -> | ||||||
|                 PlayerPipHelper.updatePIPModeActions(act, isPlayingRightNow) |                 PlayerPipHelper.updatePIPModeActions(act, isPlayingRightNow) | ||||||
|  | @ -213,7 +215,13 @@ abstract class AbstractPlayerFragment( | ||||||
|         throw NotImplementedError() |         throw NotImplementedError() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun playerError(exception: Exception) { |     private fun requestAudioFocus() { | ||||||
|  |         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||||
|  |             activity?.requestLocalAudioFocus(AppUtils.getFocusRequest()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     open fun playerError(exception: Exception) { | ||||||
|         val ctx = context ?: return |         val ctx = context ?: return | ||||||
|         when (exception) { |         when (exception) { | ||||||
|             is PlaybackException -> { |             is PlaybackException -> { | ||||||
|  | @ -267,12 +275,6 @@ abstract class AbstractPlayerFragment( | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun requestAudioFocus() { |  | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |  | ||||||
|             activity?.requestLocalAudioFocus(AppUtils.getFocusRequest()) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private fun onSubStyleChanged(style: SaveCaptionStyle) { |     private fun onSubStyleChanged(style: SaveCaptionStyle) { | ||||||
|         if (player is CS3IPlayer) { |         if (player is CS3IPlayer) { | ||||||
|             player.updateSubtitleStyle(style) |             player.updateSubtitleStyle(style) | ||||||
|  |  | ||||||
|  | @ -1,11 +1,17 @@ | ||||||
| package com.lagradost.cloudstream3.ui.player | package com.lagradost.cloudstream3.ui.player | ||||||
| 
 | 
 | ||||||
|  | import android.annotation.SuppressLint | ||||||
| import android.content.Context | import android.content.Context | ||||||
| import android.net.Uri | import android.net.Uri | ||||||
| import android.os.Handler | import android.os.Handler | ||||||
| import android.os.Looper | import android.os.Looper | ||||||
| import android.util.Log | import android.util.Log | ||||||
|  | import android.util.SparseArray | ||||||
| import android.widget.FrameLayout | import android.widget.FrameLayout | ||||||
|  | import androidx.core.util.forEach | ||||||
|  | import at.huber.youtubeExtractor.VideoMeta | ||||||
|  | import at.huber.youtubeExtractor.YouTubeExtractor | ||||||
|  | import at.huber.youtubeExtractor.YtFile | ||||||
| import com.google.android.exoplayer2.* | import com.google.android.exoplayer2.* | ||||||
| import com.google.android.exoplayer2.database.StandaloneDatabaseProvider | import com.google.android.exoplayer2.database.StandaloneDatabaseProvider | ||||||
| import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource | import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource | ||||||
|  | @ -23,6 +29,7 @@ import com.google.android.exoplayer2.upstream.cache.CacheDataSource | ||||||
| import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor | import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor | ||||||
| import com.google.android.exoplayer2.upstream.cache.SimpleCache | import com.google.android.exoplayer2.upstream.cache.SimpleCache | ||||||
| import com.google.android.exoplayer2.util.MimeTypes | import com.google.android.exoplayer2.util.MimeTypes | ||||||
|  | import com.google.android.exoplayer2.video.VideoSize | ||||||
| import com.lagradost.cloudstream3.APIHolder.getApiFromName | import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||||
| import com.lagradost.cloudstream3.USER_AGENT | import com.lagradost.cloudstream3.USER_AGENT | ||||||
| import com.lagradost.cloudstream3.app | import com.lagradost.cloudstream3.app | ||||||
|  | @ -31,6 +38,7 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | ||||||
| import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle | import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
| import com.lagradost.cloudstream3.utils.ExtractorUri | import com.lagradost.cloudstream3.utils.ExtractorUri | ||||||
|  | import com.lagradost.cloudstream3.utils.Qualities | ||||||
| import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage | import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage | ||||||
| import java.io.File | import java.io.File | ||||||
| import javax.net.ssl.HttpsURLConnection | import javax.net.ssl.HttpsURLConnection | ||||||
|  | @ -153,7 +161,8 @@ class CS3IPlayer : IPlayer { | ||||||
|         data: ExtractorUri?, |         data: ExtractorUri?, | ||||||
|         startPosition: Long?, |         startPosition: Long?, | ||||||
|         subtitles: Set<SubtitleData>, |         subtitles: Set<SubtitleData>, | ||||||
|         subtitle: SubtitleData? |         subtitle: SubtitleData?, | ||||||
|  |         autoPlay: Boolean? | ||||||
|     ) { |     ) { | ||||||
|         Log.i(TAG, "loadPlayer") |         Log.i(TAG, "loadPlayer") | ||||||
|         if (sameEpisode) { |         if (sameEpisode) { | ||||||
|  | @ -168,7 +177,7 @@ class CS3IPlayer : IPlayer { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // we want autoplay because of TV and UX |         // we want autoplay because of TV and UX | ||||||
|         isPlaying = true |         isPlaying = autoPlay ?: isPlaying | ||||||
| 
 | 
 | ||||||
|         // release the current exoplayer and cache |         // release the current exoplayer and cache | ||||||
|         releasePlayer() |         releasePlayer() | ||||||
|  | @ -322,6 +331,7 @@ class CS3IPlayer : IPlayer { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
|  |         private var ytVideos: MutableMap<String, YtFile> = mutableMapOf() | ||||||
|         private var simpleCache: SimpleCache? = null |         private var simpleCache: SimpleCache? = null | ||||||
| 
 | 
 | ||||||
|         var requestSubtitleUpdate: (() -> Unit)? = null |         var requestSubtitleUpdate: (() -> Unit)? = null | ||||||
|  | @ -686,6 +696,14 @@ class CS3IPlayer : IPlayer { | ||||||
|                         isPlaying = exo.isPlaying |                         isPlaying = exo.isPlaying | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|  |                     when (playbackState) { | ||||||
|  |                         Player.STATE_READY -> { | ||||||
|  |                             onRenderFirst() | ||||||
|  |                         } | ||||||
|  |                         else -> {} | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|                     if (playWhenReady) { |                     if (playWhenReady) { | ||||||
|                         when (playbackState) { |                         when (playbackState) { | ||||||
|                             Player.STATE_READY -> { |                             Player.STATE_READY -> { | ||||||
|  | @ -715,9 +733,50 @@ class CS3IPlayer : IPlayer { | ||||||
|                 //    super.onCues(cues.map { cue -> cue.buildUpon().setText("Hello world").setSize(Cue.DIMEN_UNSET).build() }) |                 //    super.onCues(cues.map { cue -> cue.buildUpon().setText("Hello world").setSize(Cue.DIMEN_UNSET).build() }) | ||||||
|                 //} |                 //} | ||||||
| 
 | 
 | ||||||
|  |                 override fun onIsPlayingChanged(isPlaying: Boolean) { | ||||||
|  |                     super.onIsPlayingChanged(isPlaying) | ||||||
|  |                     if (isPlaying) { | ||||||
|  |                         onRenderFirst() | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun onPlaybackStateChanged(playbackState: Int) { | ||||||
|  |                     super.onPlaybackStateChanged(playbackState) | ||||||
|  |                     when (playbackState) { | ||||||
|  |                         Player.STATE_READY -> { | ||||||
|  |                             requestAutoFocus?.invoke() | ||||||
|  |                         } | ||||||
|  |                         Player.STATE_ENDED -> { | ||||||
|  |                             handleEvent(CSPlayerEvent.NextEpisode) | ||||||
|  |                         } | ||||||
|  |                         Player.STATE_BUFFERING -> { | ||||||
|  |                             updatedTime() | ||||||
|  |                         } | ||||||
|  |                         Player.STATE_IDLE -> { | ||||||
|  |                             // IDLE | ||||||
|  |                         } | ||||||
|  |                         else -> Unit | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 override fun onVideoSizeChanged(videoSize: VideoSize) { | ||||||
|  |                     super.onVideoSizeChanged(videoSize) | ||||||
|  |                     playerDimensionsLoaded?.invoke(Pair(videoSize.width, videoSize.height)) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 override fun onRenderedFirstFrame() { |                 override fun onRenderedFirstFrame() { | ||||||
|                     updatedTime() |                     updatedTime() | ||||||
|  |                     super.onRenderedFirstFrame() | ||||||
|  |                     onRenderFirst() | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |         } catch (e: Exception) { | ||||||
|  |             Log.e(TAG, "loadExo error", e) | ||||||
|  |             playerError?.invoke(e) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|  |     fun onRenderFirst() { | ||||||
|         if (!hasUsedFirstRender) { // this insures that we only call this once per player load |         if (!hasUsedFirstRender) { // this insures that we only call this once per player load | ||||||
|             Log.i(TAG, "Rendered first frame") |             Log.i(TAG, "Rendered first frame") | ||||||
| 
 | 
 | ||||||
|  | @ -725,6 +784,7 @@ class CS3IPlayer : IPlayer { | ||||||
|                 // Only errors short playback when not playing downloaded files |                 // Only errors short playback when not playing downloaded files | ||||||
|                 duration < 20_000L && currentDownloadedFile == null |                 duration < 20_000L && currentDownloadedFile == null | ||||||
|             } ?: false |             } ?: false | ||||||
|  | 
 | ||||||
|             if (invalid) { |             if (invalid) { | ||||||
|                 releasePlayer(saveTime = false) |                 releasePlayer(saveTime = false) | ||||||
|                 playerError?.invoke(InvalidFileException("Too short playback")) |                 playerError?.invoke(InvalidFileException("Too short playback")) | ||||||
|  | @ -753,13 +813,6 @@ class CS3IPlayer : IPlayer { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|                     super.onRenderedFirstFrame() |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
|         } catch (e: Exception) { |  | ||||||
|             Log.e(TAG, "loadExo error", e) |  | ||||||
|             playerError?.invoke(e) |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun loadOfflinePlayer(context: Context, data: ExtractorUri) { |     private fun loadOfflinePlayer(context: Context, data: ExtractorUri) { | ||||||
|  | @ -829,9 +882,55 @@ class CS3IPlayer : IPlayer { | ||||||
|         return Pair(subSources, activeSubtitles) |         return Pair(subSources, activeSubtitles) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |     fun loadYtFile(context: Context, yt: YtFile) { | ||||||
|  |         loadOnlinePlayer( | ||||||
|  |             context, | ||||||
|  |             ExtractorLink( | ||||||
|  |                 "YouTube", | ||||||
|  |                 "", | ||||||
|  |                 yt.url, | ||||||
|  |                 "", | ||||||
|  |                 yt.format?.height ?: Qualities.Unknown.value | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun loadOnlinePlayer(context: Context, link: ExtractorLink) { |     private fun loadOnlinePlayer(context: Context, link: ExtractorLink) { | ||||||
|         Log.i(TAG, "loadOnlinePlayer") |         Log.i(TAG, "loadOnlinePlayer $link") | ||||||
|         try { |         try { | ||||||
|  |             if (link.url.contains("youtube.com")) { | ||||||
|  |                 val ytLink = link.url.replace("/embed/", "/watch?v=") | ||||||
|  |                 ytVideos[ytLink]?.let { | ||||||
|  |                     loadYtFile(context, it) | ||||||
|  |                     return | ||||||
|  |                 } | ||||||
|  |                 val ytExtractor = | ||||||
|  |                     @SuppressLint("StaticFieldLeak") | ||||||
|  |                     object : YouTubeExtractor(context) { | ||||||
|  |                         override fun onExtractionComplete( | ||||||
|  |                             ytFiles: SparseArray<YtFile>?, | ||||||
|  |                             videoMeta: VideoMeta? | ||||||
|  |                         ) { | ||||||
|  |                             var yt: YtFile? = null | ||||||
|  |                             ytFiles?.forEach { _, value -> | ||||||
|  |                                 if ((yt?.format?.height ?: 0) < (value.format?.height | ||||||
|  |                                         ?: -1) && (value.format?.audioBitrate ?: -1) > 0 | ||||||
|  |                                 ) { | ||||||
|  |                                     yt = value | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             yt?.let { ytf -> | ||||||
|  |                                 ytVideos[ytLink] = ytf | ||||||
|  |                                 loadYtFile(context, ytf) | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 Log.i(TAG, "YouTube extraction on $ytLink") | ||||||
|  |                 ytExtractor.extract(ytLink) | ||||||
|  |                 return | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             currentLink = link |             currentLink = link | ||||||
| 
 | 
 | ||||||
|             if (ignoreSSL) { |             if (ignoreSSL) { | ||||||
|  |  | ||||||
|  | @ -51,6 +51,21 @@ import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI | ||||||
| import com.lagradost.cloudstream3.utils.UIHelper.toPx | import com.lagradost.cloudstream3.utils.UIHelper.toPx | ||||||
| import com.lagradost.cloudstream3.utils.Vector2 | import com.lagradost.cloudstream3.utils.Vector2 | ||||||
| import kotlinx.android.synthetic.main.player_custom_layout.* | import kotlinx.android.synthetic.main.player_custom_layout.* | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.bottom_player_bar | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.exo_ffwd | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.exo_ffwd_text | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.exo_progress | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.exo_rew | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.exo_rew_text | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_center_menu | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_ffwd_holder | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_holder | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_pause_play | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_pause_play_holder | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_rew_holder | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.player_video_bar | ||||||
|  | import kotlinx.android.synthetic.main.player_custom_layout.shadow_overlay | ||||||
|  | import kotlinx.android.synthetic.main.trailer_custom_layout.* | ||||||
| import kotlin.math.* | import kotlin.math.* | ||||||
| 
 | 
 | ||||||
| const val MINIMUM_SEEK_TIME = 7000L         // when swipe seeking | const val MINIMUM_SEEK_TIME = 7000L         // when swipe seeking | ||||||
|  | @ -64,6 +79,9 @@ const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15        // in both directions | ||||||
| 
 | 
 | ||||||
| // All the UI Logic for the player | // All the UI Logic for the player | ||||||
| open class FullScreenPlayer : AbstractPlayerFragment() { | open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|  |     protected open var lockRotation = true | ||||||
|  |     protected open var isFullScreenPlayer = true | ||||||
|  | 
 | ||||||
|     // state of player UI |     // state of player UI | ||||||
|     protected var isShowing = false |     protected var isShowing = false | ||||||
|     protected var isLocked = false |     protected var isLocked = false | ||||||
|  | @ -100,11 +118,11 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
| 
 | 
 | ||||||
|     // screenWidth and screenHeight does always |     // screenWidth and screenHeight does always | ||||||
|     // refer to the screen while in landscape mode |     // refer to the screen while in landscape mode | ||||||
|     private val screenWidth: Int |     protected val screenWidth: Int | ||||||
|         get() { |         get() { | ||||||
|             return max(displayMetrics.widthPixels, displayMetrics.heightPixels) |             return max(displayMetrics.widthPixels, displayMetrics.heightPixels) | ||||||
|         } |         } | ||||||
|     private val screenHeight: Int |     protected val screenHeight: Int | ||||||
|         get() { |         get() { | ||||||
|             return min(displayMetrics.widthPixels, displayMetrics.heightPixels) |             return min(displayMetrics.widthPixels, displayMetrics.heightPixels) | ||||||
|         } |         } | ||||||
|  | @ -159,7 +177,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|         animateLayoutChanges() |         animateLayoutChanges() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private fun animateLayoutChanges() { |     protected fun animateLayoutChanges() { | ||||||
|         if (isShowing) { |         if (isShowing) { | ||||||
|             updateUIVisibility() |             updateUIVisibility() | ||||||
|         } else { |         } else { | ||||||
|  | @ -234,19 +252,24 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun onResume() { |     override fun onResume() { | ||||||
|  |         if (isFullScreenPlayer) { | ||||||
|             activity?.hideSystemUI() |             activity?.hideSystemUI() | ||||||
|         activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE |  | ||||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && fullscreenNotch) { |             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && fullscreenNotch) { | ||||||
|                 val params = activity?.window?.attributes |                 val params = activity?.window?.attributes | ||||||
|                 params?.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES |                 params?.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES | ||||||
|                 activity?.window?.attributes = params |                 activity?.window?.attributes = params | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|  |         if (lockRotation) | ||||||
|  |             activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|         super.onResume() |         super.onResume() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun onDestroy() { |     override fun onDestroy() { | ||||||
|         activity?.showSystemUI() |         activity?.showSystemUI() | ||||||
|  |         if (lockRotation) | ||||||
|             activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER |             activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER | ||||||
| 
 | 
 | ||||||
|         // simply resets brightness and notch settings that might have been overridden |         // simply resets brightness and notch settings that might have been overridden | ||||||
|  | @ -336,6 +359,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             dialog.setOnDismissListener { |             dialog.setOnDismissListener { | ||||||
|  |                 if (isFullScreenPlayer) | ||||||
|                     activity?.hideSystemUI() |                     activity?.hideSystemUI() | ||||||
|             } |             } | ||||||
|             applyButton.setOnClickListener { |             applyButton.setOnClickListener { | ||||||
|  | @ -374,8 +398,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|                 act.getString(R.string.player_speed), |                 act.getString(R.string.player_speed), | ||||||
|                 false, |                 false, | ||||||
|                 { |                 { | ||||||
|  |                     if (isFullScreenPlayer) | ||||||
|                         activity?.hideSystemUI() |                         activity?.hideSystemUI() | ||||||
|                 }) { index -> |                 }) { index -> | ||||||
|  |                 if (isFullScreenPlayer) | ||||||
|                     activity?.hideSystemUI() |                     activity?.hideSystemUI() | ||||||
|                 setPlayBackSpeed(speedsNumbers[index]) |                 setPlayBackSpeed(speedsNumbers[index]) | ||||||
|             } |             } | ||||||
|  | @ -455,8 +481,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|     private fun onClickChange() { |     private fun onClickChange() { | ||||||
|         isShowing = !isShowing |         isShowing = !isShowing | ||||||
|         if (isShowing) { |         if (isShowing) { | ||||||
|  |             player_intro_play?.isGone = true | ||||||
|             autoHide() |             autoHide() | ||||||
|         } |         } | ||||||
|  |         if (isFullScreenPlayer) | ||||||
|             activity?.hideSystemUI() |             activity?.hideSystemUI() | ||||||
|         animateLayoutChanges() |         animateLayoutChanges() | ||||||
|         player_pause_play?.requestFocus() |         player_pause_play?.requestFocus() | ||||||
|  | @ -692,7 +720,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|         if (event == null || view == null) return false |         if (event == null || view == null) return false | ||||||
|         val currentTouch = Vector2(event.x, event.y) |         val currentTouch = Vector2(event.x, event.y) | ||||||
|         val startTouch = currentTouchStart |         val startTouch = currentTouchStart | ||||||
| 
 |         player_intro_play?.isGone = true | ||||||
|         when (event.action) { |         when (event.action) { | ||||||
|             MotionEvent.ACTION_DOWN -> { |             MotionEvent.ACTION_DOWN -> { | ||||||
|                 // validates if the touch is inside of the player area |                 // validates if the touch is inside of the player area | ||||||
|  | @ -717,7 +745,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             MotionEvent.ACTION_UP -> { |             MotionEvent.ACTION_UP -> { | ||||||
|                 if (isCurrentTouchValid && !isLocked) { |                 if (isCurrentTouchValid && !isLocked && isFullScreenPlayer) { | ||||||
|                     // seek time |                     // seek time | ||||||
|                     if (swipeHorizontalEnabled && currentTouchAction == TouchAction.Time) { |                     if (swipeHorizontalEnabled && currentTouchAction == TouchAction.Time) { | ||||||
|                         val startTime = currentTouchStartPlayerTime |                         val startTime = currentTouchStartPlayerTime | ||||||
|  | @ -749,18 +777,18 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|                             if (doubleTapPauseEnabled) { // you can pause if your tap is in the middle of the screen |                             if (doubleTapPauseEnabled) { // you can pause if your tap is in the middle of the screen | ||||||
|                                 when { |                                 when { | ||||||
|                                     currentTouch.x < screenWidth / 2 - (DOUBLE_TAB_PAUSE_PERCENTAGE * screenWidth) -> { |                                     currentTouch.x < screenWidth / 2 - (DOUBLE_TAB_PAUSE_PERCENTAGE * screenWidth) -> { | ||||||
|                                         if (doubleTapEnabled) |                                         if (doubleTapEnabled && isFullScreenPlayer) | ||||||
|                                             rewind() |                                             rewind() | ||||||
|                                     } |                                     } | ||||||
|                                     currentTouch.x > screenWidth / 2 + (DOUBLE_TAB_PAUSE_PERCENTAGE * screenWidth) -> { |                                     currentTouch.x > screenWidth / 2 + (DOUBLE_TAB_PAUSE_PERCENTAGE * screenWidth) -> { | ||||||
|                                         if (doubleTapEnabled) |                                         if (doubleTapEnabled && isFullScreenPlayer) | ||||||
|                                             fastForward() |                                             fastForward() | ||||||
|                                     } |                                     } | ||||||
|                                     else -> { |                                     else -> { | ||||||
|                                         player.handleEvent(CSPlayerEvent.PlayPauseToggle) |                                         player.handleEvent(CSPlayerEvent.PlayPauseToggle) | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
|                             } else if (doubleTapEnabled) { |                             } else if (doubleTapEnabled && isFullScreenPlayer) { | ||||||
|                                 if (currentTouch.x < screenWidth / 2) { |                                 if (currentTouch.x < screenWidth / 2) { | ||||||
|                                     rewind() |                                     rewind() | ||||||
|                                 } else { |                                 } else { | ||||||
|  | @ -798,7 +826,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|             } |             } | ||||||
|             MotionEvent.ACTION_MOVE -> { |             MotionEvent.ACTION_MOVE -> { | ||||||
|                 // if current touch is valid |                 // if current touch is valid | ||||||
|                 if (startTouch != null && isCurrentTouchValid && !isLocked) { |                 if (startTouch != null && isCurrentTouchValid && !isLocked && isFullScreenPlayer) { | ||||||
|                     // action is unassigned and can therefore be assigned |                     // action is unassigned and can therefore be assigned | ||||||
|                     if (currentTouchAction == null) { |                     if (currentTouchAction == null) { | ||||||
|                         val diffFromStart = startTouch - currentTouch |                         val diffFromStart = startTouch - currentTouch | ||||||
|  | @ -1201,6 +1229,11 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | ||||||
|             showMirrorsDialogue() |             showMirrorsDialogue() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         player_intro_play?.setOnClickListener { | ||||||
|  |             player_intro_play?.isGone = true | ||||||
|  |             player.handleEvent(CSPlayerEvent.Play) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         // it is !not! a bug that you cant touch the right side, it does not register inputs on navbar or status bar |         // it is !not! a bug that you cant touch the right side, it does not register inputs on navbar or status bar | ||||||
|         player_holder?.setOnTouchListener { callView, event -> |         player_holder?.setOnTouchListener { callView, event -> | ||||||
|             return@setOnTouchListener handleMotionEvent(callView, event) |             return@setOnTouchListener handleMotionEvent(callView, event) | ||||||
|  |  | ||||||
|  | @ -98,6 +98,7 @@ interface IPlayer { | ||||||
|         startPosition: Long? = null, |         startPosition: Long? = null, | ||||||
|         subtitles : Set<SubtitleData>, |         subtitles : Set<SubtitleData>, | ||||||
|         subtitle : SubtitleData?, |         subtitle : SubtitleData?, | ||||||
|  |         autoPlay : Boolean? = true | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     fun reloadPlayer(context: Context) |     fun reloadPlayer(context: Context) | ||||||
|  |  | ||||||
|  | @ -28,7 +28,6 @@ import androidx.core.view.isGone | ||||||
| import androidx.core.view.isVisible | import androidx.core.view.isVisible | ||||||
| import androidx.core.widget.NestedScrollView | import androidx.core.widget.NestedScrollView | ||||||
| import androidx.core.widget.doOnTextChanged | import androidx.core.widget.doOnTextChanged | ||||||
| import androidx.fragment.app.Fragment |  | ||||||
| import androidx.lifecycle.ViewModelProvider | import androidx.lifecycle.ViewModelProvider | ||||||
| import androidx.preference.PreferenceManager | import androidx.preference.PreferenceManager | ||||||
| import androidx.recyclerview.widget.GridLayoutManager | import androidx.recyclerview.widget.GridLayoutManager | ||||||
|  | @ -85,7 +84,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons | ||||||
| import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes | import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes | ||||||
| import com.lagradost.cloudstream3.utils.UIHelper.requestRW | import com.lagradost.cloudstream3.utils.UIHelper.requestRW | ||||||
| import com.lagradost.cloudstream3.utils.UIHelper.setImage | import com.lagradost.cloudstream3.utils.UIHelper.setImage | ||||||
| import com.lagradost.cloudstream3.utils.UIHelper.setImageBlur |  | ||||||
| import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFileName | import com.lagradost.cloudstream3.utils.VideoDownloadManager.getFileName | ||||||
| import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename | import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename | ||||||
| import kotlinx.android.synthetic.main.fragment_result.* | import kotlinx.android.synthetic.main.fragment_result.* | ||||||
|  | @ -183,7 +181,7 @@ fun ResultEpisode.getWatchProgress(): Float { | ||||||
|     return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat() |     return (getDisplayPosition() / 1000).toFloat() / (duration / 1000).toFloat() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegionsListener { | class ResultFragment : ResultTrailerPlayer() { | ||||||
|     companion object { |     companion object { | ||||||
|         const val URL_BUNDLE = "url" |         const val URL_BUNDLE = "url" | ||||||
|         const val API_NAME_BUNDLE = "apiName" |         const val API_NAME_BUNDLE = "apiName" | ||||||
|  | @ -601,6 +599,53 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|         setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f)) |         setFormatText(result_meta_rating, R.string.rating_format, rating?.div(1000f)) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     var currentTrailers: List<String> = emptyList() | ||||||
|  |     var currentTrailerIndex = 0 | ||||||
|  | 
 | ||||||
|  |     override fun nextMirror() { | ||||||
|  |         currentTrailerIndex++ | ||||||
|  |         loadTrailer() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun playerError(exception: Exception) { | ||||||
|  |         if (player.getIsPlaying()) // because we dont want random toasts in player | ||||||
|  |             super.playerError(exception) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun loadTrailer(index: Int? = null) { | ||||||
|  |         currentTrailers.getOrNull(index ?: currentTrailerIndex)?.let { trailer -> | ||||||
|  |             //if(trailer.contains("youtube.com")) { // wont load in exo | ||||||
|  |             //    nextMirror() | ||||||
|  |             //    return | ||||||
|  |             //} | ||||||
|  |             context?.let { ctx -> | ||||||
|  |                 player.onPause() | ||||||
|  |                 player.loadPlayer( | ||||||
|  |                     ctx, | ||||||
|  |                     false, | ||||||
|  |                     ExtractorLink( | ||||||
|  |                         "", | ||||||
|  |                         "Trailer", | ||||||
|  |                         trailer, | ||||||
|  |                         "", | ||||||
|  |                         Qualities.Unknown.value | ||||||
|  |                     ), | ||||||
|  |                     null, | ||||||
|  |                     startPosition = 0L, | ||||||
|  |                     subtitles = emptySet(), | ||||||
|  |                     subtitle = null, | ||||||
|  |                     autoPlay = false | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun setTrailers(trailers: List<String>?) { | ||||||
|  |         if(context?.isTvSettings() == true) return | ||||||
|  |         currentTrailers = trailers ?: emptyList() | ||||||
|  |         loadTrailer() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun setActors(actors: List<ActorData>?) { |     private fun setActors(actors: List<ActorData>?) { | ||||||
|         if (actors.isNullOrEmpty()) { |         if (actors.isNullOrEmpty()) { | ||||||
|             result_cast_text?.isVisible = false |             result_cast_text?.isVisible = false | ||||||
|  | @ -777,7 +822,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|             } else if (dy < -5) { |             } else if (dy < -5) { | ||||||
|                 result_bookmark_fab?.extend() |                 result_bookmark_fab?.extend() | ||||||
|             } |             } | ||||||
|             result_poster_blur_holder?.translationY = -scrollY.toFloat() |             //result_poster_blur_holder?.translationY = -scrollY.toFloat() | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|         result_back.setOnClickListener { |         result_back.setOnClickListener { | ||||||
|  | @ -1727,6 +1772,8 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|                     setRecommendations(d.recommendations, null) |                     setRecommendations(d.recommendations, null) | ||||||
|                     setActors(d.actors) |                     setActors(d.actors) | ||||||
| 
 | 
 | ||||||
|  |                     setTrailers(d.trailers) | ||||||
|  | 
 | ||||||
|                     if (syncModel.addSyncs(d.syncData)) { |                     if (syncModel.addSyncs(d.syncData)) { | ||||||
|                         syncModel.updateMetaAndUser() |                         syncModel.updateMetaAndUser() | ||||||
|                         syncModel.updateSynced() |                         syncModel.updateSynced() | ||||||
|  | @ -1739,7 +1786,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
|                     val posterImageLink = d.posterUrl |                     val posterImageLink = d.posterUrl | ||||||
|                     if (!posterImageLink.isNullOrEmpty()) { |                     if (!posterImageLink.isNullOrEmpty()) { | ||||||
|                         result_poster?.setImage(posterImageLink, d.posterHeaders) |                         result_poster?.setImage(posterImageLink, d.posterHeaders) | ||||||
|                         result_poster_blur?.setImageBlur(posterImageLink, 10, 3, d.posterHeaders) |                         //result_poster_blur?.setImageBlur(posterImageLink, 10, 3, d.posterHeaders) | ||||||
|                         //Full screen view of Poster image |                         //Full screen view of Poster image | ||||||
|                         if (context?.isTrueTvSettings() == false) // Poster not clickable on tv |                         if (context?.isTrueTvSettings() == false) // Poster not clickable on tv | ||||||
|                             result_poster_holder?.setOnClickListener { |                             result_poster_holder?.setOnClickListener { | ||||||
|  | @ -1768,7 +1815,7 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio | ||||||
| 
 | 
 | ||||||
|                     } else { |                     } else { | ||||||
|                         result_poster?.setImageResource(R.drawable.default_cover) |                         result_poster?.setImageResource(R.drawable.default_cover) | ||||||
|                         result_poster_blur?.setImageResource(R.drawable.default_cover) |                         //result_poster_blur?.setImageResource(R.drawable.default_cover) | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                     result_poster_holder?.visibility = VISIBLE |                     result_poster_holder?.visibility = VISIBLE | ||||||
|  |  | ||||||
|  | @ -0,0 +1,68 @@ | ||||||
|  | package com.lagradost.cloudstream3.ui.result | ||||||
|  | 
 | ||||||
|  | import android.content.res.Configuration | ||||||
|  | import android.graphics.Rect | ||||||
|  | import android.widget.LinearLayout | ||||||
|  | import androidx.core.view.isVisible | ||||||
|  | import com.discord.panels.PanelsChildGestureRegionObserver | ||||||
|  | import com.lagradost.cloudstream3.ui.player.SubtitleData | ||||||
|  | import kotlinx.android.synthetic.main.fragment_trailer.* | ||||||
|  | 
 | ||||||
|  | open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreenPlayer(), | ||||||
|  |     PanelsChildGestureRegionObserver.GestureRegionsListener { | ||||||
|  | 
 | ||||||
|  |     override var lockRotation = false | ||||||
|  |     override var isFullScreenPlayer = false | ||||||
|  |     override var hasPipModeSupport = false | ||||||
|  | 
 | ||||||
|  |     companion object { | ||||||
|  |         const val TAG = "RESULT_TRAILER" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var playerWidthHeight: Pair<Int, Int>? = null | ||||||
|  | 
 | ||||||
|  |     override fun nextEpisode() {} | ||||||
|  | 
 | ||||||
|  |     override fun prevEpisode() {} | ||||||
|  | 
 | ||||||
|  |     override fun playerPositionChanged(posDur: Pair<Long, Long>) {} | ||||||
|  | 
 | ||||||
|  |     override fun nextMirror() {} | ||||||
|  | 
 | ||||||
|  |     override fun onConfigurationChanged(newConfig: Configuration) { | ||||||
|  |         super.onConfigurationChanged(newConfig) | ||||||
|  |         uiReset() | ||||||
|  |         fixPlayerSize() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun fixPlayerSize() { | ||||||
|  |         playerWidthHeight?.let { (w, h) -> | ||||||
|  |             val orientation = this.resources.configuration?.orientation ?: return | ||||||
|  | 
 | ||||||
|  |             val sw = if (orientation == Configuration.ORIENTATION_LANDSCAPE) { | ||||||
|  |                 screenWidth | ||||||
|  |             } else { | ||||||
|  |                 screenHeight | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             player_background?.apply { | ||||||
|  |                 isVisible = true | ||||||
|  |                 layoutParams = | ||||||
|  |                     LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, sw * h / w) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun playerDimensionsLoaded(widthHeight: Pair<Int, Int>) { | ||||||
|  |         playerWidthHeight = widthHeight | ||||||
|  |         fixPlayerSize() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun subtitlesChanged() {} | ||||||
|  | 
 | ||||||
|  |     override fun embeddedSubtitlesFetched(subtitles: List<SubtitleData>) {} | ||||||
|  | 
 | ||||||
|  |     override fun exitedPipMode() {} | ||||||
|  | 
 | ||||||
|  |     override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {} | ||||||
|  | } | ||||||
|  | @ -117,38 +117,13 @@ | ||||||
|                 android:textColor="?attr/textColor" /> |                 android:textColor="?attr/textColor" /> | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
| 
 | 
 | ||||||
|     <FrameLayout |     <LinearLayout | ||||||
|             android:id="@+id/result_finish_loading" |             android:id="@+id/result_finish_loading" | ||||||
|             android:layout_width="match_parent" |             android:layout_width="match_parent" | ||||||
|             android:layout_height="match_parent" |             android:layout_height="match_parent" | ||||||
|             android:visibility="gone" |             android:visibility="gone" | ||||||
|             tools:visibility="visible"> |             tools:visibility="visible" | ||||||
| 
 |             android:orientation="vertical"> | ||||||
|         <FrameLayout |  | ||||||
|                 android:id="@+id/result_poster_blur_holder" |  | ||||||
| 
 |  | ||||||
|                 android:layout_width="match_parent" |  | ||||||
|                 android:layout_height="230dp" |  | ||||||
|                 android:visibility="visible"> |  | ||||||
| 
 |  | ||||||
|             <ImageView |  | ||||||
|                     android:id="@+id/result_poster_blur" |  | ||||||
| 
 |  | ||||||
|                     android:layout_width="match_parent" |  | ||||||
|                     android:layout_height="match_parent" |  | ||||||
|                     android:alpha="0" |  | ||||||
|                     android:background="?attr/primaryGrayBackground" |  | ||||||
|                     android:scaleType="centerCrop" |  | ||||||
|                     tools:ignore="ContentDescription" |  | ||||||
|                     tools:src="@drawable/example_poster" /> |  | ||||||
| 
 |  | ||||||
|             <ImageView |  | ||||||
|                     android:layout_width="match_parent" |  | ||||||
|                     android:layout_height="30dp" |  | ||||||
|                     android:layout_gravity="bottom" |  | ||||||
|                     android:src="@drawable/background_shadow" |  | ||||||
|                     tools:ignore="ContentDescription" /> |  | ||||||
|         </FrameLayout> |  | ||||||
| 
 | 
 | ||||||
|         <androidx.core.widget.NestedScrollView |         <androidx.core.widget.NestedScrollView | ||||||
|                 android:id="@+id/result_scroll" |                 android:id="@+id/result_scroll" | ||||||
|  | @ -163,6 +138,8 @@ | ||||||
|                     android:background="?attr/primaryBlackBackground" |                     android:background="?attr/primaryBlackBackground" | ||||||
|                     android:orientation="vertical"> |                     android:orientation="vertical"> | ||||||
| 
 | 
 | ||||||
|  |                 <include layout="@layout/fragment_trailer" /> | ||||||
|  | 
 | ||||||
|                 <!-- |                 <!-- | ||||||
|                 <FrameLayout |                 <FrameLayout | ||||||
|                         android:background="?attr/primaryGrayBackground" |                         android:background="?attr/primaryGrayBackground" | ||||||
|  | @ -399,6 +376,7 @@ | ||||||
|                             tools:text="Bookmark" |                             tools:text="Bookmark" | ||||||
|                             tools:visibility="visible" /> |                             tools:visibility="visible" /> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|                     <TextView |                     <TextView | ||||||
|                             android:id="@+id/result_cast_text" |                             android:id="@+id/result_cast_text" | ||||||
|                             android:layout_width="wrap_content" |                             android:layout_width="wrap_content" | ||||||
|  | @ -855,7 +833,7 @@ | ||||||
|             </LinearLayout> |             </LinearLayout> | ||||||
|         </androidx.core.widget.NestedScrollView> |         </androidx.core.widget.NestedScrollView> | ||||||
| 
 | 
 | ||||||
|     </FrameLayout> |     </LinearLayout> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     <!-- |     <!-- | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								app/src/main/res/layout/fragment_trailer.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								app/src/main/res/layout/fragment_trailer.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <androidx.constraintlayout.widget.ConstraintLayout 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="0dp" | ||||||
|  |         android:visibility="visible" | ||||||
|  |         android:orientation="horizontal" | ||||||
|  |         android:keepScreenOn="true" | ||||||
|  |         android:id="@+id/player_background" | ||||||
|  |         app:backgroundTint="@android:color/black" | ||||||
|  |         android:background="@android:color/black" | ||||||
|  |         android:screenOrientation="sensorLandscape" | ||||||
|  |         app:surface_type="texture_view"> | ||||||
|  |     <!-- | ||||||
|  |           app:fastforward_increment="10000" | ||||||
|  |             app:rewind_increment="10000"--> | ||||||
|  |     <com.google.android.exoplayer2.ui.PlayerView | ||||||
|  |             android:id="@+id/player_view" | ||||||
|  |             app:show_timeout="0" | ||||||
|  |             app:hide_on_touch="false" | ||||||
|  |             app:auto_show="true" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent" | ||||||
|  |             app:backgroundTint="@android:color/black" | ||||||
|  |             android:background="@android:color/black" | ||||||
|  |             app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |             app:layout_constraintEnd_toEndOf="parent" | ||||||
|  |             app:layout_constraintStart_toStartOf="parent" | ||||||
|  |             app:layout_constraintTop_toTopOf="parent" | ||||||
|  |             app:controller_layout_id="@layout/trailer_custom_layout" /> | ||||||
|  | 
 | ||||||
|  | </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
							
								
								
									
										330
									
								
								app/src/main/res/layout/trailer_custom_layout.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										330
									
								
								app/src/main/res/layout/trailer_custom_layout.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,330 @@ | ||||||
|  | <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:id="@+id/player_holder" | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:orientation="vertical" | ||||||
|  |         android:screenOrientation="landscape" | ||||||
|  |         tools:orientation="vertical"> | ||||||
|  | 
 | ||||||
|  |     <FrameLayout | ||||||
|  |             android:id="@+id/player_intro_play" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent"> | ||||||
|  | 
 | ||||||
|  |         <View | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="match_parent" | ||||||
|  |                 android:background="@drawable/player_gradient_tv" /> | ||||||
|  | 
 | ||||||
|  |         <TextView | ||||||
|  |                 android:textSize="20sp" | ||||||
|  |                 android:textStyle="bold" | ||||||
|  |                 android:textColor="@android:color/white" | ||||||
|  |                 android:text="@string/trailer" | ||||||
|  |                 android:padding="10dp" | ||||||
|  |                 android:layout_gravity="start|bottom" | ||||||
|  |                 android:layout_width="wrap_content" | ||||||
|  |                 android:layout_height="wrap_content" /> | ||||||
|  | 
 | ||||||
|  |         <ImageView | ||||||
|  |                 android:layout_gravity="center" | ||||||
|  |                 android:src="@drawable/play_button" | ||||||
|  |                 android:layout_width="60dp" | ||||||
|  |                 android:layout_height="60dp" /> | ||||||
|  |     </FrameLayout> | ||||||
|  | 
 | ||||||
|  |     <FrameLayout | ||||||
|  |             android:id="@+id/subtitle_holder" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent"> | ||||||
|  | 
 | ||||||
|  |         <View | ||||||
|  |                 android:id="@+id/shadow_overlay" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="match_parent" | ||||||
|  |                 android:background="@color/black_overlay" /> | ||||||
|  |     </FrameLayout> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     <androidx.constraintlayout.widget.ConstraintLayout | ||||||
|  |             android:id="@+id/player_video_holder" | ||||||
|  |             android:layout_width="match_parent" | ||||||
|  |             android:layout_height="match_parent"> | ||||||
|  | 
 | ||||||
|  |         <!--use for thinner app:trackThickness="3dp" com.google.android.material.progressindicator.CircularProgressIndicator--> | ||||||
|  |         <ProgressBar | ||||||
|  |                 app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                 app:layout_constraintLeft_toLeftOf="parent" | ||||||
|  |                 app:layout_constraintRight_toRightOf="parent" | ||||||
|  |                 app:layout_constraintTop_toTopOf="parent" | ||||||
|  | 
 | ||||||
|  |                 android:focusable="false" | ||||||
|  |                 android:clickable="false" | ||||||
|  |                 android:focusableInTouchMode="false" | ||||||
|  | 
 | ||||||
|  |                 android:indeterminate="true" | ||||||
|  |                 android:visibility="gone" | ||||||
|  |                 tools:visibility="visible" | ||||||
|  |                 android:id="@+id/player_buffering" | ||||||
|  |                 android:layout_gravity="center" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_width="wrap_content" /> | ||||||
|  | 
 | ||||||
|  |         <!-- This nested layout is necessary because of buffering and clicking--> | ||||||
|  |         <FrameLayout | ||||||
|  |                 app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                 app:layout_constraintLeft_toLeftOf="parent" | ||||||
|  |                 app:layout_constraintRight_toRightOf="parent" | ||||||
|  |                 app:layout_constraintTop_toTopOf="parent" | ||||||
|  |                 android:layout_width="100dp" | ||||||
|  |                 android:layout_height="100dp" | ||||||
|  |                 android:id="@+id/player_pause_play_holder_holder"> | ||||||
|  | 
 | ||||||
|  |             <FrameLayout | ||||||
|  |                     tools:ignore="uselessParent" | ||||||
|  |                     android:id="@+id/player_pause_play_holder" | ||||||
|  |                     android:layout_width="match_parent" | ||||||
|  |                     android:layout_height="match_parent"> | ||||||
|  | 
 | ||||||
|  |                 <ImageView | ||||||
|  |                         app:tint="@color/white" | ||||||
|  |                         android:id="@+id/player_pause_play" | ||||||
|  |                         android:nextFocusLeft="@id/exo_rew" | ||||||
|  |                         android:nextFocusRight="@id/exo_ffwd" | ||||||
|  |                         android:nextFocusUp="@id/player_go_back" | ||||||
|  |                         android:nextFocusDown="@id/player_lock" | ||||||
|  | 
 | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  | 
 | ||||||
|  |                         android:src="@drawable/netflix_pause" | ||||||
|  |                         android:background="@drawable/video_tap_button" | ||||||
|  | 
 | ||||||
|  |                         android:layout_width="70dp" | ||||||
|  |                         android:layout_height="70dp" | ||||||
|  |                         tools:ignore="ContentDescription" /> | ||||||
|  |             </FrameLayout> | ||||||
|  |         </FrameLayout> | ||||||
|  | 
 | ||||||
|  |         <androidx.constraintlayout.widget.ConstraintLayout | ||||||
|  |                 android:id="@+id/player_center_menu" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="100dp" | ||||||
|  |                 android:layout_gravity="center" | ||||||
|  |                 android:gravity="center" | ||||||
|  |                 android:orientation="horizontal" | ||||||
|  |                 app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                 app:layout_constraintLeft_toLeftOf="parent" | ||||||
|  |                 app:layout_constraintRight_toRightOf="parent" | ||||||
|  |                 app:layout_constraintTop_toTopOf="parent"> | ||||||
|  | 
 | ||||||
|  |             <FrameLayout | ||||||
|  |                     android:id="@+id/player_rew_holder" | ||||||
|  |                     android:layout_width="0dp" | ||||||
|  |                     android:layout_height="wrap_content" | ||||||
|  |                     app:layout_constraintWidth_percent="0.5" | ||||||
|  |                     android:layout_gravity="center_vertical|start" | ||||||
|  |                     app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                     app:layout_constraintLeft_toLeftOf="parent" | ||||||
|  |                     app:layout_constraintRight_toLeftOf="@id/player_ffwd_holder" | ||||||
|  |                     app:layout_constraintTop_toTopOf="parent"> | ||||||
|  | 
 | ||||||
|  |                 <TextView | ||||||
|  |                         android:id="@+id/exo_rew_text" | ||||||
|  |                         android:layout_width="200dp" | ||||||
|  |                         android:layout_height="40dp" | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  | 
 | ||||||
|  |                         android:gravity="center" | ||||||
|  |                         android:textColor="@color/white" | ||||||
|  |                         android:textSize="19sp" | ||||||
|  | 
 | ||||||
|  |                         android:textStyle="bold" | ||||||
|  |                         tools:text="10" /> | ||||||
|  | 
 | ||||||
|  |                 <ImageButton | ||||||
|  |                         android:id="@id/exo_rew" | ||||||
|  |                         android:layout_width="70dp" | ||||||
|  |                         android:layout_height="70dp" | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  | 
 | ||||||
|  |                         android:background="@drawable/video_tap_button_skip" | ||||||
|  |                         android:nextFocusLeft="@id/exo_rew" | ||||||
|  |                         android:nextFocusUp="@id/player_go_back" | ||||||
|  |                         android:nextFocusDown="@id/player_lock" | ||||||
|  |                         android:padding="10dp" | ||||||
|  |                         android:scaleType="fitCenter" | ||||||
|  |                         android:scaleX="-1" | ||||||
|  |                         android:src="@drawable/netflix_skip_forward" | ||||||
|  |                         app:tint="@color/white" | ||||||
|  |                         android:tintMode="src_in" | ||||||
|  |                         tools:ignore="ContentDescription" /> | ||||||
|  |             </FrameLayout> | ||||||
|  | 
 | ||||||
|  |             <FrameLayout | ||||||
|  |                     android:id="@+id/player_ffwd_holder" | ||||||
|  |                     android:layout_width="0dp" | ||||||
|  |                     app:layout_constraintWidth_percent="0.5" | ||||||
|  |                     android:layout_height="wrap_content" | ||||||
|  |                     android:layout_gravity="center_vertical|end" | ||||||
|  |                     app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                     app:layout_constraintLeft_toRightOf="@id/player_rew_holder" | ||||||
|  |                     app:layout_constraintRight_toRightOf="parent" | ||||||
|  |                     app:layout_constraintTop_toTopOf="parent"> | ||||||
|  | 
 | ||||||
|  |                 <TextView | ||||||
|  |                         android:id="@+id/exo_ffwd_text" | ||||||
|  |                         android:layout_width="200dp" | ||||||
|  |                         android:layout_height="40dp" | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  |                         android:gravity="center" | ||||||
|  |                         android:textColor="@color/white" | ||||||
|  |                         android:textSize="19sp" | ||||||
|  |                         android:textStyle="bold" | ||||||
|  |                         tools:text="10" /> | ||||||
|  | 
 | ||||||
|  |                 <ImageButton | ||||||
|  |                         android:id="@id/exo_ffwd" | ||||||
|  |                         android:layout_width="70dp" | ||||||
|  |                         android:layout_height="70dp" | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  | 
 | ||||||
|  |                         android:background="@drawable/video_tap_button_skip" | ||||||
|  |                         android:nextFocusRight="@id/exo_rew" | ||||||
|  |                         android:nextFocusUp="@id/player_go_back" | ||||||
|  |                         android:nextFocusDown="@id/player_lock" | ||||||
|  |                         android:padding="10dp" | ||||||
|  |                         android:scaleType="fitCenter" | ||||||
|  |                         android:src="@drawable/netflix_skip_forward" | ||||||
|  |                         app:tint="@color/white" | ||||||
|  |                         android:tintMode="src_in" | ||||||
|  |                         tools:ignore="ContentDescription" /> | ||||||
|  |             </FrameLayout> | ||||||
|  |         </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|  | 
 | ||||||
|  |         <LinearLayout | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_gravity="bottom" | ||||||
|  |                 android:layout_marginBottom="20dp" | ||||||
|  |                 android:gravity="center" | ||||||
|  |                 android:orientation="horizontal" | ||||||
|  |                 android:paddingTop="4dp" | ||||||
|  |                 android:visibility="gone" | ||||||
|  |                 app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                 app:layout_constraintEnd_toEndOf="parent"> | ||||||
|  | 
 | ||||||
|  |             <ImageButton | ||||||
|  |                     android:id="@id/exo_prev" | ||||||
|  |                     style="@style/ExoMediaButton.Previous" | ||||||
|  |                     app:tint="?attr/colorPrimaryDark" | ||||||
|  |                     android:tintMode="src_in" | ||||||
|  |                     tools:ignore="ContentDescription" /> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             <ImageButton | ||||||
|  |                     android:id="@id/exo_repeat_toggle" | ||||||
|  |                     style="@style/ExoMediaButton" | ||||||
|  |                     app:tint="?attr/colorPrimaryDark" | ||||||
|  |                     android:tintMode="src_in" | ||||||
|  |                     tools:ignore="ContentDescription" /> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             <ImageButton | ||||||
|  |                     android:id="@id/exo_next" | ||||||
|  |                     style="@style/ExoMediaButton.Next" | ||||||
|  |                     app:tint="?attr/colorPrimaryDark" | ||||||
|  |                     android:tintMode="src_in" | ||||||
|  |                     tools:ignore="ContentDescription" /> | ||||||
|  | 
 | ||||||
|  |             <ImageButton | ||||||
|  |                     android:id="@id/exo_vr" | ||||||
|  |                     style="@style/ExoMediaButton.VR" | ||||||
|  |                     app:tint="?attr/colorPrimaryDark" | ||||||
|  |                     android:tintMode="src_in" | ||||||
|  |                     tools:ignore="ContentDescription" /> | ||||||
|  | 
 | ||||||
|  |             <ImageButton | ||||||
|  |                     android:id="@id/exo_play" | ||||||
|  |                     app:tint="?attr/colorPrimaryDark" | ||||||
|  |                     android:tintMode="src_in" | ||||||
|  |                     tools:ignore="ContentDescription" | ||||||
|  |                     android:layout_height="0dp" | ||||||
|  |                     android:layout_width="0dp" /> | ||||||
|  | 
 | ||||||
|  |             <ImageButton | ||||||
|  |                     android:id="@id/exo_pause" | ||||||
|  |                     app:tint="?attr/colorPrimaryDark" | ||||||
|  |                     android:tintMode="src_in" | ||||||
|  |                     tools:ignore="ContentDescription" | ||||||
|  |                     android:layout_height="0dp" | ||||||
|  |                     android:layout_width="0dp" /> | ||||||
|  |         </LinearLayout> | ||||||
|  | 
 | ||||||
|  |         <LinearLayout | ||||||
|  |                 android:id="@+id/bottom_player_bar" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_marginBottom="10dp" | ||||||
|  |                 android:gravity="center_vertical" | ||||||
|  |                 android:orientation="vertical" | ||||||
|  |                 app:layout_constraintBottom_toBottomOf="parent" | ||||||
|  |                 app:layout_constraintEnd_toEndOf="parent"> | ||||||
|  | 
 | ||||||
|  |             <LinearLayout | ||||||
|  |                     android:id="@+id/player_video_bar" | ||||||
|  |                     android:layout_width="match_parent" | ||||||
|  |                     android:layout_height="wrap_content" | ||||||
|  |                     android:orientation="horizontal"> | ||||||
|  | 
 | ||||||
|  |                 <TextView | ||||||
|  |                         android:id="@id/exo_position" | ||||||
|  |                         android:layout_width="wrap_content" | ||||||
|  |                         android:layout_height="wrap_content" | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  |                         android:layout_marginStart="20dp" | ||||||
|  |                         android:gravity="end" | ||||||
|  |                         android:includeFontPadding="false" | ||||||
|  |                         android:minWidth="50dp" | ||||||
|  |                         android:paddingLeft="4dp" | ||||||
|  |                         android:paddingRight="4dp" | ||||||
|  |                         android:textColor="@android:color/white" | ||||||
|  |                         android:textSize="14sp" | ||||||
|  |                         android:textStyle="normal" | ||||||
|  |                         tools:text="15:30" /> | ||||||
|  |                 <!--app:buffered_color="@color/videoCache"--> | ||||||
|  |                 <com.google.android.exoplayer2.ui.DefaultTimeBar | ||||||
|  |                         android:id="@id/exo_progress" | ||||||
|  |                         android:layout_width="0dp" | ||||||
|  |                         android:layout_height="30dp" | ||||||
|  |                         android:layout_weight="1" | ||||||
|  |                         app:bar_height="2dp" | ||||||
|  |                         app:played_color="?attr/colorPrimary" | ||||||
|  | 
 | ||||||
|  |                         app:scrubber_color="?attr/colorPrimary" | ||||||
|  |                         app:scrubber_dragged_size="26dp" | ||||||
|  |                         app:scrubber_enabled_size="24dp" | ||||||
|  |                         app:unplayed_color="@color/videoProgress" /> | ||||||
|  | 
 | ||||||
|  |                 <!-- exo_duration--> | ||||||
|  |                 <TextView | ||||||
|  |                         android:id="@id/exo_duration" | ||||||
|  |                         android:layout_width="wrap_content" | ||||||
|  |                         android:layout_height="wrap_content" | ||||||
|  |                         android:layout_gravity="center" | ||||||
|  |                         android:layout_marginEnd="20dp" | ||||||
|  |                         android:includeFontPadding="false" | ||||||
|  |                         android:minWidth="50dp" | ||||||
|  |                         android:paddingLeft="4dp" | ||||||
|  |                         android:paddingRight="4dp" | ||||||
|  |                         android:textColor="@android:color/white" | ||||||
|  |                         android:textSize="14sp" | ||||||
|  |                         android:textStyle="normal" | ||||||
|  |                         tools:text="23:20" /> | ||||||
|  | 
 | ||||||
|  |             </LinearLayout> | ||||||
|  | 
 | ||||||
|  |         </LinearLayout> | ||||||
|  |     </androidx.constraintlayout.widget.ConstraintLayout> | ||||||
|  | </FrameLayout> | ||||||
|  | @ -530,4 +530,5 @@ | ||||||
|     <string name="subtitles_remove_captions">Remove closed captions from subtitles</string> |     <string name="subtitles_remove_captions">Remove closed captions from subtitles</string> | ||||||
|     <string name="subtitles_remove_bloat">Remove bloat from subtitles</string> |     <string name="subtitles_remove_bloat">Remove bloat from subtitles</string> | ||||||
|     <string name="extras">Extras</string> |     <string name="extras">Extras</string> | ||||||
|  |     <string name="trailer">Trailer</string> | ||||||
| </resources> | </resources> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue