mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-24 13:48:55 +03:00
Reimplement chapter download indicator longpress (#7412)
This commit is contained in:
parent
f3c50ee9a3
commit
deaded5af2
8 changed files with 169 additions and 4 deletions
|
@ -92,6 +92,10 @@ class ChapterRepositoryImpl(
|
||||||
return handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, chapterMapper) }
|
return handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, chapterMapper) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getChapterById(id: Long): Chapter? {
|
||||||
|
return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, chapterMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
|
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
|
||||||
return handler.subscribeToList { chaptersQueries.getChaptersByMangaId(mangaId, chapterMapper) }
|
return handler.subscribeToList { chaptersQueries.getChaptersByMangaId(mangaId, chapterMapper) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import eu.kanade.domain.category.interactor.InsertCategory
|
||||||
import eu.kanade.domain.category.interactor.MoveMangaToCategories
|
import eu.kanade.domain.category.interactor.MoveMangaToCategories
|
||||||
import eu.kanade.domain.category.interactor.UpdateCategory
|
import eu.kanade.domain.category.interactor.UpdateCategory
|
||||||
import eu.kanade.domain.category.repository.CategoryRepository
|
import eu.kanade.domain.category.repository.CategoryRepository
|
||||||
|
import eu.kanade.domain.chapter.interactor.GetChapter
|
||||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||||
import eu.kanade.domain.chapter.interactor.ShouldUpdateDbChapter
|
import eu.kanade.domain.chapter.interactor.ShouldUpdateDbChapter
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||||
|
@ -84,6 +85,7 @@ class DomainModule : InjektModule {
|
||||||
addFactory { InsertTrack(get()) }
|
addFactory { InsertTrack(get()) }
|
||||||
|
|
||||||
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
|
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
|
||||||
|
addFactory { GetChapter(get()) }
|
||||||
addFactory { GetChapterByMangaId(get()) }
|
addFactory { GetChapterByMangaId(get()) }
|
||||||
addFactory { UpdateChapter(get()) }
|
addFactory { UpdateChapter(get()) }
|
||||||
addFactory { ShouldUpdateDbChapter() }
|
addFactory { ShouldUpdateDbChapter() }
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package eu.kanade.domain.chapter.interactor
|
||||||
|
|
||||||
|
import eu.kanade.domain.chapter.model.Chapter
|
||||||
|
import eu.kanade.domain.chapter.repository.ChapterRepository
|
||||||
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
import logcat.LogPriority
|
||||||
|
|
||||||
|
class GetChapter(
|
||||||
|
private val chapterRepository: ChapterRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(id: Long): Chapter? {
|
||||||
|
return try {
|
||||||
|
chapterRepository.getChapterById(id)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logcat(LogPriority.ERROR, e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,5 +16,7 @@ interface ChapterRepository {
|
||||||
|
|
||||||
suspend fun getChapterByMangaId(mangaId: Long): List<Chapter>
|
suspend fun getChapterByMangaId(mangaId: Long): List<Chapter>
|
||||||
|
|
||||||
|
suspend fun getChapterById(id: Long): Chapter?
|
||||||
|
|
||||||
suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>>
|
suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import androidx.compose.material.icons.filled.CheckCircle
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
|
import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||||
|
@ -51,6 +50,14 @@ fun ChapterDownloadIndicator(
|
||||||
onClick(ChapterDownloadAction.START)
|
onClick(ChapterDownloadAction.START)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
onLongClick = {
|
||||||
|
val chapterDownloadAction = when {
|
||||||
|
isDownloaded -> ChapterDownloadAction.DELETE
|
||||||
|
isDownloading -> ChapterDownloadAction.CANCEL
|
||||||
|
else -> ChapterDownloadAction.START_NOW
|
||||||
|
}
|
||||||
|
onClick(chapterDownloadAction)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
if (isDownloaded) {
|
if (isDownloaded) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.interaction.Interaction
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButtonColors
|
||||||
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.util.minimumTouchTargetSize
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a href="https://m3.material.io/components/icon-button/overview" class="external" target="_blank">Material Design standard icon button</a>.
|
||||||
|
*
|
||||||
|
* Icon buttons help people take supplementary actions with a single tap. They’re used when a
|
||||||
|
* compact button is required, such as in a toolbar or image list.
|
||||||
|
*
|
||||||
|
* ![Standard icon button image](https://developer.android.com/images/reference/androidx/compose/material3/standard-icon-button.png)
|
||||||
|
*
|
||||||
|
* [content] should typically be an [Icon] (see [androidx.compose.material.icons.Icons]). If using a
|
||||||
|
* custom icon, note that the typical size for the internal icon is 24 x 24 dp.
|
||||||
|
* This icon button has an overall minimum touch target size of 48 x 48dp, to meet accessibility
|
||||||
|
* guidelines.
|
||||||
|
*
|
||||||
|
* @sample androidx.compose.material3.samples.IconButtonSample
|
||||||
|
*
|
||||||
|
* Tachiyomi changes:
|
||||||
|
* * Add on long click
|
||||||
|
*
|
||||||
|
* @param onClick called when this icon button is clicked
|
||||||
|
* @param modifier the [Modifier] to be applied to this icon button
|
||||||
|
* @param enabled controls the enabled state of this icon button. When `false`, this component will
|
||||||
|
* not respond to user input, and it will appear visually disabled and disabled to accessibility
|
||||||
|
* services.
|
||||||
|
* @param interactionSource the [MutableInteractionSource] representing the stream of [Interaction]s
|
||||||
|
* for this icon button. You can create and pass in your own `remember`ed instance to observe
|
||||||
|
* [Interaction]s and customize the appearance / behavior of this icon button in different states.
|
||||||
|
* @param colors [IconButtonColors] that will be used to resolve the colors used for this icon
|
||||||
|
* button in different states. See [IconButtonDefaults.iconButtonColors].
|
||||||
|
* @param content the content of this icon button, typically an [Icon]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun IconButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
onLongClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||||
|
colors: IconButtonColors = IconButtonDefaults.iconButtonColors(),
|
||||||
|
content: @Composable () -> Unit,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.minimumTouchTargetSize()
|
||||||
|
.size(IconButtonTokens.StateLayerSize)
|
||||||
|
.background(color = colors.containerColor(enabled).value)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
enabled = enabled,
|
||||||
|
role = Role.Button,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = rememberRipple(
|
||||||
|
bounded = false,
|
||||||
|
radius = IconButtonTokens.StateLayerSize / 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
val contentColor = colors.contentColor(enabled).value
|
||||||
|
CompositionLocalProvider(LocalContentColor provides contentColor, content = content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IconButtonTokens {
|
||||||
|
val StateLayerSize = 40.0.dp
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -104,10 +105,12 @@ class DownloadManager(
|
||||||
|
|
||||||
fun startDownloadNow(chapterId: Long?) {
|
fun startDownloadNow(chapterId: Long?) {
|
||||||
if (chapterId == null) return
|
if (chapterId == null) return
|
||||||
val download = downloader.queue.find { it.chapter.id == chapterId } ?: return
|
val download = downloader.queue.find { it.chapter.id == chapterId }
|
||||||
|
// If not in queue try to start a new download
|
||||||
|
val toAdd = download ?: runBlocking { Download.fromChapterId(chapterId) } ?: return
|
||||||
val queue = downloader.queue.toMutableList()
|
val queue = downloader.queue.toMutableList()
|
||||||
queue.remove(download)
|
download?.let { queue.remove(it) }
|
||||||
queue.add(0, download)
|
queue.add(0, toAdd)
|
||||||
reorderQueue(queue)
|
reorderQueue(queue)
|
||||||
if (isPaused()) {
|
if (isPaused()) {
|
||||||
if (DownloadService.isRunning(context)) {
|
if (DownloadService.isRunning(context)) {
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package eu.kanade.tachiyomi.data.download.model
|
package eu.kanade.tachiyomi.data.download.model
|
||||||
|
|
||||||
|
import eu.kanade.domain.chapter.interactor.GetChapter
|
||||||
|
import eu.kanade.domain.chapter.model.toDbChapter
|
||||||
|
import eu.kanade.domain.manga.interactor.GetMangaById
|
||||||
|
import eu.kanade.domain.manga.model.toDbManga
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
data class Download(
|
data class Download(
|
||||||
val source: HttpSource,
|
val source: HttpSource,
|
||||||
|
@ -57,4 +64,19 @@ data class Download(
|
||||||
DOWNLOADED(3),
|
DOWNLOADED(3),
|
||||||
ERROR(4),
|
ERROR(4),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
suspend fun fromChapterId(
|
||||||
|
chapterId: Long,
|
||||||
|
getChapter: GetChapter = Injekt.get(),
|
||||||
|
getMangaById: GetMangaById = Injekt.get(),
|
||||||
|
sourceManager: SourceManager = Injekt.get(),
|
||||||
|
): Download? {
|
||||||
|
val chapter = getChapter.await(chapterId) ?: return null
|
||||||
|
val manga = getMangaById.await(chapter.mangaId) ?: return null
|
||||||
|
val source = sourceManager.get(manga.source) as? HttpSource ?: return null
|
||||||
|
|
||||||
|
return Download(source, manga.toDbManga(), chapter.toDbChapter())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue