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
|
@ -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…
Reference in a new issue