thank you android studio, very cool

This commit is contained in:
Secozzi 2024-06-06 00:31:35 +02:00
parent f72df3f620
commit 2a875f6c86
No known key found for this signature in database
GPG key ID: 71E9C97D8DDC2662
2 changed files with 342 additions and 0 deletions

View file

@ -0,0 +1,260 @@
package eu.kanade.presentation.track.anime
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewLightDark
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.StringResource
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
import eu.kanade.presentation.track.components.TrackLogoIcon
import eu.kanade.presentation.track.manga.TrackDetailsItem
import eu.kanade.presentation.track.manga.TrackInfoItemMenu
import eu.kanade.tachiyomi.data.track.Tracker
import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem
import eu.kanade.tachiyomi.util.lang.toLocalDate
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import java.time.format.DateTimeFormatter
private const val UnsetStatusTextAlpha = 0.5F
@Composable
fun AnimeTrackInfoDialogHome(
trackItems: List<AnimeTrackItem>,
dateFormat: DateTimeFormatter,
onStatusClick: (AnimeTrackItem) -> Unit,
onEpisodeClick: (AnimeTrackItem) -> Unit,
onScoreClick: (AnimeTrackItem) -> Unit,
onStartDateEdit: (AnimeTrackItem) -> Unit,
onEndDateEdit: (AnimeTrackItem) -> Unit,
onNewSearch: (AnimeTrackItem) -> Unit,
onOpenInBrowser: (AnimeTrackItem) -> Unit,
onRemoved: (AnimeTrackItem) -> Unit,
) {
Column(
modifier = Modifier
.animateContentSize()
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(16.dp)
.windowInsetsPadding(WindowInsets.systemBars),
verticalArrangement = Arrangement.spacedBy(24.dp),
) {
trackItems.forEach { item ->
if (item.track != null) {
val supportsScoring = item.tracker.animeService.getScoreList().isNotEmpty()
val supportsReadingDates = item.tracker.supportsReadingDates
TrackInfoItem(
title = item.track.title,
tracker = item.tracker,
status = item.tracker.getStatus(item.track.status),
onStatusClick = { onStatusClick(item) },
episodes = "${item.track.lastEpisodeSeen.toInt()}".let {
val totalEpisodes = item.track.totalEpisodes
if (totalEpisodes > 0) {
// Add known total episode count
"$it / $totalEpisodes"
} else {
it
}
},
onEpisodesClick = { onEpisodeClick(item) },
score = item.tracker.animeService.displayScore(item.track)
.takeIf { supportsScoring && item.track.score != 0.0 },
onScoreClick = { onScoreClick(item) }
.takeIf { supportsScoring },
startDate = remember(item.track.startDate) {
dateFormat.format(
item.track.startDate.toLocalDate(),
)
}
.takeIf { supportsReadingDates && item.track.startDate != 0L },
onStartDateClick = { onStartDateEdit(item) } // TODO
.takeIf { supportsReadingDates },
endDate = dateFormat.format(item.track.finishDate.toLocalDate())
.takeIf { supportsReadingDates && item.track.finishDate != 0L },
onEndDateClick = { onEndDateEdit(item) }
.takeIf { supportsReadingDates },
onNewSearch = { onNewSearch(item) },
onOpenInBrowser = { onOpenInBrowser(item) },
onRemoved = { onRemoved(item) },
)
} else {
TrackInfoItemEmpty(
tracker = item.tracker,
onNewSearch = { onNewSearch(item) },
)
}
}
}
}
@Composable
private fun TrackInfoItem(
title: String,
tracker: Tracker,
status: StringResource?,
onStatusClick: () -> Unit,
episodes: String,
onEpisodesClick: () -> Unit,
score: String?,
onScoreClick: (() -> Unit)?,
startDate: String?,
onStartDateClick: (() -> Unit)?,
endDate: String?,
onEndDateClick: (() -> Unit)?,
onNewSearch: () -> Unit,
onOpenInBrowser: () -> Unit,
onRemoved: () -> Unit,
) {
val context = LocalContext.current
Column {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
TrackLogoIcon(
tracker = tracker,
onClick = onOpenInBrowser,
)
Box(
modifier = Modifier
.height(48.dp)
.weight(1f)
.combinedClickable(
onClick = onNewSearch,
onLongClick = {
context.copyToClipboard(title, title)
},
)
.padding(start = 16.dp),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface,
)
}
VerticalDivider()
TrackInfoItemMenu(
onOpenInBrowser = onOpenInBrowser,
onRemoved = onRemoved,
)
}
Box(
modifier = Modifier
.padding(top = 12.dp)
.clip(MaterialTheme.shapes.medium)
.background(MaterialTheme.colorScheme.surface)
.padding(8.dp)
.clip(RoundedCornerShape(6.dp)),
) {
Column {
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
TrackDetailsItem(
modifier = Modifier.weight(1f),
text = status?.let { stringResource(it) } ?: "",
onClick = onStatusClick,
)
VerticalDivider()
TrackDetailsItem(
modifier = Modifier.weight(1f),
text = episodes,
onClick = onEpisodesClick,
)
if (onScoreClick != null) {
VerticalDivider()
TrackDetailsItem(
modifier = Modifier
.weight(1f)
.alpha(if (score == null) UnsetStatusTextAlpha else 1f),
text = score ?: stringResource(MR.strings.score),
onClick = onScoreClick,
)
}
}
if (onStartDateClick != null && onEndDateClick != null) {
HorizontalDivider()
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
TrackDetailsItem(
modifier = Modifier.weight(1F),
text = startDate,
placeholder = stringResource(MR.strings.track_started_reading_date),
onClick = onStartDateClick,
)
VerticalDivider()
TrackDetailsItem(
modifier = Modifier.weight(1F),
text = endDate,
placeholder = stringResource(MR.strings.track_finished_reading_date),
onClick = onEndDateClick,
)
}
}
}
}
}
}
@Composable
private fun TrackInfoItemEmpty(
tracker: Tracker,
onNewSearch: () -> Unit,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
TrackLogoIcon(tracker)
TextButton(
onClick = onNewSearch,
modifier = Modifier
.padding(start = 16.dp)
.weight(1f),
) {
Text(text = stringResource(MR.strings.add_tracking))
}
}
}
@PreviewLightDark
@Composable
private fun TrackInfoDialogHomePreviews(
@PreviewParameter(AnimeTrackInfoDialogHomePreviewProvider::class)
content: @Composable () -> Unit,
) {
TachiyomiPreviewTheme { content() }
}

View file

@ -0,0 +1,82 @@
package eu.kanade.presentation.track.anime
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import eu.kanade.tachiyomi.ui.entries.anime.track.AnimeTrackItem
import eu.kanade.test.DummyTracker
import tachiyomi.domain.track.anime.model.AnimeTrack
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
internal class AnimeTrackInfoDialogHomePreviewProvider :
PreviewParameterProvider<@Composable () -> Unit> {
private val aTrack = AnimeTrack(
id = 1L,
animeId = 2L,
trackerId = 3L,
remoteId = 4L,
libraryId = null,
title = "Manage Name On Tracker Site",
lastEpisodeSeen = 2.0,
totalEpisodes = 12L,
status = 1L,
score = 2.0,
remoteUrl = "https://example.com",
startDate = 0L,
finishDate = 0L,
)
private val trackItemWithoutTrack = AnimeTrackItem(
track = null,
tracker = DummyTracker(
id = 1L,
name = "Example Tracker",
),
)
private val trackItemWithTrack = AnimeTrackItem(
track = aTrack,
tracker = DummyTracker(
id = 2L,
name = "Example Tracker 2",
),
)
private val trackersWithAndWithoutTrack = @Composable {
AnimeTrackInfoDialogHome(
trackItems = listOf(
trackItemWithoutTrack,
trackItemWithTrack,
),
dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
onStatusClick = {},
onEpisodeClick = {},
onScoreClick = {},
onStartDateEdit = {},
onEndDateEdit = {},
onNewSearch = {},
onOpenInBrowser = {},
onRemoved = {},
)
}
private val noTrackers = @Composable {
AnimeTrackInfoDialogHome(
trackItems = listOf(),
dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
onStatusClick = {},
onEpisodeClick = {},
onScoreClick = {},
onStartDateEdit = {},
onEndDateEdit = {},
onNewSearch = {},
onOpenInBrowser = {},
onRemoved = {},
)
}
override val values: Sequence<@Composable () -> Unit>
get() = sequenceOf(
trackersWithAndWithoutTrack,
noTrackers,
)
}