Misc cleanup

- Migrate sources filter screen to full compose
- Use standard "OK"/"Cancel" actions for delete category dialog
- Abstract some AppBar logic
- Remove some dead code
- Group related strings
This commit is contained in:
arkon 2022-07-18 18:32:25 -04:00
parent 00519e3b93
commit e2510c144a
13 changed files with 160 additions and 274 deletions

View File

@ -1,24 +1,27 @@
package eu.kanade.presentation.browse package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import eu.kanade.domain.source.model.Source import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.components.BaseSourceItem import eu.kanade.presentation.browse.components.BaseSourceItem
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.PreferenceRow import eu.kanade.presentation.components.PreferenceRow
import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.components.ScrollbarLazyColumn import eu.kanade.presentation.components.ScrollbarLazyColumn
import eu.kanade.presentation.util.plus
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel import eu.kanade.tachiyomi.ui.browse.source.FilterUiModel
import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterPresenter import eu.kanade.tachiyomi.ui.browse.source.SourcesFilterPresenter
@ -28,24 +31,34 @@ import kotlinx.coroutines.flow.collectLatest
@Composable @Composable
fun SourcesFilterScreen( fun SourcesFilterScreen(
nestedScrollInterop: NestedScrollConnection, navigateUp: () -> Unit,
presenter: SourcesFilterPresenter, presenter: SourcesFilterPresenter,
onClickLang: (String) -> Unit, onClickLang: (String) -> Unit,
onClickSource: (Source) -> Unit, onClickSource: (Source) -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
Scaffold(
modifier = Modifier.statusBarsPadding(),
topBar = {
AppBar(
title = stringResource(R.string.label_sources),
navigateUp = navigateUp,
)
},
) { paddingValues ->
when { when {
presenter.isLoading -> LoadingScreen() presenter.isLoading -> LoadingScreen()
presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen) presenter.isEmpty -> EmptyScreen(textResource = R.string.source_filter_empty_screen)
else -> { else -> {
SourcesFilterContent( SourcesFilterContent(
nestedScrollInterop = nestedScrollInterop, paddingValues = paddingValues,
state = presenter, state = presenter,
onClickLang = onClickLang, onClickLang = onClickLang,
onClickSource = onClickSource, onClickSource = onClickSource,
) )
} }
} }
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
presenter.events.collectLatest { event -> presenter.events.collectLatest { event ->
when (event) { when (event) {
@ -59,14 +72,13 @@ fun SourcesFilterScreen(
@Composable @Composable
fun SourcesFilterContent( fun SourcesFilterContent(
nestedScrollInterop: NestedScrollConnection, paddingValues: PaddingValues,
state: SourcesFilterState, state: SourcesFilterState,
onClickLang: (String) -> Unit, onClickLang: (String) -> Unit,
onClickSource: (Source) -> Unit, onClickSource: (Source) -> Unit,
) { ) {
ScrollbarLazyColumn( ScrollbarLazyColumn(
modifier = Modifier.nestedScroll(nestedScrollInterop), contentPadding = paddingValues + WindowInsets.navigationBars.asPaddingValues(),
contentPadding = WindowInsets.navigationBars.asPaddingValues(),
) { ) {
items( items(
items = state.items, items = state.items,

View File

@ -118,16 +118,16 @@ fun CategoryDeleteDialog(
AlertDialog( AlertDialog(
onDismissRequest = onDismissRequest, onDismissRequest = onDismissRequest,
confirmButton = { confirmButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.no))
}
},
dismissButton = {
TextButton(onClick = { TextButton(onClick = {
onDelete() onDelete()
onDismissRequest() onDismissRequest()
},) { },) {
Text(text = stringResource(R.string.yes)) Text(text = stringResource(android.R.string.ok))
}
},
dismissButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(android.R.string.cancel))
} }
}, },
title = { title = {

View File

@ -2,8 +2,14 @@ package eu.kanade.presentation.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@ -11,11 +17,16 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SmallTopAppBar import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -24,14 +35,44 @@ import eu.kanade.tachiyomi.R
@Composable @Composable
fun AppBar( fun AppBar(
modifier: Modifier = Modifier,
// Text
title: String?, title: String?,
subtitle: String? = null, subtitle: String? = null,
// Up button
navigateUp: (() -> Unit)? = null, navigateUp: (() -> Unit)? = null,
navigationIcon: ImageVector = Icons.Default.ArrowBack, navigationIcon: ImageVector = Icons.Default.ArrowBack,
// Menu
actions: @Composable RowScope.() -> Unit = {}, actions: @Composable RowScope.() -> Unit = {},
// Action mode
actionModeCounter: Int = 0,
onCancelActionMode: () -> Unit = {},
actionModeActions: @Composable RowScope.() -> Unit = {},
// Banners
downloadedOnlyMode: Boolean = false,
incognitoMode: Boolean = false,
) { ) {
val isActionMode by derivedStateOf { actionModeCounter > 0 }
val backgroundColor = if (isActionMode) {
TopAppBarDefaults.smallTopAppBarColors().containerColor(1f).value
} else {
MaterialTheme.colorScheme.surface
}
Column(
modifier = modifier.drawBehind { drawRect(backgroundColor) },
) {
SmallTopAppBar( SmallTopAppBar(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)),
navigationIcon = { navigationIcon = {
if (isActionMode) {
IconButton(onClick = onCancelActionMode) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(id = R.string.action_cancel),
)
}
} else {
navigateUp?.let { navigateUp?.let {
IconButton(onClick = it) { IconButton(onClick = it) {
Icon( Icon(
@ -40,13 +81,36 @@ fun AppBar(
) )
} }
} }
}
}, },
title = { title = {
if (isActionMode) {
AppBarTitle(actionModeCounter.toString())
} else {
AppBarTitle(title, subtitle) AppBarTitle(title, subtitle)
// TODO: incognito/downloaded only banners }
}, },
actions = actions, actions = {
if (isActionMode) {
actionModeActions()
} else {
actions()
}
},
// Background handled by parent
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent,
),
) )
if (downloadedOnlyMode) {
DownloadedOnlyModeBanner()
}
if (incognitoMode) {
IncognitoModeBanner()
}
}
} }
@Composable @Composable

View File

@ -60,9 +60,9 @@ import eu.kanade.presentation.components.VerticalFastScroller
import eu.kanade.presentation.manga.components.ChapterHeader import eu.kanade.presentation.manga.components.ChapterHeader
import eu.kanade.presentation.manga.components.ExpandableMangaDescription import eu.kanade.presentation.manga.components.ExpandableMangaDescription
import eu.kanade.presentation.manga.components.MangaActionRow import eu.kanade.presentation.manga.components.MangaActionRow
import eu.kanade.presentation.manga.components.MangaAppBar
import eu.kanade.presentation.manga.components.MangaChapterListItem import eu.kanade.presentation.manga.components.MangaChapterListItem
import eu.kanade.presentation.manga.components.MangaInfoBox import eu.kanade.presentation.manga.components.MangaInfoBox
import eu.kanade.presentation.manga.components.MangaSmallAppBar
import eu.kanade.presentation.util.isScrolledToEnd import eu.kanade.presentation.util.isScrolledToEnd
import eu.kanade.presentation.util.isScrollingUp import eu.kanade.presentation.util.isScrollingUp
import eu.kanade.presentation.util.plus import eu.kanade.presentation.util.plus
@ -237,7 +237,7 @@ private fun MangaScreenSmallImpl(
val animatedBgAlpha by animateFloatAsState( val animatedBgAlpha by animateFloatAsState(
if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f, if (firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0) 1f else 0f,
) )
MangaSmallAppBar( MangaAppBar(
title = state.manga.title, title = state.manga.title,
titleAlphaProvider = { animatedTitleAlpha }, titleAlphaProvider = { animatedTitleAlpha },
backgroundAlphaProvider = { animatedBgAlpha }, backgroundAlphaProvider = { animatedBgAlpha },
@ -458,7 +458,7 @@ fun MangaScreenLargeImpl(
Scaffold( Scaffold(
modifier = Modifier.padding(insetPadding), modifier = Modifier.padding(insetPadding),
topBar = { topBar = {
MangaSmallAppBar( MangaAppBar(
modifier = Modifier.onSizeChanged { onTopBarHeightChanged(it.height) }, modifier = Modifier.onSizeChanged { onTopBarHeightChanged(it.height) },
title = state.manga.title, title = state.manga.title,
titleAlphaProvider = { if (chapters.any { it.selected }) 1f else 0f }, titleAlphaProvider = { if (chapters.any { it.selected }) 1f else 0f },

View File

@ -38,7 +38,7 @@ import eu.kanade.presentation.manga.DownloadAction
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@Composable @Composable
fun MangaSmallAppBar( fun MangaAppBar(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
title: String, title: String,
titleAlphaProvider: () -> Float, titleAlphaProvider: () -> Float,
@ -57,7 +57,7 @@ fun MangaSmallAppBar(
) { ) {
val isActionMode = actionModeCounter > 0 val isActionMode = actionModeCounter > 0
val backgroundAlpha = if (isActionMode) 1f else backgroundAlphaProvider() val backgroundAlpha = if (isActionMode) 1f else backgroundAlphaProvider()
val backgroundColor by TopAppBarDefaults.centerAlignedTopAppBarColors().containerColor(1f) val backgroundColor by TopAppBarDefaults.smallTopAppBarColors().containerColor(1f)
Column( Column(
modifier = modifier.drawBehind { modifier = modifier.drawBehind {
drawRect(backgroundColor.copy(alpha = backgroundAlpha)) drawRect(backgroundColor.copy(alpha = backgroundAlpha))
@ -199,7 +199,7 @@ fun MangaSmallAppBar(
} }
}, },
// Background handled by parent // Background handled by parent
colors = TopAppBarDefaults.centerAlignedTopAppBarColors( colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Transparent, containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent, scrolledContainerColor = Color.Transparent,
), ),

View File

@ -1,7 +1,6 @@
package eu.kanade.presentation.updates package eu.kanade.presentation.updates
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
@ -12,35 +11,25 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.FlipToBack import androidx.compose.material.icons.filled.FlipToBack
import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.SelectAll import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SmallTopAppBar
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import com.google.accompanist.swiperefresh.SwipeRefresh import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.ChapterDownloadAction import eu.kanade.presentation.components.ChapterDownloadAction
import eu.kanade.presentation.components.DownloadedOnlyModeBanner
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.IncognitoModeBanner
import eu.kanade.presentation.components.MangaBottomActionMenu import eu.kanade.presentation.components.MangaBottomActionMenu
import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.components.Scaffold
import eu.kanade.presentation.components.SwipeRefreshIndicator import eu.kanade.presentation.components.SwipeRefreshIndicator
@ -201,37 +190,20 @@ fun UpdatesAppBar(
onSelectAll: () -> Unit, onSelectAll: () -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
val isActionMode = actionModeCounter > 0 AppBar(
val backgroundColor = if (isActionMode) { modifier = modifier,
TopAppBarDefaults.centerAlignedTopAppBarColors().containerColor(1f).value title = stringResource(R.string.label_recent_updates),
} else {
MaterialTheme.colorScheme.surface
}
Column(
modifier = modifier.drawBehind { drawRect(backgroundColor) },
) {
SmallTopAppBar(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top)),
navigationIcon = {
if (isActionMode) {
IconButton(onClick = { selected.clear() }) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = stringResource(id = R.string.action_cancel),
)
}
}
},
title = {
Text(
text = if (isActionMode) actionModeCounter.toString() else stringResource(R.string.label_recent_updates),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
actions = { actions = {
if (isActionMode) { IconButton(onClick = onUpdateLibrary) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = stringResource(R.string.action_update_library),
)
}
},
actionModeCounter = actionModeCounter,
onCancelActionMode = { selected.clear() },
actionModeActions = {
IconButton(onClick = onSelectAll) { IconButton(onClick = onSelectAll) {
Icon( Icon(
imageVector = Icons.Default.SelectAll, imageVector = Icons.Default.SelectAll,
@ -244,29 +216,10 @@ fun UpdatesAppBar(
contentDescription = stringResource(R.string.action_select_inverse), contentDescription = stringResource(R.string.action_select_inverse),
) )
} }
} else {
IconButton(onClick = onUpdateLibrary) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = stringResource(R.string.action_update_library),
)
}
}
}, },
// Background handled by parent downloadedOnlyMode = downloadedOnlyMode,
colors = TopAppBarDefaults.centerAlignedTopAppBarColors( incognitoMode = incognitoMode,
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent,
),
) )
if (downloadedOnlyMode) {
DownloadedOnlyModeBanner()
}
if (incognitoMode) {
IncognitoModeBanner()
}
}
} }
@Composable @Composable

View File

@ -1,22 +1,18 @@
package eu.kanade.tachiyomi.ui.browse.source package eu.kanade.tachiyomi.ui.browse.source
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import eu.kanade.domain.source.model.Source import eu.kanade.domain.source.model.Source
import eu.kanade.presentation.browse.SourcesFilterScreen import eu.kanade.presentation.browse.SourcesFilterScreen
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
import eu.kanade.tachiyomi.ui.base.controller.ComposeController
class SourceFilterController : ComposeController<SourcesFilterPresenter>() { class SourceFilterController : FullComposeController<SourcesFilterPresenter>() {
override fun getTitle() = resources?.getString(R.string.label_sources)
override fun createPresenter(): SourcesFilterPresenter = SourcesFilterPresenter() override fun createPresenter(): SourcesFilterPresenter = SourcesFilterPresenter()
@Composable @Composable
override fun ComposeContent(nestedScrollInterop: NestedScrollConnection) { override fun ComposeContent() {
SourcesFilterScreen( SourcesFilterScreen(
nestedScrollInterop = nestedScrollInterop, navigateUp = router::popCurrentController,
presenter = presenter, presenter = presenter,
onClickLang = { language -> onClickLang = { language ->
presenter.toggleLanguage(language) presenter.toggleLanguage(language)

View File

@ -1,41 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.chapter
import android.content.Context
import android.util.AttributeSet
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.AbstractComposeView
import eu.kanade.presentation.components.ChapterDownloadAction
import eu.kanade.presentation.components.ChapterDownloadIndicator
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.data.download.model.Download
class ChapterDownloadView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0,
) : AbstractComposeView(context, attrs, defStyle) {
private var state by mutableStateOf(Download.State.NOT_DOWNLOADED)
private var progress by mutableStateOf(0)
var listener: (ChapterDownloadAction) -> Unit = {}
@Composable
override fun Content() {
TachiyomiTheme {
ChapterDownloadIndicator(
downloadStateProvider = { state },
downloadProgressProvider = { progress },
onClick = listener,
)
}
}
fun setState(state: Download.State, progress: Int = 0) {
this.state = state
this.progress = progress
}
}

View File

@ -1,21 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.chapter.base
import android.view.View
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.presentation.components.ChapterDownloadAction
open class BaseChapterHolder(
view: View,
private val adapter: BaseChaptersAdapter<*>,
) : FlexibleViewHolder(view, adapter) {
val downloadActionListener: (ChapterDownloadAction) -> Unit = { action ->
when (action) {
ChapterDownloadAction.START -> adapter.clickListener.downloadChapter(bindingAdapterPosition)
ChapterDownloadAction.START_NOW -> adapter.clickListener.startDownloadNow(bindingAdapterPosition)
ChapterDownloadAction.CANCEL, ChapterDownloadAction.DELETE -> {
adapter.clickListener.deleteChapter(bindingAdapterPosition)
}
}
}
}

View File

@ -1,47 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.chapter.base
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.AbstractSectionableItem
import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.source.model.Page
abstract class BaseChapterItem<T : BaseChapterHolder, H : AbstractHeaderItem<*>>(
val chapter: Chapter,
header: H? = null,
) : AbstractSectionableItem<T, H?>(header) {
private var _status: Download.State = Download.State.NOT_DOWNLOADED
var status: Download.State
get() = download?.status ?: _status
set(value) {
_status = value
}
val progress: Int
get() {
val pages = download?.pages ?: return 0
return pages.map(Page::progress).average().toInt()
}
@Transient
var download: Download? = null
val isDownloaded: Boolean
get() = status == Download.State.DOWNLOADED
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is BaseChapterItem<*, *>) {
return chapter.id == other.chapter.id && chapter.read == other.chapter.read
}
return false
}
override fun hashCode(): Int {
var result = chapter.id.hashCode()
result = 31 * result + chapter.read.hashCode()
return result
}
}

View File

@ -1,24 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.chapter.base
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
abstract class BaseChaptersAdapter<T : IFlexible<*>>(
controller: OnChapterClickListener,
items: List<T>? = null,
) : FlexibleAdapter<T>(items, controller, true) {
/**
* Listener for browse item clicks.
*/
val clickListener: OnChapterClickListener = controller
/**
* Listener which should be called when user clicks the download icons.
*/
interface OnChapterClickListener {
fun downloadChapter(position: Int)
fun deleteChapter(position: Int)
fun startDownloadNow(position: Int)
}
}

View File

@ -24,7 +24,6 @@
android:layout_height="95dp" android:layout_height="95dp"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:contentDescription="@string/description_cover"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View File

@ -72,6 +72,8 @@
<string name="action_edit_categories">Edit categories</string> <string name="action_edit_categories">Edit categories</string>
<string name="action_rename_category">Rename category</string> <string name="action_rename_category">Rename category</string>
<string name="action_move_category">Set categories</string> <string name="action_move_category">Set categories</string>
<string name="delete_category_confirmation">Do you wish to delete the category \"%s\"?</string>
<string name="delete_category">Delete category</string>
<string name="action_edit_cover">Edit cover</string> <string name="action_edit_cover">Edit cover</string>
<string name="action_view_chapters">View chapters</string> <string name="action_view_chapters">View chapters</string>
<string name="action_stop">Stop</string> <string name="action_stop">Stop</string>
@ -135,6 +137,7 @@
<!-- Operations --> <!-- Operations -->
<string name="loading">Loading…</string> <string name="loading">Loading…</string>
<string name="internal_error">InternalError: Check crash logs for further information</string>
<!-- Shortcuts--> <!-- Shortcuts-->
<string name="app_not_available">App not available</string> <string name="app_not_available">App not available</string>
@ -357,6 +360,8 @@
<string name="scale_type_fit_height">Fit height</string> <string name="scale_type_fit_height">Fit height</string>
<string name="scale_type_original_size">Original size</string> <string name="scale_type_original_size">Original size</string>
<string name="scale_type_smart_fit">Smart fit</string> <string name="scale_type_smart_fit">Smart fit</string>
<string name="pref_navigate_pan">Navigate to pan</string>
<string name="pref_landscape_zoom">Zoom landscape image</string>
<string name="pref_zoom_start">Zoom start position</string> <string name="pref_zoom_start">Zoom start position</string>
<string name="zoom_start_automatic">Automatic</string> <string name="zoom_start_automatic">Automatic</string>
<string name="zoom_start_left">Left</string> <string name="zoom_start_left">Left</string>
@ -722,6 +727,7 @@
<!-- Updates fragment --> <!-- Updates fragment -->
<string name="updating_library">Updating library</string> <string name="updating_library">Updating library</string>
<string name="cant_open_last_read_chapter">Unable to open last read chapter</string>
<!-- History fragment --> <!-- History fragment -->
<string name="recent_manga_time">Ch. %1$s - %2$s</string> <string name="recent_manga_time">Ch. %1$s - %2$s</string>
@ -804,9 +810,6 @@
<item quantity="other">%d extension updates available</item> <item quantity="other">%d extension updates available</item>
</plurals> </plurals>
<!--Content Description-->
<string name="description_cover">Cover of manga</string>
<!-- Information Text --> <!-- Information Text -->
<string name="information_no_downloads">No downloads</string> <string name="information_no_downloads">No downloads</string>
<string name="information_no_recent">No recent updates</string> <string name="information_no_recent">No recent updates</string>
@ -848,12 +851,4 @@
<!-- S Pen actions --> <!-- S Pen actions -->
<string name="spen_previous_page">Previous page</string> <string name="spen_previous_page">Previous page</string>
<string name="spen_next_page">Next page</string> <string name="spen_next_page">Next page</string>
<string name="pref_navigate_pan">Navigate to pan</string>
<string name="pref_landscape_zoom">Zoom landscape image</string>
<string name="cant_open_last_read_chapter">Unable to open last read chapter</string>
<string name="delete_category_confirmation">Do you wish to delete the category %s</string>
<string name="delete_category">Delete category</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="internal_error">InternalError: Check crash logs for further information</string>
</resources> </resources>