forked from recloudstream/cloudstream
		
	crash fix and headphones pause
This commit is contained in:
		
							parent
							
								
									ca4d05bd34
								
							
						
					
					
						commit
						0f1229354a
					
				
					 9 changed files with 246 additions and 149 deletions
				
			
		
							
								
								
									
										1
									
								
								.idea/gradle.xml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								.idea/gradle.xml
									
										
									
										generated
									
									
									
								
							|  | @ -8,7 +8,6 @@ | ||||||
|         <option name="testRunner" value="GRADLE" /> |         <option name="testRunner" value="GRADLE" /> | ||||||
|         <option name="distributionType" value="DEFAULT_WRAPPED" /> |         <option name="distributionType" value="DEFAULT_WRAPPED" /> | ||||||
|         <option name="externalProjectPath" value="$PROJECT_DIR$" /> |         <option name="externalProjectPath" value="$PROJECT_DIR$" /> | ||||||
|         <option name="gradleJvm" value="jbr-11" /> |  | ||||||
|         <option name="modules"> |         <option name="modules"> | ||||||
|           <set> |           <set> | ||||||
|             <option value="$PROJECT_DIR$" /> |             <option value="$PROJECT_DIR$" /> | ||||||
|  |  | ||||||
|  | @ -35,8 +35,8 @@ android { | ||||||
|         minSdkVersion 21 |         minSdkVersion 21 | ||||||
|         targetSdkVersion 30 |         targetSdkVersion 30 | ||||||
| 
 | 
 | ||||||
|         versionCode 40 |         versionCode 41 | ||||||
|         versionName "2.4.8" |         versionName "2.5.8" | ||||||
| 
 | 
 | ||||||
|         resValue "string", "app_version", |         resValue "string", "app_version", | ||||||
|                 "${defaultConfig.versionName}${versionNameSuffix ?: ""}" |                 "${defaultConfig.versionName}${versionNameSuffix ?: ""}" | ||||||
|  |  | ||||||
|  | @ -39,7 +39,6 @@ object APIHolder { | ||||||
|         WcoProvider(), |         WcoProvider(), | ||||||
|         // MeloMovieProvider(), // Captcha for links |         // MeloMovieProvider(), // Captcha for links | ||||||
|         DubbedAnimeProvider(), |         DubbedAnimeProvider(), | ||||||
|         HDMProvider(), |  | ||||||
|         IHaveNoTvProvider(), // Documentaries provider |         IHaveNoTvProvider(), // Documentaries provider | ||||||
|         //LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...) |         //LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...) | ||||||
|         VMoveeProvider(), |         VMoveeProvider(), | ||||||
|  | @ -58,8 +57,6 @@ object APIHolder { | ||||||
| 
 | 
 | ||||||
|         //TmdbProvider(), |         //TmdbProvider(), | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         FilmanProvider(), |         FilmanProvider(), | ||||||
| 
 | 
 | ||||||
|         ZoroProvider(), |         ZoroProvider(), | ||||||
|  | @ -81,6 +78,7 @@ object APIHolder { | ||||||
| 
 | 
 | ||||||
|     private val backwardsCompatibleProviders = arrayListOf( |     private val backwardsCompatibleProviders = arrayListOf( | ||||||
|         KawaiifuProvider(), // removed due to cloudflare |         KawaiifuProvider(), // removed due to cloudflare | ||||||
|  |         HDMProvider(),// removed due to cloudflare | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     fun getApiFromName(apiName: String?): MainAPI { |     fun getApiFromName(apiName: String?): MainAPI { | ||||||
|  | @ -179,7 +177,8 @@ object APIHolder { | ||||||
| 
 | 
 | ||||||
|     fun Context.filterProviderByPreferredMedia(): List<MainAPI> { |     fun Context.filterProviderByPreferredMedia(): List<MainAPI> { | ||||||
|         val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) |         val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) | ||||||
|         val currentPrefMedia = settingsManager.getInt(this.getString(R.string.prefer_media_type_key), 0) |         val currentPrefMedia = | ||||||
|  |             settingsManager.getInt(this.getString(R.string.prefer_media_type_key), 0) | ||||||
|         val langs = this.getApiProviderLangSettings() |         val langs = this.getApiProviderLangSettings() | ||||||
|         val allApis = apis.filter { langs.contains(it.lang) }.filter { api -> api.hasMainPage } |         val allApis = apis.filter { langs.contains(it.lang) }.filter { api -> api.hasMainPage } | ||||||
|         return if (currentPrefMedia < 1) { |         return if (currentPrefMedia < 1) { | ||||||
|  | @ -275,7 +274,7 @@ fun parseRating(ratingString: String?): Int? { | ||||||
|     return (floatRating * 10).toInt() |     return (floatRating * 10).toInt() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun MainAPI.fixUrlNull(url : String?) : String? { | fun MainAPI.fixUrlNull(url: String?): String? { | ||||||
|     if (url.isNullOrEmpty()) { |     if (url.isNullOrEmpty()) { | ||||||
|         return null |         return null | ||||||
|     } |     } | ||||||
|  | @ -305,7 +304,7 @@ fun sortUrls(urls: Set<ExtractorLink>): List<ExtractorLink> { | ||||||
|     return urls.sortedBy { t -> -t.quality } |     return urls.sortedBy { t -> -t.quality } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun sortSubs(subs : Set<SubtitleData>) : List<SubtitleData> { | fun sortSubs(subs: Set<SubtitleData>): List<SubtitleData> { | ||||||
|     return subs.sortedBy { it.name } |     return subs.sortedBy { it.name } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -473,7 +472,7 @@ fun LoadResponse?.isAnimeBased(): Boolean { | ||||||
|     return (this.type == TvType.Anime || this.type == TvType.ONA) // && (this is AnimeLoadResponse) |     return (this.type == TvType.Anime || this.type == TvType.ONA) // && (this is AnimeLoadResponse) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| fun TvType?.isEpisodeBased() : Boolean { | fun TvType?.isEpisodeBased(): Boolean { | ||||||
|     if (this == null) return false |     if (this == null) return false | ||||||
|     return (this == TvType.TvSeries || this == TvType.Anime) |     return (this == TvType.TvSeries || this == TvType.Anime) | ||||||
| } | } | ||||||
|  | @ -573,7 +572,13 @@ fun MainAPI.newMovieLoadResponse( | ||||||
|     dataUrl: String, |     dataUrl: String, | ||||||
|     initializer: MovieLoadResponse.() -> Unit = { } |     initializer: MovieLoadResponse.() -> Unit = { } | ||||||
| ): MovieLoadResponse { | ): MovieLoadResponse { | ||||||
|     val builder = MovieLoadResponse(name = name, url = url, apiName = this.name, type = type, dataUrl = dataUrl) |     val builder = MovieLoadResponse( | ||||||
|  |         name = name, | ||||||
|  |         url = url, | ||||||
|  |         apiName = this.name, | ||||||
|  |         type = type, | ||||||
|  |         dataUrl = dataUrl | ||||||
|  |     ) | ||||||
|     builder.initializer() |     builder.initializer() | ||||||
|     return builder |     return builder | ||||||
| } | } | ||||||
|  | @ -634,7 +639,13 @@ fun MainAPI.newTvSeriesLoadResponse( | ||||||
|     episodes: List<TvSeriesEpisode>, |     episodes: List<TvSeriesEpisode>, | ||||||
|     initializer: TvSeriesLoadResponse.() -> Unit = { } |     initializer: TvSeriesLoadResponse.() -> Unit = { } | ||||||
| ): TvSeriesLoadResponse { | ): TvSeriesLoadResponse { | ||||||
|     val builder = TvSeriesLoadResponse(name = name, url = url, apiName = this.name, type = type, episodes = episodes) |     val builder = TvSeriesLoadResponse( | ||||||
|  |         name = name, | ||||||
|  |         url = url, | ||||||
|  |         apiName = this.name, | ||||||
|  |         type = type, | ||||||
|  |         episodes = episodes | ||||||
|  |     ) | ||||||
|     builder.initializer() |     builder.initializer() | ||||||
|     return builder |     return builder | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,26 +1,27 @@ | ||||||
| package com.lagradost.cloudstream3.ui.player | package com.lagradost.cloudstream3.ui.player | ||||||
| 
 | 
 | ||||||
| import android.annotation.SuppressLint | import android.annotation.SuppressLint | ||||||
| import android.content.BroadcastReceiver | import android.content.* | ||||||
| import android.content.Context |  | ||||||
| import android.content.Intent |  | ||||||
| import android.content.IntentFilter |  | ||||||
| import android.graphics.drawable.AnimatedImageDrawable | import android.graphics.drawable.AnimatedImageDrawable | ||||||
| import android.graphics.drawable.AnimatedVectorDrawable | import android.graphics.drawable.AnimatedVectorDrawable | ||||||
| import android.media.metrics.PlaybackErrorEvent | import android.media.metrics.PlaybackErrorEvent | ||||||
| import android.os.Build | import android.os.Build | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
|  | import android.support.v4.media.session.MediaSessionCompat | ||||||
| import android.view.LayoutInflater | import android.view.LayoutInflater | ||||||
| import android.view.View | import android.view.View | ||||||
| import android.view.ViewGroup | import android.view.ViewGroup | ||||||
|  | import android.view.WindowManager | ||||||
| import android.widget.Toast | import android.widget.Toast | ||||||
| import androidx.annotation.LayoutRes | import androidx.annotation.LayoutRes | ||||||
| import androidx.annotation.StringRes | import androidx.annotation.StringRes | ||||||
| import androidx.core.view.isVisible | import androidx.core.view.isVisible | ||||||
| import androidx.fragment.app.Fragment | import androidx.fragment.app.Fragment | ||||||
|  | import androidx.media.session.MediaButtonReceiver | ||||||
| import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat | import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat | ||||||
| import com.google.android.exoplayer2.ExoPlayer | import com.google.android.exoplayer2.ExoPlayer | ||||||
| import com.google.android.exoplayer2.PlaybackException | import com.google.android.exoplayer2.PlaybackException | ||||||
|  | import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector | ||||||
| import com.google.android.exoplayer2.ui.AspectRatioFrameLayout | import com.google.android.exoplayer2.ui.AspectRatioFrameLayout | ||||||
| import com.google.android.exoplayer2.ui.SubtitleView | import com.google.android.exoplayer2.ui.SubtitleView | ||||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.getKey | import com.lagradost.cloudstream3.AcraApplication.Companion.getKey | ||||||
|  | @ -81,9 +82,20 @@ abstract class AbstractPlayerFragment( | ||||||
|         throw NotImplementedError() |         throw NotImplementedError() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private fun keepScreenOn(on : Boolean) { | ||||||
|  |         if(on) { | ||||||
|  |             activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) | ||||||
|  |         } else { | ||||||
|  |             activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private fun updateIsPlaying(playing: Pair<CSPlayerLoading, CSPlayerLoading>) { |     private fun updateIsPlaying(playing: Pair<CSPlayerLoading, CSPlayerLoading>) { | ||||||
|         val (wasPlaying, isPlaying) = playing |         val (wasPlaying, isPlaying) = playing | ||||||
|         val isPlayingRightNow = CSPlayerLoading.IsPlaying == isPlaying |         val isPlayingRightNow = CSPlayerLoading.IsPlaying == isPlaying | ||||||
|  |         val isPausedRightNow = CSPlayerLoading.IsPaused == isPlaying | ||||||
|  | 
 | ||||||
|  |         keepScreenOn(!isPausedRightNow) | ||||||
| 
 | 
 | ||||||
|         isBuffering = CSPlayerLoading.IsBuffering == isPlaying |         isBuffering = CSPlayerLoading.IsBuffering == isPlaying | ||||||
|         if (isBuffering) { |         if (isBuffering) { | ||||||
|  | @ -241,11 +253,48 @@ abstract class AbstractPlayerFragment( | ||||||
| 
 | 
 | ||||||
|     private fun playerUpdated(player: Any?) { |     private fun playerUpdated(player: Any?) { | ||||||
|         if (player is ExoPlayer) { |         if (player is ExoPlayer) { | ||||||
|  |             context?.let { ctx -> | ||||||
|  |                 val mediaButtonReceiver = ComponentName(ctx, MediaButtonReceiver::class.java) | ||||||
|  |                 MediaSessionCompat(ctx, "Player", mediaButtonReceiver, null).let { media -> | ||||||
|  |                     //media.setCallback(mMediaSessionCallback) | ||||||
|  |                     //media.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS) | ||||||
|  |                     val mediaSessionConnector = MediaSessionConnector(media) | ||||||
|  |                     mediaSessionConnector.setPlayer(player) | ||||||
|  |                     media.isActive = true | ||||||
|  |                     mMediaSessionCompat = media | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             player_view?.player = player |             player_view?.player = player | ||||||
|             player_view?.performClick() |             player_view?.performClick() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     private var mediaSessionConnector: MediaSessionConnector? = null | ||||||
|  |     private var mMediaSessionCompat: MediaSessionCompat? = null | ||||||
|  | 
 | ||||||
|  |     // this can be used in the future for players other than exoplayer | ||||||
|  |     //private val mMediaSessionCallback: MediaSessionCompat.Callback = object : MediaSessionCompat.Callback() { | ||||||
|  |     //    override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean { | ||||||
|  |     //        val keyEvent = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT) as KeyEvent? | ||||||
|  |     //        if (keyEvent != null) { | ||||||
|  |     //            if (keyEvent.action == KeyEvent.ACTION_DOWN) { // NO DOUBLE SKIP | ||||||
|  |     //                val consumed = when (keyEvent.keyCode) { | ||||||
|  |     //                    KeyEvent.KEYCODE_MEDIA_PAUSE -> callOnPause() | ||||||
|  |     //                    KeyEvent.KEYCODE_MEDIA_PLAY -> callOnPlay() | ||||||
|  |     //                    KeyEvent.KEYCODE_MEDIA_STOP -> callOnStop() | ||||||
|  |     //                    KeyEvent.KEYCODE_MEDIA_NEXT -> callOnNext() | ||||||
|  |     //                    else -> false | ||||||
|  |     //                } | ||||||
|  |     //                if (consumed) return true | ||||||
|  |     //            } | ||||||
|  |     //        } | ||||||
|  |     // | ||||||
|  |     //        return super.onMediaButtonEvent(mediaButtonEvent) | ||||||
|  |     //    } | ||||||
|  |     //} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     @SuppressLint("SetTextI18n") |     @SuppressLint("SetTextI18n") | ||||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||||
|         resizeMode = getKey(RESIZE_MODE_KEY) ?: 0 |         resizeMode = getKey(RESIZE_MODE_KEY) ?: 0 | ||||||
|  | @ -295,6 +344,7 @@ abstract class AbstractPlayerFragment( | ||||||
|         keyEventListener = null |         keyEventListener = null | ||||||
|         SubtitlesFragment.applyStyleEvent -= ::onSubStyleChanged |         SubtitlesFragment.applyStyleEvent -= ::onSubStyleChanged | ||||||
| 
 | 
 | ||||||
|  |         keepScreenOn(false) | ||||||
|         super.onDestroy() |         super.onDestroy() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -130,7 +130,7 @@ open class FullScreenPlayer : AbstractPlayerFragment(R.layout.fragment_player) { | ||||||
|         if (isShowing) { |         if (isShowing) { | ||||||
|             updateUIVisibility() |             updateUIVisibility() | ||||||
|         } else { |         } else { | ||||||
|             player_holder.postDelayed({ updateUIVisibility() }, 200) |             player_holder?.postDelayed({ updateUIVisibility() }, 200) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         val titleMove = if (isShowing) 0f else -50.toPx.toFloat() |         val titleMove = if (isShowing) 0f else -50.toPx.toFloat() | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ import com.hippo.unifile.UniFile | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.CommonActivity.showToast | import com.lagradost.cloudstream3.CommonActivity.showToast | ||||||
| import com.lagradost.cloudstream3.mvvm.Resource | import com.lagradost.cloudstream3.mvvm.Resource | ||||||
|  | import com.lagradost.cloudstream3.mvvm.logError | ||||||
| import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | ||||||
| import com.lagradost.cloudstream3.mvvm.observe | import com.lagradost.cloudstream3.mvvm.observe | ||||||
| import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType | import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType | ||||||
|  | @ -171,111 +172,115 @@ class GeneratorPlayer : FullScreenPlayer() { | ||||||
| 
 | 
 | ||||||
|     var selectSourceDialog: AlertDialog? = null |     var selectSourceDialog: AlertDialog? = null | ||||||
|     override fun showMirrorsDialogue() { |     override fun showMirrorsDialogue() { | ||||||
|         currentSelectedSubtitles = player.getCurrentPreferredSubtitle() |         try { | ||||||
|         context?.let { ctx -> |             currentSelectedSubtitles = player.getCurrentPreferredSubtitle() | ||||||
|             val isPlaying = player.getIsPlaying() |             context?.let { ctx -> | ||||||
|             player.handleEvent(CSPlayerEvent.Pause) |                 val isPlaying = player.getIsPlaying() | ||||||
|             val currentSubtitles = sortSubs(currentSubs) |                 player.handleEvent(CSPlayerEvent.Pause) | ||||||
|  |                 val currentSubtitles = sortSubs(currentSubs) | ||||||
| 
 | 
 | ||||||
|             val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) |                 val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) | ||||||
|                 .setView(R.layout.player_select_source_and_subs) |                     .setView(R.layout.player_select_source_and_subs) | ||||||
| 
 | 
 | ||||||
|             val sourceDialog = sourceBuilder.create() |                 val sourceDialog = sourceBuilder.create() | ||||||
|             selectSourceDialog = sourceDialog |                 selectSourceDialog = sourceDialog | ||||||
|             sourceDialog.show() |                 sourceDialog.show() | ||||||
|             val providerList = |                 val providerList = | ||||||
|                 sourceDialog.findViewById<ListView>(R.id.sort_providers)!! |                     sourceDialog.findViewById<ListView>(R.id.sort_providers)!! | ||||||
|             val subtitleList = |                 val subtitleList = | ||||||
|                 sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!! |                     sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!! | ||||||
|             val applyButton = |                 val applyButton = | ||||||
|                 sourceDialog.findViewById<MaterialButton>(R.id.apply_btt)!! |                     sourceDialog.findViewById<MaterialButton>(R.id.apply_btt)!! | ||||||
|             val cancelButton = |                 val cancelButton = | ||||||
|                 sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!! |                     sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!! | ||||||
| 
 | 
 | ||||||
|             val footer: TextView = |                 val footer: TextView = | ||||||
|                 layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView |                     layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView | ||||||
|             footer.text = ctx.getString(R.string.player_load_subtitles) |                 footer.text = ctx.getString(R.string.player_load_subtitles) | ||||||
|             footer.setOnClickListener { |                 footer.setOnClickListener { | ||||||
|                 openSubPicker() |                     openSubPicker() | ||||||
|             } |  | ||||||
|             subtitleList.addFooterView(footer) |  | ||||||
| 
 |  | ||||||
|             var sourceIndex = 0 |  | ||||||
|             var startSource = 0 |  | ||||||
| 
 |  | ||||||
|             val sortedUrls = sortLinks(useQualitySettings = false) |  | ||||||
|             if (sortedUrls.isNullOrEmpty()) { |  | ||||||
|                 sourceDialog.findViewById<LinearLayout>(R.id.sort_sources_holder)?.isGone = true |  | ||||||
|             } else { |  | ||||||
|                 startSource = sortedUrls.indexOf(currentSelectedLink) |  | ||||||
|                 sourceIndex = startSource |  | ||||||
| 
 |  | ||||||
|                 val sourcesArrayAdapter = |  | ||||||
|                     ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice) |  | ||||||
| 
 |  | ||||||
|                 sourcesArrayAdapter.addAll(sortedUrls.map { |  | ||||||
|                     it.first?.name ?: it.second?.name ?: "NULL" |  | ||||||
|                 }) |  | ||||||
| 
 |  | ||||||
|                 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) |  | ||||||
|                 } |                 } | ||||||
|             } |                 subtitleList.addFooterView(footer) | ||||||
| 
 | 
 | ||||||
|             sourceDialog.setOnDismissListener { |                 var sourceIndex = 0 | ||||||
|                 if (isPlaying) { |                 var startSource = 0 | ||||||
|                     player.handleEvent(CSPlayerEvent.Play) |  | ||||||
|                 } |  | ||||||
|                 activity?.hideSystemUI() |  | ||||||
|                 selectSourceDialog = null |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             val subtitleIndexStart = currentSubtitles.indexOf(currentSelectedSubtitles) + 1 |                 val sortedUrls = sortLinks(useQualitySettings = false) | ||||||
|             var subtitleIndex = subtitleIndexStart |                 if (sortedUrls.isNullOrEmpty()) { | ||||||
|  |                     sourceDialog.findViewById<LinearLayout>(R.id.sort_sources_holder)?.isGone = true | ||||||
|  |                 } else { | ||||||
|  |                     startSource = sortedUrls.indexOf(currentSelectedLink) | ||||||
|  |                     sourceIndex = startSource | ||||||
| 
 | 
 | ||||||
|             val subsArrayAdapter = |                     val sourcesArrayAdapter = | ||||||
|                 ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice) |                         ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice) | ||||||
|             subsArrayAdapter.add(getString(R.string.no_subtitles)) |  | ||||||
|             subsArrayAdapter.addAll(currentSubtitles.map { it.name }) |  | ||||||
| 
 | 
 | ||||||
|             subtitleList.adapter = subsArrayAdapter |                     sourcesArrayAdapter.addAll(sortedUrls.map { | ||||||
|             subtitleList.choiceMode = AbsListView.CHOICE_MODE_SINGLE |                         it.first?.name ?: it.second?.name ?: "NULL" | ||||||
|  |                     }) | ||||||
| 
 | 
 | ||||||
|             subtitleList.setSelection(subtitleIndex) |                     providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE | ||||||
|             subtitleList.setItemChecked(subtitleIndex, true) |                     providerList.adapter = sourcesArrayAdapter | ||||||
|  |                     providerList.setSelection(sourceIndex) | ||||||
|  |                     providerList.setItemChecked(sourceIndex, true) | ||||||
| 
 | 
 | ||||||
|             subtitleList.setOnItemClickListener { _, _, which, _ -> |                     providerList.setOnItemClickListener { _, _, which, _ -> | ||||||
|                 subtitleIndex = which |                         sourceIndex = which | ||||||
|                 subtitleList.setItemChecked(which, true) |                         providerList.setItemChecked(which, true) | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             cancelButton.setOnClickListener { |  | ||||||
|                 sourceDialog.dismissSafe(activity) |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             applyButton.setOnClickListener { |  | ||||||
|                 var init = false |  | ||||||
|                 if (sourceIndex != startSource) { |  | ||||||
|                     init = true |  | ||||||
|                 } |  | ||||||
|                 if (subtitleIndex != subtitleIndexStart) { |  | ||||||
|                     init = init || if (subtitleIndex <= 0) { |  | ||||||
|                         noSubtitles() |  | ||||||
|                     } else { |  | ||||||
|                         setSubtitles(currentSubtitles[subtitleIndex - 1]) |  | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 if (init) { | 
 | ||||||
|                     loadLink(sortedUrls[sourceIndex], true) |                 sourceDialog.setOnDismissListener { | ||||||
|  |                     if (isPlaying) { | ||||||
|  |                         player.handleEvent(CSPlayerEvent.Play) | ||||||
|  |                     } | ||||||
|  |                     activity?.hideSystemUI() | ||||||
|  |                     selectSourceDialog = null | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 val subtitleIndexStart = currentSubtitles.indexOf(currentSelectedSubtitles) + 1 | ||||||
|  |                 var subtitleIndex = subtitleIndexStart | ||||||
|  | 
 | ||||||
|  |                 val subsArrayAdapter = | ||||||
|  |                     ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice) | ||||||
|  |                 subsArrayAdapter.add(getString(R.string.no_subtitles)) | ||||||
|  |                 subsArrayAdapter.addAll(currentSubtitles.map { it.name }) | ||||||
|  | 
 | ||||||
|  |                 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) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 cancelButton.setOnClickListener { | ||||||
|  |                     sourceDialog.dismissSafe(activity) | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 applyButton.setOnClickListener { | ||||||
|  |                     var init = false | ||||||
|  |                     if (sourceIndex != startSource) { | ||||||
|  |                         init = true | ||||||
|  |                     } | ||||||
|  |                     if (subtitleIndex != subtitleIndexStart) { | ||||||
|  |                         init = init || if (subtitleIndex <= 0) { | ||||||
|  |                             noSubtitles() | ||||||
|  |                         } else { | ||||||
|  |                             setSubtitles(currentSubtitles[subtitleIndex - 1]) | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     if (init) { | ||||||
|  |                         loadLink(sortedUrls[sourceIndex], true) | ||||||
|  |                     } | ||||||
|  |                     sourceDialog.dismissSafe(activity) | ||||||
|                 } |                 } | ||||||
|                 sourceDialog.dismissSafe(activity) |  | ||||||
|             } |             } | ||||||
|  |         } catch (e : Exception) { | ||||||
|  |             logError(e) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -755,9 +755,13 @@ class ResultFragment : Fragment() { | ||||||
| 
 | 
 | ||||||
|                 ACTION_PLAY_EPISODE_IN_BROWSER -> { |                 ACTION_PLAY_EPISODE_IN_BROWSER -> { | ||||||
|                     acquireSingeExtractorLink(getString(R.string.episode_action_play_in_browser)) { link -> |                     acquireSingeExtractorLink(getString(R.string.episode_action_play_in_browser)) { link -> | ||||||
|                         val i = Intent(ACTION_VIEW) |                         try { | ||||||
|                         i.data = Uri.parse(link.url) |                             val i = Intent(ACTION_VIEW) | ||||||
|                         startActivity(i) |                             i.data = Uri.parse(link.url) | ||||||
|  |                             startActivity(i) | ||||||
|  |                         } catch (e : Exception) { | ||||||
|  |                             logError(e) | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | @ -1125,11 +1129,15 @@ class ResultFragment : Fragment() { | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         result_share?.setOnClickListener { |                         result_share?.setOnClickListener { | ||||||
|                             val i = Intent(ACTION_SEND) |                             try { | ||||||
|                             i.type = "text/plain" |                                 val i = Intent(ACTION_SEND) | ||||||
|                             i.putExtra(EXTRA_SUBJECT, d.name) |                                 i.type = "text/plain" | ||||||
|                             i.putExtra(EXTRA_TEXT, d.url) |                                 i.putExtra(EXTRA_SUBJECT, d.name) | ||||||
|                             startActivity(createChooser(i, d.name)) |                                 i.putExtra(EXTRA_TEXT, d.url) | ||||||
|  |                                 startActivity(createChooser(i, d.name)) | ||||||
|  |                             } catch (e: Exception) { | ||||||
|  |                                 logError(e) | ||||||
|  |                             } | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         updateSync(d.getId()) |                         updateSync(d.getId()) | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ import com.lagradost.cloudstream3.DubStatus | ||||||
| import com.lagradost.cloudstream3.R | import com.lagradost.cloudstream3.R | ||||||
| import com.lagradost.cloudstream3.app | import com.lagradost.cloudstream3.app | ||||||
| import com.lagradost.cloudstream3.mvvm.logError | import com.lagradost.cloudstream3.mvvm.logError | ||||||
|  | import com.lagradost.cloudstream3.mvvm.normalSafeApiCall | ||||||
| import com.lagradost.cloudstream3.syncproviders.AccountManager | import com.lagradost.cloudstream3.syncproviders.AccountManager | ||||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API | import com.lagradost.cloudstream3.syncproviders.OAuth2API | ||||||
| import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi | ||||||
|  | @ -301,22 +302,24 @@ class SettingsFragment : PreferenceFragmentCompat() { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fun getDownloadDirs(): List<String> { |         fun getDownloadDirs(): List<String> { | ||||||
|             val defaultDir = getDownloadDir()?.filePath |             return normalSafeApiCall { | ||||||
|  |                 val defaultDir = getDownloadDir()?.filePath | ||||||
| 
 | 
 | ||||||
|             // app_name_download_path = Cloudstream and does not change depending on release. |                 // app_name_download_path = Cloudstream and does not change depending on release. | ||||||
|             // DOES NOT WORK ON SCOPED STORAGE. |                 // DOES NOT WORK ON SCOPED STORAGE. | ||||||
|             val secondaryDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + |                 val secondaryDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + | ||||||
|                     File.separator + resources.getString(R.string.app_name_download_path) |                         File.separator + resources.getString(R.string.app_name_download_path) | ||||||
|             val first = listOf(defaultDir, secondaryDir) |                 val first = listOf(defaultDir, secondaryDir) | ||||||
|             return (try { |                 (try { | ||||||
|                 val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second } |                     val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second } | ||||||
| 
 | 
 | ||||||
|                 (first + |                     (first + | ||||||
|                         requireContext().getExternalFilesDirs("").mapNotNull { it.path } + |                             requireContext().getExternalFilesDirs("").mapNotNull { it.path } + | ||||||
|                         currentDir) |                             currentDir) | ||||||
|             } catch (e: Exception) { |                 } catch (e: Exception) { | ||||||
|                 first |                     first | ||||||
|             }).filterNotNull().distinct() |                 }).filterNotNull().distinct() | ||||||
|  |             } ?: emptyList() | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         downloadPathPreference.setOnPreferenceClickListener { |         downloadPathPreference.setOnPreferenceClickListener { | ||||||
|  |  | ||||||
|  | @ -126,7 +126,12 @@ class InAppUpdater { | ||||||
|                         } |                         } | ||||||
|                     )!! < 0 else false |                     )!! < 0 else false | ||||||
|                 return if (foundVersion != null) { |                 return if (foundVersion != null) { | ||||||
|                     Update(shouldUpdate, foundAsset.browser_download_url, foundVersion.groupValues[2], found.body) |                     Update( | ||||||
|  |                         shouldUpdate, | ||||||
|  |                         foundAsset.browser_download_url, | ||||||
|  |                         foundVersion.groupValues[2], | ||||||
|  |                         found.body | ||||||
|  |                     ) | ||||||
|                 } else { |                 } else { | ||||||
|                     Update(false, null, null, null) |                     Update(false, null, null, null) | ||||||
|                 } |                 } | ||||||
|  | @ -135,7 +140,8 @@ class InAppUpdater { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         private fun Activity.getPreReleaseUpdate(): Update { |         private fun Activity.getPreReleaseUpdate(): Update { | ||||||
|             val tagUrl = "https://api.github.com/repos/LagradOst/CloudStream-3/git/ref/tags/pre-release" |             val tagUrl = | ||||||
|  |                 "https://api.github.com/repos/LagradOst/CloudStream-3/git/ref/tags/pre-release" | ||||||
|             val releaseUrl = "https://api.github.com/repos/LagradOst/CloudStream-3/releases" |             val releaseUrl = "https://api.github.com/repos/LagradOst/CloudStream-3/releases" | ||||||
|             val headers = mapOf("Accept" to "application/vnd.github.v3+json") |             val headers = mapOf("Accept" to "application/vnd.github.v3+json") | ||||||
|             val response = |             val response = | ||||||
|  | @ -150,10 +156,16 @@ class InAppUpdater { | ||||||
|             val tagResponse = |             val tagResponse = | ||||||
|                 mapper.readValue<GithubTag>(app.get(tagUrl, headers = headers).text) |                 mapper.readValue<GithubTag>(app.get(tagUrl, headers = headers).text) | ||||||
| 
 | 
 | ||||||
|             val shouldUpdate = (getString(R.string.prerelease_commit_hash) != tagResponse.github_object.sha) |             val shouldUpdate = | ||||||
|  |                 (getString(R.string.prerelease_commit_hash) != tagResponse.github_object.sha) | ||||||
| 
 | 
 | ||||||
|             return if (foundAsset != null) { |             return if (foundAsset != null) { | ||||||
|                 Update(shouldUpdate, foundAsset.browser_download_url, tagResponse.github_object.sha, found.body) |                 Update( | ||||||
|  |                     shouldUpdate, | ||||||
|  |                     foundAsset.browser_download_url, | ||||||
|  |                     tagResponse.github_object.sha, | ||||||
|  |                     found.body | ||||||
|  |                 ) | ||||||
|             } else { |             } else { | ||||||
|                 Update(false, null, null, null) |                 Update(false, null, null, null) | ||||||
|             } |             } | ||||||
|  | @ -217,26 +229,33 @@ class InAppUpdater { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fun openApk(context: Context, uri: Uri) { |         fun openApk(context: Context, uri: Uri) { | ||||||
|             uri.path?.let { |             try { | ||||||
|                 val contentUri = FileProvider.getUriForFile( |                 uri.path?.let { | ||||||
|                     context, |                     val contentUri = FileProvider.getUriForFile( | ||||||
|                     BuildConfig.APPLICATION_ID + ".provider", |                         context, | ||||||
|                     File(it) |                         BuildConfig.APPLICATION_ID + ".provider", | ||||||
|                 ) |                         File(it) | ||||||
|                 val installIntent = Intent(Intent.ACTION_VIEW).apply { |                     ) | ||||||
|                     addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) |                     val installIntent = Intent(Intent.ACTION_VIEW).apply { | ||||||
|                     addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) |                         addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) | ||||||
|                     putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) |                         addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) | ||||||
|                     data = contentUri |                         putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) | ||||||
|  |                         data = contentUri | ||||||
|  |                     } | ||||||
|  |                     context.startActivity(installIntent) | ||||||
|                 } |                 } | ||||||
|                 context.startActivity(installIntent) |             } catch (e: Exception) { | ||||||
|  |                 logError(e) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         fun Activity.runAutoUpdate(checkAutoUpdate: Boolean = true): Boolean { |         fun Activity.runAutoUpdate(checkAutoUpdate: Boolean = true): Boolean { | ||||||
|             val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) |             val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) | ||||||
| 
 | 
 | ||||||
|             if (!checkAutoUpdate || settingsManager.getBoolean(getString(R.string.auto_update_key), true) |             if (!checkAutoUpdate || settingsManager.getBoolean( | ||||||
|  |                     getString(R.string.auto_update_key), | ||||||
|  |                     true | ||||||
|  |                 ) | ||||||
|             ) { |             ) { | ||||||
|                 val update = getAppUpdate() |                 val update = getAppUpdate() | ||||||
|                 if (update.shouldUpdate && update.updateURL != null) { |                 if (update.shouldUpdate && update.updateURL != null) { | ||||||
|  | @ -264,7 +283,8 @@ class InAppUpdater { | ||||||
|                                     showToast(context, R.string.download_started, Toast.LENGTH_LONG) |                                     showToast(context, R.string.download_started, Toast.LENGTH_LONG) | ||||||
|                                     thread { |                                     thread { | ||||||
|                                         val downloadStatus = |                                         val downloadStatus = | ||||||
|                                             normalSafeApiCall { context.downloadUpdate(update.updateURL) } ?: false |                                             normalSafeApiCall { context.downloadUpdate(update.updateURL) } | ||||||
|  |                                                 ?: false | ||||||
|                                         if (!downloadStatus) { |                                         if (!downloadStatus) { | ||||||
|                                             runOnUiThread { |                                             runOnUiThread { | ||||||
|                                                 showToast( |                                                 showToast( | ||||||
|  | @ -281,7 +301,8 @@ class InAppUpdater { | ||||||
| 
 | 
 | ||||||
|                                 if (checkAutoUpdate) { |                                 if (checkAutoUpdate) { | ||||||
|                                     setNeutralButton(R.string.dont_show_again) { _, _ -> |                                     setNeutralButton(R.string.dont_show_again) { _, _ -> | ||||||
|                                         settingsManager.edit().putBoolean("auto_update", false).apply() |                                         settingsManager.edit().putBoolean("auto_update", false) | ||||||
|  |                                             .apply() | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue