crash fix and headphones pause

This commit is contained in:
LagradOst 2022-01-13 22:09:05 +01:00
parent ca4d05bd34
commit 0f1229354a
9 changed files with 246 additions and 149 deletions

View file

@ -8,7 +8,6 @@
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-11" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

View file

@ -35,8 +35,8 @@ android {
minSdkVersion 21
targetSdkVersion 30
versionCode 40
versionName "2.4.8"
versionCode 41
versionName "2.5.8"
resValue "string", "app_version",
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"

View file

@ -39,7 +39,6 @@ object APIHolder {
WcoProvider(),
// MeloMovieProvider(), // Captcha for links
DubbedAnimeProvider(),
HDMProvider(),
IHaveNoTvProvider(), // Documentaries provider
//LookMovieProvider(), // RECAPTCHA (Please allow up to 5 seconds...)
VMoveeProvider(),
@ -58,8 +57,6 @@ object APIHolder {
//TmdbProvider(),
FilmanProvider(),
ZoroProvider(),
@ -81,6 +78,7 @@ object APIHolder {
private val backwardsCompatibleProviders = arrayListOf(
KawaiifuProvider(), // removed due to cloudflare
HDMProvider(),// removed due to cloudflare
)
fun getApiFromName(apiName: String?): MainAPI {
@ -179,7 +177,8 @@ object APIHolder {
fun Context.filterProviderByPreferredMedia(): List<MainAPI> {
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 allApis = apis.filter { langs.contains(it.lang) }.filter { api -> api.hasMainPage }
return if (currentPrefMedia < 1) {
@ -275,7 +274,7 @@ fun parseRating(ratingString: String?): Int? {
return (floatRating * 10).toInt()
}
fun MainAPI.fixUrlNull(url : String?) : String? {
fun MainAPI.fixUrlNull(url: String?): String? {
if (url.isNullOrEmpty()) {
return null
}
@ -305,7 +304,7 @@ fun sortUrls(urls: Set<ExtractorLink>): List<ExtractorLink> {
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 }
}
@ -473,7 +472,7 @@ fun LoadResponse?.isAnimeBased(): Boolean {
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
return (this == TvType.TvSeries || this == TvType.Anime)
}
@ -573,7 +572,13 @@ fun MainAPI.newMovieLoadResponse(
dataUrl: String,
initializer: MovieLoadResponse.() -> Unit = { }
): 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()
return builder
}
@ -634,7 +639,13 @@ fun MainAPI.newTvSeriesLoadResponse(
episodes: List<TvSeriesEpisode>,
initializer: TvSeriesLoadResponse.() -> Unit = { }
): 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()
return builder
}

View file

@ -1,26 +1,27 @@
package com.lagradost.cloudstream3.ui.player
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.*
import android.graphics.drawable.AnimatedImageDrawable
import android.graphics.drawable.AnimatedVectorDrawable
import android.media.metrics.PlaybackErrorEvent
import android.os.Build
import android.os.Bundle
import android.support.v4.media.session.MediaSessionCompat
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.media.session.MediaButtonReceiver
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.google.android.exoplayer2.ExoPlayer
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.SubtitleView
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
@ -81,9 +82,20 @@ abstract class AbstractPlayerFragment(
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>) {
val (wasPlaying, isPlaying) = playing
val isPlayingRightNow = CSPlayerLoading.IsPlaying == isPlaying
val isPausedRightNow = CSPlayerLoading.IsPaused == isPlaying
keepScreenOn(!isPausedRightNow)
isBuffering = CSPlayerLoading.IsBuffering == isPlaying
if (isBuffering) {
@ -241,11 +253,48 @@ abstract class AbstractPlayerFragment(
private fun playerUpdated(player: Any?) {
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?.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")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
resizeMode = getKey(RESIZE_MODE_KEY) ?: 0
@ -295,6 +344,7 @@ abstract class AbstractPlayerFragment(
keyEventListener = null
SubtitlesFragment.applyStyleEvent -= ::onSubStyleChanged
keepScreenOn(false)
super.onDestroy()
}

View file

@ -130,7 +130,7 @@ open class FullScreenPlayer : AbstractPlayerFragment(R.layout.fragment_player) {
if (isShowing) {
updateUIVisibility()
} else {
player_holder.postDelayed({ updateUIVisibility() }, 200)
player_holder?.postDelayed({ updateUIVisibility() }, 200)
}
val titleMove = if (isShowing) 0f else -50.toPx.toFloat()

View file

@ -17,6 +17,7 @@ import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
@ -171,111 +172,115 @@ class GeneratorPlayer : FullScreenPlayer() {
var selectSourceDialog: AlertDialog? = null
override fun showMirrorsDialogue() {
currentSelectedSubtitles = player.getCurrentPreferredSubtitle()
context?.let { ctx ->
val isPlaying = player.getIsPlaying()
player.handleEvent(CSPlayerEvent.Pause)
val currentSubtitles = sortSubs(currentSubs)
try {
currentSelectedSubtitles = player.getCurrentPreferredSubtitle()
context?.let { ctx ->
val isPlaying = player.getIsPlaying()
player.handleEvent(CSPlayerEvent.Pause)
val currentSubtitles = sortSubs(currentSubs)
val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack)
.setView(R.layout.player_select_source_and_subs)
val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack)
.setView(R.layout.player_select_source_and_subs)
val sourceDialog = sourceBuilder.create()
selectSourceDialog = sourceDialog
sourceDialog.show()
val providerList =
sourceDialog.findViewById<ListView>(R.id.sort_providers)!!
val subtitleList =
sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!!
val applyButton =
sourceDialog.findViewById<MaterialButton>(R.id.apply_btt)!!
val cancelButton =
sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!!
val sourceDialog = sourceBuilder.create()
selectSourceDialog = sourceDialog
sourceDialog.show()
val providerList =
sourceDialog.findViewById<ListView>(R.id.sort_providers)!!
val subtitleList =
sourceDialog.findViewById<ListView>(R.id.sort_subtitles)!!
val applyButton =
sourceDialog.findViewById<MaterialButton>(R.id.apply_btt)!!
val cancelButton =
sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!!
val footer: TextView =
layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView
footer.text = ctx.getString(R.string.player_load_subtitles)
footer.setOnClickListener {
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)
val footer: TextView =
layoutInflater.inflate(R.layout.sort_bottom_footer_add_choice, null) as TextView
footer.text = ctx.getString(R.string.player_load_subtitles)
footer.setOnClickListener {
openSubPicker()
}
}
subtitleList.addFooterView(footer)
sourceDialog.setOnDismissListener {
if (isPlaying) {
player.handleEvent(CSPlayerEvent.Play)
}
activity?.hideSystemUI()
selectSourceDialog = null
}
var sourceIndex = 0
var startSource = 0
val subtitleIndexStart = currentSubtitles.indexOf(currentSelectedSubtitles) + 1
var subtitleIndex = subtitleIndexStart
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 subsArrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
subsArrayAdapter.add(getString(R.string.no_subtitles))
subsArrayAdapter.addAll(currentSubtitles.map { it.name })
val sourcesArrayAdapter =
ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
subtitleList.adapter = subsArrayAdapter
subtitleList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
sourcesArrayAdapter.addAll(sortedUrls.map {
it.first?.name ?: it.second?.name ?: "NULL"
})
subtitleList.setSelection(subtitleIndex)
subtitleList.setItemChecked(subtitleIndex, true)
providerList.choiceMode = AbsListView.CHOICE_MODE_SINGLE
providerList.adapter = sourcesArrayAdapter
providerList.setSelection(sourceIndex)
providerList.setItemChecked(sourceIndex, 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])
providerList.setOnItemClickListener { _, _, which, _ ->
sourceIndex = which
providerList.setItemChecked(which, true)
}
}
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)
}
}

View file

@ -755,9 +755,13 @@ class ResultFragment : Fragment() {
ACTION_PLAY_EPISODE_IN_BROWSER -> {
acquireSingeExtractorLink(getString(R.string.episode_action_play_in_browser)) { link ->
val i = Intent(ACTION_VIEW)
i.data = Uri.parse(link.url)
startActivity(i)
try {
val i = Intent(ACTION_VIEW)
i.data = Uri.parse(link.url)
startActivity(i)
} catch (e : Exception) {
logError(e)
}
}
}
@ -1125,11 +1129,15 @@ class ResultFragment : Fragment() {
}
result_share?.setOnClickListener {
val i = Intent(ACTION_SEND)
i.type = "text/plain"
i.putExtra(EXTRA_SUBJECT, d.name)
i.putExtra(EXTRA_TEXT, d.url)
startActivity(createChooser(i, d.name))
try {
val i = Intent(ACTION_SEND)
i.type = "text/plain"
i.putExtra(EXTRA_SUBJECT, d.name)
i.putExtra(EXTRA_TEXT, d.url)
startActivity(createChooser(i, d.name))
} catch (e: Exception) {
logError(e)
}
}
updateSync(d.getId())

View file

@ -32,6 +32,7 @@ import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.OAuth2API
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi
@ -301,22 +302,24 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
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.
// DOES NOT WORK ON SCOPED STORAGE.
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)
val first = listOf(defaultDir, secondaryDir)
return (try {
val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second }
// app_name_download_path = Cloudstream and does not change depending on release.
// DOES NOT WORK ON SCOPED STORAGE.
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)
val first = listOf(defaultDir, secondaryDir)
(try {
val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second }
(first +
requireContext().getExternalFilesDirs("").mapNotNull { it.path } +
currentDir)
} catch (e: Exception) {
first
}).filterNotNull().distinct()
(first +
requireContext().getExternalFilesDirs("").mapNotNull { it.path } +
currentDir)
} catch (e: Exception) {
first
}).filterNotNull().distinct()
} ?: emptyList()
}
downloadPathPreference.setOnPreferenceClickListener {

View file

@ -126,7 +126,12 @@ class InAppUpdater {
}
)!! < 0 else false
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 {
Update(false, null, null, null)
}
@ -135,7 +140,8 @@ class InAppUpdater {
}
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 headers = mapOf("Accept" to "application/vnd.github.v3+json")
val response =
@ -150,10 +156,16 @@ class InAppUpdater {
val tagResponse =
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) {
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 {
Update(false, null, null, null)
}
@ -217,26 +229,33 @@ class InAppUpdater {
}
fun openApk(context: Context, uri: Uri) {
uri.path?.let {
val contentUri = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".provider",
File(it)
)
val installIntent = Intent(Intent.ACTION_VIEW).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
data = contentUri
try {
uri.path?.let {
val contentUri = FileProvider.getUriForFile(
context,
BuildConfig.APPLICATION_ID + ".provider",
File(it)
)
val installIntent = Intent(Intent.ACTION_VIEW).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
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 {
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()
if (update.shouldUpdate && update.updateURL != null) {
@ -264,7 +283,8 @@ class InAppUpdater {
showToast(context, R.string.download_started, Toast.LENGTH_LONG)
thread {
val downloadStatus =
normalSafeApiCall { context.downloadUpdate(update.updateURL) } ?: false
normalSafeApiCall { context.downloadUpdate(update.updateURL) }
?: false
if (!downloadStatus) {
runOnUiThread {
showToast(
@ -281,7 +301,8 @@ class InAppUpdater {
if (checkAutoUpdate) {
setNeutralButton(R.string.dont_show_again) { _, _ ->
settingsManager.edit().putBoolean("auto_update", false).apply()
settingsManager.edit().putBoolean("auto_update", false)
.apply()
}
}
}