1
0
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:
TobiGr
2021-04-02 21:41:06 +02:00
parent 761e01c3b9
commit 6ad4b425e4
4 changed files with 95 additions and 8 deletions

View File

@@ -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()

View File

@@ -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
}
}
}
}
}