mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-11-25 19:44:50 +00:00
Better error handling of terminated channels when loading feed
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
|
||||
package org.schabi.newpipe.local.feed
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
@@ -28,6 +29,7 @@ import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.os.bundleOf
|
||||
@@ -35,15 +37,24 @@ import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import icepick.State
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import org.schabi.newpipe.NewPipeDatabase
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.database.subscription.SubscriptionEntity
|
||||
import org.schabi.newpipe.databinding.FragmentFeedBinding
|
||||
import org.schabi.newpipe.error.ErrorInfo
|
||||
import org.schabi.newpipe.error.UserAction
|
||||
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
|
||||
import org.schabi.newpipe.fragments.list.BaseListFragment
|
||||
import org.schabi.newpipe.ktx.animate
|
||||
import org.schabi.newpipe.ktx.animateHideRecyclerViewAllowingScrolling
|
||||
import org.schabi.newpipe.local.feed.service.FeedLoadService
|
||||
import org.schabi.newpipe.local.subscription.SubscriptionManager
|
||||
import org.schabi.newpipe.util.Localization
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
@@ -51,6 +62,8 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||
private var _feedBinding: FragmentFeedBinding? = null
|
||||
private val feedBinding get() = _feedBinding!!
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
|
||||
private lateinit var viewModel: FeedViewModel
|
||||
@State
|
||||
@JvmField
|
||||
@@ -158,6 +171,7 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
disposables.dispose()
|
||||
super.onDestroy()
|
||||
activity?.supportActionBar?.subtitle = null
|
||||
}
|
||||
@@ -243,9 +257,9 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||
|
||||
oldestSubscriptionUpdate = loadedState.oldestUpdate
|
||||
|
||||
val loadedCount = loadedState.notLoadedCount > 0
|
||||
feedBinding.refreshSubtitleText.isVisible = loadedCount
|
||||
if (loadedCount) {
|
||||
val feedsNotLoaded = loadedState.notLoadedCount > 0
|
||||
feedBinding.refreshSubtitleText.isVisible = feedsNotLoaded
|
||||
if (feedsNotLoaded) {
|
||||
feedBinding.refreshSubtitleText.text = getString(
|
||||
R.string.feed_subscription_not_loaded_count,
|
||||
loadedState.notLoadedCount
|
||||
@@ -264,11 +278,64 @@ class FeedFragment : BaseListFragment<FeedState, Unit>() {
|
||||
hideLoading()
|
||||
false
|
||||
} else {
|
||||
showError(ErrorInfo(errorState.error, UserAction.REQUESTED_FEED, "Loading feed"))
|
||||
if (errorState.error is FeedLoadService.RequestException) {
|
||||
disposables.add(
|
||||
Single.fromCallable {
|
||||
NewPipeDatabase.getInstance(requireContext()).subscriptionDAO()
|
||||
.getSubscription(errorState.error.subscriptionId)
|
||||
}.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{
|
||||
subscriptionEntity ->
|
||||
handleFeedNotAvailable(
|
||||
subscriptionEntity,
|
||||
errorState.error.cause?.cause
|
||||
)
|
||||
},
|
||||
{ throwable -> throwable.printStackTrace() }
|
||||
)
|
||||
)
|
||||
} else {
|
||||
showError(ErrorInfo(errorState.error, UserAction.REQUESTED_FEED, "Loading feed"))
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFeedNotAvailable(
|
||||
subscriptionEntity: SubscriptionEntity,
|
||||
@Nullable cause: Throwable?
|
||||
) {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val isFastFeedModeEnabled = sharedPreferences.getBoolean(
|
||||
getString(R.string.feed_use_dedicated_fetch_method_key), false
|
||||
)
|
||||
val builder = AlertDialog.Builder(requireContext())
|
||||
.setTitle(R.string.feed_load_error)
|
||||
.setPositiveButton(
|
||||
R.string.unsubscribe,
|
||||
DialogInterface.OnClickListener {
|
||||
_, _ ->
|
||||
SubscriptionManager(requireContext()).deleteSubscription(
|
||||
subscriptionEntity.serviceId, subscriptionEntity.url
|
||||
).subscribe()
|
||||
}
|
||||
)
|
||||
.setNegativeButton(R.string.cancel, DialogInterface.OnClickListener { _, _ -> })
|
||||
if (cause is AccountTerminatedException) {
|
||||
builder.setMessage(R.string.feed_load_error_terminated)
|
||||
} else if (cause is ContentNotAvailableException && isFastFeedModeEnabled) {
|
||||
builder.setMessage(R.string.feed_load_error_fast_unknown)
|
||||
.setNeutralButton(R.string.feed_use_dedicated_fetch_method_disable_button) { _, _ ->
|
||||
sharedPreferences.edit {
|
||||
putBoolean(getString(R.string.feed_use_dedicated_fetch_method_key), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.create().show()
|
||||
}
|
||||
|
||||
private fun updateRelativeTimeViews() {
|
||||
updateRefreshViewState()
|
||||
infoListAdapter.notifyDataSetChanged()
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.schabi.newpipe.MainActivity.DEBUG
|
||||
import org.schabi.newpipe.R
|
||||
import org.schabi.newpipe.database.feed.model.FeedGroupEntity
|
||||
import org.schabi.newpipe.extractor.ListInfo
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
||||
import org.schabi.newpipe.ktx.isNetworkRelated
|
||||
@@ -162,7 +163,7 @@ class FeedLoadService : Service() {
|
||||
// Loading & Handling
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private class RequestException(val subscriptionId: Long, message: String, cause: Throwable) : Exception(message, cause) {
|
||||
public class RequestException(val subscriptionId: Long, message: String, cause: Throwable) : Exception(message, cause) {
|
||||
companion object {
|
||||
fun wrapList(subscriptionId: Long, info: ListInfo<StreamInfoItem>): List<Throwable> {
|
||||
val toReturn = ArrayList<Throwable>(info.errors.size)
|
||||
@@ -334,8 +335,9 @@ class FeedLoadService : Service() {
|
||||
private val errorHandlingConsumer: Consumer<Notification<Pair<Long, ListInfo<StreamInfoItem>>>>
|
||||
get() = Consumer {
|
||||
if (it.isOnError) {
|
||||
var error = it.error!!
|
||||
if (error is RequestException) error = error.cause!!
|
||||
var maybeWrapper = it.error!!
|
||||
val error = if (maybeWrapper is RequestException) maybeWrapper.cause!!
|
||||
else maybeWrapper
|
||||
val cause = error.cause
|
||||
|
||||
when {
|
||||
@@ -345,6 +347,19 @@ class FeedLoadService : Service() {
|
||||
error is IOException -> throw error
|
||||
cause is IOException -> throw cause
|
||||
error.isNetworkRelated -> throw IOException(error)
|
||||
|
||||
cause is ContentNotAvailableException -> {
|
||||
// maybeWrapper is definitely a RequestException,
|
||||
// because this is an exception thrown in the extractor
|
||||
if (maybeWrapper is RequestException) {
|
||||
throw maybeWrapper
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Cause is ContentNotAvailableException, but maybeWrapper is not a RequestException")
|
||||
}
|
||||
throw cause // should never be the case
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user