This was effectively DDoSing sources as it does a request for every entry to get the details (primarily a cover image).
The expectation now is that users have to open individual entries to load the details/cover if needed.
This isn't necessary for most sources, which are able to provide covers as part of the listing normally.
* Drop duplicate initial call in Preference.asHotFlow
Preference.changes() always starts by returning the current value of
the preference, so asHotFlow calls block twice on the initial value.
Possible breaking change: As implemented, asHotFlow ran block(get())
before returning the flow. After this change, the first call to block
will run within the flow collection. This might cause concurrency
issues if the flow collection is late to execute.
* Inline Preference.asHotFlow
The Preference.changes().onEach().launchIn() pattern is used widely,
so the asHotFlow extension method is redundant.
This partially reverts commit 2769525b2c.
Keeps the change to silently ignore spliting errors since it falls back to
the original images in those cases.
Jetpack Compose treats mouse input differently than just mimicking a touch input, so dragging doesn't actually
invoke the pull to refresh. If that changes in the future, we could consider removing these.
Doesn't seem too necessary for the extensions list, so I skipped that.
Closes#8455
Downloader.stop is now the sole responsible for stopping the
DownloadService. This will help cleanly removing
DownloadService.stop when migrating to coroutines.
* Rename functions for DownloadService internal use
* Call DownloadService.start via DownloadManager
* Inline DownloadService.stop into pauseDownloads
* Inline DownloadService.stop into clearQueue
NotificationReceiver will now also stop the DownloadService when
receiving ACTION_CLEAR_DOWNLOADS.
* Provide DownloadService.isRunning via DownloadManager
1:1 translation from the RxJava implementation, should match the
previous behavior.
Dropped the return value from functions of the form
```
fun foo(t: T, ...): Observable<T>
```
where the Observable produced the original argument `t`.
The caller already has the result if necessary.
While this conversion is not flow-based overall, some sections use
flows to use the flatMapMerge and retryWhen operators.
Removed RetryWithDelay as it was only used here.
Inlined fetchAllImageUrlsFromPageList instead of converting it to a
suspending equivalent. fetchAllImageUrlsFromPageList is no longer
used in the app, but was not removed as it is part of source-api.
(However, it does not seem to be used exposed in extensions-lib or
used in tachiyomi-extensions.)
runBlocking is used as a temporary stop-gap.
When restarting a download, the page count would display as 0 until
the first page download completion, after all the existing pages were
rechecked.
To fix, calculate downloadedImages from pages instead of relying on
the downloader to reset and increment the count.
Includes side effects:
- No longer need to restart app for user agent string change to take effect
- parseAs extension function requires a Json instance in the calling context, which doesn't necessarily need to be the default one provided by Injekt
`it.id` is the source ID of the source being sorted.
`state.value.manga!!.id` is the manga ID of the selected manga.
`state.value.manga!!.source` is the source ID of the selected manga.
(cherry picked from commit dc2eaf0788)
Inline readImageHeaderSubscription in PageHolder
Inline readImageHeaderSubscription in PagerPageHolder and
WebtoonPageHolder by converting setImage() into a suspend function.
The image processing runs in the loadPageAndProcessStatus
continuation.
Use suspendCancellableCoroutine as a substitute for doOnUnsubscribe
in WebtoonPageHolder.
Closing openStream after the frame.setImage but before the PageHolder
is recycled causes the page display to fail for reasons that are not
currently understood.
Remove subscription handling from WebtoonViewer/WebtoonBaseHolder as
it is no longer used.
Inline statusJob into loadJob, using supervisorScope to load the page
and track status changes in parallel.
- supervisorScope does not complete until both the child loadPage
coroutine and statusFlow.collectLatest have completed.
- Cancelling supervisorScope cancels the child loadPage coroutine and
statusFlow.collectLatest.
- Use supervisorScope instead of coroutineScope to let status
collection continue if loadPage fails.
Inline progressJob into loadJob, using collectLatest's cancellation
to avoid cancelling the progressFlow collection explicitly.
- collectLatest cancels the previous action block when the flow
emits a new value. This means the DOWNLOAD_IMAGE
progressFlow.collectLatest gets automatically cancelled when
statusFlow emits a new state.
Convert launchLoadJob to suspend function, move job launch to caller,
and rename as loadPageAndProcessStatus.
`it.id` is the source ID of the source being sorted.
`state.value.manga!!.id` is the manga ID of the selected manga.
`state.value.manga!!.source` is the source ID of the selected manga.
* Rework the wheel picker
doesn't need for the animation to stop to change the value
* fix
---------
Co-authored-by: arkon <arkon@users.noreply.github.com>
* Move LibraryItem vars to constructor vals
* Convert LibraryItem to data class
Remove redundant equals and hashCode
* Remove unused LibraryItem.displayMode
* Simplify LibraryItem.matches()
* Align types in LibraryItem and LibraryBadges
* fixup! Simplify LibraryItem.matches()
No more trampolining, and stuff.
It's pretty much straight copy-paste from the service, with
some changes related to cancellation handling. Manual updates
will also runs with workman job so auto update work
scheduling need some adjustments too.
Bumped version code to re-enqueue auto update job with the
new spec.
Co-authored-by: arkon <arkon@users.noreply.github.com>
* Misc cleanup
- Replace !List.isEmpty with List.isNotEmpty
- Remove redundant case in MoreScreenModel
- Drop no-op StateFlow.catch
- From lint warning:
> SharedFlow never completes, so this operator typically has not
> effect, it can only catch exceptions from 'onSubscribe' operator
* Convert DownloadQueue queue to MutableStateFlow
Replace delegation to a MutableList with an internal
MutableStateFlow<List>.
In order to avoid modifying every usage of the queue as a list, add
passthrough functions for the currently used list functions. This
should be later refactored, possibly by inlining DownloadQueue
into Downloader.
DownloadQueue.updates was a SharedFlow which updated every time a
change was made to the queue. This is now equivalent to the queue
StateFlow.
Simultaneous assignments to _state.value could cause concurrency
issues. To avoid this, always modify the queue using _state.update.
* Add Download.statusFlow/progressFlow
progressFlow is based on the DownloadQueueScreenModel implementation
rather than the DownloadQueue implementation.
* Reimplement DownloadQueue.statusFlow/progressFlow
Use StateFlow<List<T>>.flatMapLatest() and List<Flow<T>>.merge() to
replicate the effect of PublishSubject.
Use drop(1) to avoid re-emitting the state of each download each time
the merged flow is recreated.
* fixup! Reimplement DownloadQueue.statusFlow/progressFlow
This reverts commit 6bb3070c57.
This doesn't quite work correctly, so reverting for now.
We'll have to have more robust states or something to deal with this in the
future.
* Simplify DownloadService wake lock handling
_isRunning is only modified in onCreate/onDestroy, so the listener
job is redundant.
* Drop superclass calls to Service.onCreate/onDestroy
From https://developer.android.com/guide/components/services
> Note: Unlike the activity lifecycle callback methods, you are not
> required to call the superclass implementation of these callback
> methods.
Fixes#8962.
withTimeout throws a TimeoutCancellationException if the timeout
expires. To avoid crashing renewalJob when there are no extensions,
use withTimeoutOrNull which does not throw on timeout.
Fixup for e4bc8990 (#8955)
HttpSource.fetchImage() uses Call.asObservableSuccess(), which
cancels the call on unsubscribe. This causes the call to be cancelled
before it is used, leading to a "java.net.SocketException: Socket is
closed" when trying to use the response in putImageToCache().
To fix this, use Call.awaitSuccess() via a new HttpSource.getImage()
suspending function. This addition to source-api is only intended for
app use, so it will not be added to the extensions-api stubs.