mirror of
https://github.com/mihonapp/mihon.git
synced 2025-08-19 21:11:31 +02:00
Compare commits
18 Commits
574887aded
...
57e6bf1d13
Author | SHA1 | Date | |
---|---|---|---|
|
57e6bf1d13 | ||
|
840b647b4b | ||
|
1b0bbb8440 | ||
|
95d4df9ca8 | ||
|
fb86c470f6 | ||
|
5aec8f8018 | ||
|
e183cbb231 | ||
|
6bdb37be65 | ||
|
7ff95e21ba | ||
|
96c236e5c3 | ||
|
72f3756a3b | ||
|
0780385d2e | ||
|
31e9273b1f | ||
|
088e37b2d8 | ||
|
5b88f1bd94 | ||
|
18beb20aac | ||
|
d5d7065e75 | ||
|
78271a54a4 |
1
.github/renovate.json5
vendored
1
.github/renovate.json5
vendored
@@ -4,6 +4,7 @@
|
||||
"config:base"
|
||||
],
|
||||
"schedule": ["every sunday"],
|
||||
"labels": ["Dependencies"],
|
||||
"packageRules": [
|
||||
{
|
||||
// Compiler plugins are tightly coupled to Kotlin version
|
||||
|
41
README.md
41
README.md
@@ -44,40 +44,13 @@ Discover and read manga, webtoons, comics, and more – easier than ever on your
|
||||
|
||||
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
||||
|
||||
If you got any questions, [join our Discord server](https://discord.gg/mihon).
|
||||
Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues); if you got any questions, join our [Discord server](https://discord.gg/mihon).
|
||||
|
||||
<details align="center"><summary>Issues</summary><div align="left">
|
||||
|
||||
Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues).
|
||||
|
||||
</div></details>
|
||||
|
||||
<details align="center"><summary>Bugs</summary><div align="left">
|
||||
|
||||
* Include version (**More → About → Version**).
|
||||
* If not latest, try updating, it may have already been solved.
|
||||
* Beta version is equal to the number of commits as seen on the main page.
|
||||
* Include steps to reproduce (if not obvious from description).
|
||||
* Include screenshot (if needed).
|
||||
* If it could be device-dependent, try reproducing on another device (if possible).
|
||||
* Don't group unrelated requests into one issue
|
||||
- **DO:** [#24](https://git.mihon.dev/tachiyomi/tachiyomi/issues/24), [#71](https://git.mihon.dev/tachiyomi/tachiyomi/issues/71)
|
||||
- **DON'T:** [#75](https://git.mihon.dev/tachiyomi/tachiyomi/issues/75)
|
||||
|
||||
</div></details>
|
||||
|
||||
<details align="center"><summary>Feature requests</summary><div align="left">
|
||||
|
||||
* Write a detailed issue, explaining what it should do or how.
|
||||
* Avoid writing just "like X app does";
|
||||
* Include screenshot (if needed)
|
||||
* Source requests are not accepted.
|
||||
|
||||
</div></details>
|
||||
|
||||
### Repositories
|
||||
|
||||
[](https://github.com/mihonapp/website/)
|
||||
[](https://github.com/mihonapp/bitmap.kt/)
|
||||
|
||||
### Credits
|
||||
|
||||
@@ -93,8 +66,10 @@ The developer(s) of this application does not have any affiliation with the cont
|
||||
|
||||
### License
|
||||
|
||||
```
|
||||
<pre>
|
||||
Copyright © 2015 Javier Tomás
|
||||
Copyright © 2024 The Mihon 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
|
||||
@@ -106,8 +81,6 @@ 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.
|
||||
</pre>
|
||||
|
||||
Modifications Copyright © 2024 The Mihon Open Source Project
|
||||
```
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -7,8 +7,8 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.preference.getEnum
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.Locale
|
||||
|
||||
class UiPreferences(
|
||||
@@ -31,9 +31,9 @@ class UiPreferences(
|
||||
fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC)
|
||||
|
||||
companion object {
|
||||
fun dateFormat(format: String): DateFormat = when (format) {
|
||||
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
else -> SimpleDateFormat(format, Locale.getDefault())
|
||||
fun dateFormat(format: String): DateTimeFormatter = when (format) {
|
||||
"" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
|
||||
else -> DateTimeFormatter.ofPattern(format, Locale.getDefault())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -9,20 +9,26 @@ import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
|
||||
@Composable
|
||||
fun relativeDateText(
|
||||
dateEpochMillis: Long,
|
||||
): String {
|
||||
return relativeDateText(
|
||||
date = Date(dateEpochMillis).takeIf { dateEpochMillis > 0L },
|
||||
localDate = LocalDate.ofInstant(
|
||||
Instant.ofEpochMilli(dateEpochMillis),
|
||||
ZoneId.systemDefault(),
|
||||
)
|
||||
.takeIf { dateEpochMillis > 0L },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun relativeDateText(
|
||||
date: Date?,
|
||||
localDate: LocalDate?,
|
||||
): String {
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -30,11 +36,10 @@ fun relativeDateText(
|
||||
val relativeTime = remember { preferences.relativeTime().get() }
|
||||
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
||||
|
||||
return date
|
||||
?.toRelativeString(
|
||||
context = context,
|
||||
relative = relativeTime,
|
||||
dateFormat = dateFormat,
|
||||
)
|
||||
return localDate?.toRelativeString(
|
||||
context = context,
|
||||
relative = relativeTime,
|
||||
dateFormat = dateFormat,
|
||||
)
|
||||
?: stringResource(MR.strings.not_applicable)
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import java.util.Date
|
||||
import java.time.LocalDate
|
||||
|
||||
@Composable
|
||||
fun HistoryScreen(
|
||||
@@ -133,7 +133,7 @@ private fun HistoryScreenContent(
|
||||
}
|
||||
|
||||
sealed interface HistoryUiModel {
|
||||
data class Header(val date: Date) : HistoryUiModel
|
||||
data class Header(val date: LocalDate) : HistoryUiModel
|
||||
data class Item(val item: HistoryWithRelations) : HistoryUiModel
|
||||
}
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
import tachiyomi.domain.manga.model.MangaCover
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.Date
|
||||
import kotlin.random.Random
|
||||
@@ -71,10 +72,10 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenMo
|
||||
private object HistoryUiModelExamples {
|
||||
val headerToday = header()
|
||||
val headerTomorrow =
|
||||
HistoryUiModel.Header(Date.from(Instant.now().plus(1, ChronoUnit.DAYS)))
|
||||
HistoryUiModel.Header(LocalDate.now().plusDays(1))
|
||||
|
||||
fun header(instantBuilder: (Instant) -> Instant = { it }) =
|
||||
HistoryUiModel.Header(Date.from(instantBuilder(Instant.now())))
|
||||
HistoryUiModel.Header(LocalDate.from(instantBuilder(Instant.now())))
|
||||
|
||||
fun items() = sequence {
|
||||
var count = 1
|
||||
|
@@ -26,7 +26,7 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
|
||||
object SettingsAppearanceScreen : SearchableSettings {
|
||||
|
||||
@@ -101,7 +101,7 @@ object SettingsAppearanceScreen : SearchableSettings {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
|
||||
val now = remember { Instant.now().toEpochMilli() }
|
||||
val now = remember { LocalDate.now() }
|
||||
|
||||
val dateFormat by uiPreferences.dateFormat().collectAsState()
|
||||
val formattedNow = remember(dateFormat) {
|
||||
|
@@ -55,10 +55,10 @@ import tachiyomi.presentation.core.icons.Reddit
|
||||
import tachiyomi.presentation.core.icons.X
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
object AboutScreen : Screen() {
|
||||
|
||||
@@ -269,16 +269,9 @@ object AboutScreen : Screen() {
|
||||
|
||||
internal fun getFormattedBuildTime(): String {
|
||||
return try {
|
||||
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)
|
||||
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val buildTime = inputDf.parse(BuildConfig.BUILD_TIME)
|
||||
|
||||
val outputDf = DateFormat.getDateTimeInstance(
|
||||
DateFormat.MEDIUM,
|
||||
DateFormat.SHORT,
|
||||
Locale.getDefault(),
|
||||
)
|
||||
outputDf.timeZone = TimeZone.getDefault()
|
||||
val df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)
|
||||
.withZone(ZoneId.of("UTC"))
|
||||
val buildTime = LocalDateTime.from(df.parse(BuildConfig.BUILD_TIME))
|
||||
|
||||
buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
|
||||
} catch (e: Exception) {
|
||||
|
@@ -42,7 +42,9 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.plus
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
|
||||
class WorkerInfoScreen : Screen() {
|
||||
|
||||
@@ -148,13 +150,16 @@ class WorkerInfoScreen : Screen() {
|
||||
}
|
||||
appendLine("State: ${workInfo.state}")
|
||||
if (workInfo.state == WorkInfo.State.ENQUEUED) {
|
||||
appendLine(
|
||||
"Next scheduled run: ${Date(workInfo.nextScheduleTimeMillis).toDateTimestampString(
|
||||
val timestamp = LocalDateTime.ofInstant(
|
||||
Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis),
|
||||
ZoneId.systemDefault(),
|
||||
)
|
||||
.toDateTimestampString(
|
||||
UiPreferences.dateFormat(
|
||||
Injekt.get<UiPreferences>().dateFormat().get(),
|
||||
),
|
||||
)}",
|
||||
)
|
||||
)
|
||||
appendLine("Next scheduled run: $timestamp",)
|
||||
appendLine("Attempt #${workInfo.runAttemptCount + 1}")
|
||||
}
|
||||
appendLine()
|
||||
|
@@ -8,6 +8,7 @@ import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.AppTheme
|
||||
import eu.kanade.presentation.theme.colorscheme.BaseColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.GreenAppleColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.LavenderColorScheme
|
||||
import eu.kanade.presentation.theme.colorscheme.MidnightDuskColorScheme
|
||||
@@ -62,23 +63,27 @@ private fun getThemeColorScheme(
|
||||
appTheme: AppTheme,
|
||||
isAmoled: Boolean,
|
||||
): ColorScheme {
|
||||
val colorScheme = when (appTheme) {
|
||||
AppTheme.DEFAULT -> TachiyomiColorScheme
|
||||
AppTheme.MONET -> MonetColorScheme(LocalContext.current)
|
||||
AppTheme.GREEN_APPLE -> GreenAppleColorScheme
|
||||
AppTheme.LAVENDER -> LavenderColorScheme
|
||||
AppTheme.MIDNIGHT_DUSK -> MidnightDuskColorScheme
|
||||
AppTheme.NORD -> NordColorScheme
|
||||
AppTheme.STRAWBERRY_DAIQUIRI -> StrawberryColorScheme
|
||||
AppTheme.TAKO -> TakoColorScheme
|
||||
AppTheme.TEALTURQUOISE -> TealTurqoiseColorScheme
|
||||
AppTheme.TIDAL_WAVE -> TidalWaveColorScheme
|
||||
AppTheme.YINYANG -> YinYangColorScheme
|
||||
AppTheme.YOTSUBA -> YotsubaColorScheme
|
||||
else -> TachiyomiColorScheme
|
||||
val colorScheme = if (appTheme == AppTheme.MONET) {
|
||||
MonetColorScheme(LocalContext.current)
|
||||
} else {
|
||||
colorSchemes.getOrDefault(appTheme, TachiyomiColorScheme)
|
||||
}
|
||||
return colorScheme.getColorScheme(
|
||||
isSystemInDarkTheme(),
|
||||
isAmoled,
|
||||
)
|
||||
}
|
||||
|
||||
private val colorSchemes: Map<AppTheme, BaseColorScheme> = mapOf(
|
||||
AppTheme.DEFAULT to TachiyomiColorScheme,
|
||||
AppTheme.GREEN_APPLE to GreenAppleColorScheme,
|
||||
AppTheme.LAVENDER to LavenderColorScheme,
|
||||
AppTheme.MIDNIGHT_DUSK to MidnightDuskColorScheme,
|
||||
AppTheme.NORD to NordColorScheme,
|
||||
AppTheme.STRAWBERRY_DAIQUIRI to StrawberryColorScheme,
|
||||
AppTheme.TAKO to TakoColorScheme,
|
||||
AppTheme.TEALTURQUOISE to TealTurqoiseColorScheme,
|
||||
AppTheme.TIDAL_WAVE to TidalWaveColorScheme,
|
||||
AppTheme.YINYANG to YinYangColorScheme,
|
||||
AppTheme.YOTSUBA to YotsubaColorScheme,
|
||||
)
|
||||
|
@@ -9,18 +9,15 @@ internal abstract class BaseColorScheme {
|
||||
abstract val lightScheme: ColorScheme
|
||||
|
||||
fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme {
|
||||
return (if (isDark) darkScheme else lightScheme)
|
||||
.let {
|
||||
if (isDark && isAmoled) {
|
||||
it.copy(
|
||||
background = Color.Black,
|
||||
onBackground = Color.White,
|
||||
surface = Color.Black,
|
||||
onSurface = Color.White,
|
||||
)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
if (!isDark) return lightScheme
|
||||
|
||||
if (!isAmoled) return darkScheme
|
||||
|
||||
return darkScheme.copy(
|
||||
background = Color.Black,
|
||||
onBackground = Color.White,
|
||||
surface = Color.Black,
|
||||
onSurface = Color.White,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@@ -52,17 +52,18 @@ import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.presentation.track.components.TrackLogoIcon
|
||||
import eu.kanade.tachiyomi.data.track.Tracker
|
||||
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
|
||||
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.text.DateFormat
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
private const val UnsetStatusTextAlpha = 0.5F
|
||||
|
||||
@Composable
|
||||
fun TrackInfoDialogHome(
|
||||
trackItems: List<TrackItem>,
|
||||
dateFormat: DateFormat,
|
||||
dateFormat: DateTimeFormatter,
|
||||
onStatusClick: (TrackItem) -> Unit,
|
||||
onChapterClick: (TrackItem) -> Unit,
|
||||
onScoreClick: (TrackItem) -> Unit,
|
||||
@@ -104,11 +105,11 @@ fun TrackInfoDialogHome(
|
||||
.takeIf { supportsScoring && item.track.score != 0.0 },
|
||||
onScoreClick = { onScoreClick(item) }
|
||||
.takeIf { supportsScoring },
|
||||
startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate) }
|
||||
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)
|
||||
endDate = dateFormat.format(item.track.finishDate.toLocalDate())
|
||||
.takeIf { supportsReadingDates && item.track.finishDate != 0L },
|
||||
onEndDateClick = { onEndDateEdit(item) }
|
||||
.takeIf { supportsReadingDates },
|
||||
|
@@ -5,7 +5,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
|
||||
import eu.kanade.test.DummyTracker
|
||||
import tachiyomi.domain.track.model.Track
|
||||
import java.text.DateFormat
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
|
||||
internal class TrackInfoDialogHomePreviewProvider :
|
||||
PreviewParameterProvider<@Composable () -> Unit> {
|
||||
@@ -46,7 +47,7 @@ internal class TrackInfoDialogHomePreviewProvider :
|
||||
trackItemWithoutTrack,
|
||||
trackItemWithTrack,
|
||||
),
|
||||
dateFormat = DateFormat.getDateInstance(),
|
||||
dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
|
||||
onStatusClick = {},
|
||||
onChapterClick = {},
|
||||
onScoreClick = {},
|
||||
@@ -61,7 +62,7 @@ internal class TrackInfoDialogHomePreviewProvider :
|
||||
private val noTrackers = @Composable {
|
||||
TrackInfoDialogHome(
|
||||
trackItems = listOf(),
|
||||
dateFormat = DateFormat.getDateInstance(),
|
||||
dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM),
|
||||
onStatusClick = {},
|
||||
onChapterClick = {},
|
||||
onScoreClick = {},
|
||||
|
@@ -36,7 +36,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import java.util.Date
|
||||
import java.time.LocalDate
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Composable
|
||||
@@ -206,6 +206,6 @@ private fun UpdatesBottomBar(
|
||||
}
|
||||
|
||||
sealed interface UpdatesUiModel {
|
||||
data class Header(val date: Date) : UpdatesUiModel
|
||||
data class Header(val date: LocalDate) : UpdatesUiModel
|
||||
data class Item(val item: UpdatesItem) : UpdatesUiModel
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package eu.kanade.tachiyomi.ui.base.delegate
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
@@ -106,7 +108,12 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
|
||||
if (activity.isAuthenticationSupported()) {
|
||||
if (!SecureActivityDelegate.requireUnlock) return
|
||||
activity.startActivity(Intent(activity, UnlockActivity::class.java))
|
||||
activity.overridePendingTransition(0, 0)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
activity.overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN, 0, 0)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
activity.overridePendingTransition(0, 0)
|
||||
}
|
||||
} else {
|
||||
securityPreferences.useAuthenticator().set(false)
|
||||
}
|
||||
|
@@ -12,51 +12,10 @@ interface ThemingDelegate {
|
||||
|
||||
companion object {
|
||||
fun getThemeResIds(appTheme: AppTheme, isAmoled: Boolean): List<Int> {
|
||||
val resIds = mutableListOf<Int>()
|
||||
when (appTheme) {
|
||||
AppTheme.MONET -> {
|
||||
resIds += R.style.Theme_Tachiyomi_Monet
|
||||
}
|
||||
AppTheme.GREEN_APPLE -> {
|
||||
resIds += R.style.Theme_Tachiyomi_GreenApple
|
||||
}
|
||||
AppTheme.LAVENDER -> {
|
||||
resIds += R.style.Theme_Tachiyomi_Lavender
|
||||
}
|
||||
AppTheme.MIDNIGHT_DUSK -> {
|
||||
resIds += R.style.Theme_Tachiyomi_MidnightDusk
|
||||
}
|
||||
AppTheme.NORD -> {
|
||||
resIds += R.style.Theme_Tachiyomi_Nord
|
||||
}
|
||||
AppTheme.STRAWBERRY_DAIQUIRI -> {
|
||||
resIds += R.style.Theme_Tachiyomi_StrawberryDaiquiri
|
||||
}
|
||||
AppTheme.TAKO -> {
|
||||
resIds += R.style.Theme_Tachiyomi_Tako
|
||||
}
|
||||
AppTheme.TEALTURQUOISE -> {
|
||||
resIds += R.style.Theme_Tachiyomi_TealTurquoise
|
||||
}
|
||||
AppTheme.YINYANG -> {
|
||||
resIds += R.style.Theme_Tachiyomi_YinYang
|
||||
}
|
||||
AppTheme.YOTSUBA -> {
|
||||
resIds += R.style.Theme_Tachiyomi_Yotsuba
|
||||
}
|
||||
AppTheme.TIDAL_WAVE -> {
|
||||
resIds += R.style.Theme_Tachiyomi_TidalWave
|
||||
}
|
||||
else -> {
|
||||
resIds += R.style.Theme_Tachiyomi
|
||||
}
|
||||
return buildList(2) {
|
||||
add(themeResources.getOrDefault(appTheme, R.style.Theme_Tachiyomi))
|
||||
if (isAmoled) add(R.style.ThemeOverlay_Tachiyomi_Amoled)
|
||||
}
|
||||
|
||||
if (isAmoled) {
|
||||
resIds += R.style.ThemeOverlay_Tachiyomi_Amoled
|
||||
}
|
||||
|
||||
return resIds
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,3 +27,17 @@ class ThemingDelegateImpl : ThemingDelegate {
|
||||
.forEach(activity::setTheme)
|
||||
}
|
||||
}
|
||||
|
||||
private val themeResources: Map<AppTheme, Int> = mapOf(
|
||||
AppTheme.MONET to R.style.Theme_Tachiyomi_Monet,
|
||||
AppTheme.GREEN_APPLE to R.style.Theme_Tachiyomi_GreenApple,
|
||||
AppTheme.LAVENDER to R.style.Theme_Tachiyomi_Lavender,
|
||||
AppTheme.MIDNIGHT_DUSK to R.style.Theme_Tachiyomi_MidnightDusk,
|
||||
AppTheme.NORD to R.style.Theme_Tachiyomi_Nord,
|
||||
AppTheme.STRAWBERRY_DAIQUIRI to R.style.Theme_Tachiyomi_StrawberryDaiquiri,
|
||||
AppTheme.TAKO to R.style.Theme_Tachiyomi_Tako,
|
||||
AppTheme.TEALTURQUOISE to R.style.Theme_Tachiyomi_TealTurquoise,
|
||||
AppTheme.YINYANG to R.style.Theme_Tachiyomi_YinYang,
|
||||
AppTheme.YOTSUBA to R.style.Theme_Tachiyomi_Yotsuba,
|
||||
AppTheme.TIDAL_WAVE to R.style.Theme_Tachiyomi_TidalWave,
|
||||
)
|
||||
|
@@ -5,7 +5,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import eu.kanade.core.util.insertSeparators
|
||||
import eu.kanade.presentation.history.HistoryUiModel
|
||||
import eu.kanade.tachiyomi.util.lang.toDateKey
|
||||
import eu.kanade.tachiyomi.util.lang.toLocalDate
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@@ -28,7 +28,7 @@ import tachiyomi.domain.history.interactor.RemoveHistory
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
import java.time.LocalDate
|
||||
|
||||
class HistoryScreenModel(
|
||||
private val getHistory: GetHistory = Injekt.get(),
|
||||
@@ -60,10 +60,12 @@ class HistoryScreenModel(
|
||||
private fun List<HistoryWithRelations>.toHistoryUiModels(): List<HistoryUiModel> {
|
||||
return map { HistoryUiModel.Item(it) }
|
||||
.insertSeparators { before, after ->
|
||||
val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0)
|
||||
val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0)
|
||||
val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN
|
||||
val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN
|
||||
when {
|
||||
beforeDate.time != afterDate.time && afterDate.time != 0L -> HistoryUiModel.Header(afterDate)
|
||||
beforeDate.isAfter(afterDate)
|
||||
or afterDate.equals(LocalDate.MIN)
|
||||
or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate)
|
||||
// Return null to avoid adding a separator between two items.
|
||||
else -> null
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.reader
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.assist.AssistContent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -136,7 +137,16 @@ class ReaderActivity : BaseActivity() {
|
||||
*/
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
registerSecureActivity(this)
|
||||
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
overrideActivityTransition(
|
||||
Activity.OVERRIDE_TRANSITION_OPEN,
|
||||
R.anim.shared_axis_x_push_enter,
|
||||
R.anim.shared_axis_x_push_exit,
|
||||
)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -267,7 +277,16 @@ class ReaderActivity : BaseActivity() {
|
||||
override fun finish() {
|
||||
viewModel.onActivityFinish()
|
||||
super.finish()
|
||||
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
overrideActivityTransition(
|
||||
Activity.OVERRIDE_TRANSITION_CLOSE,
|
||||
R.anim.shared_axis_x_pop_enter,
|
||||
R.anim.shared_axis_x_pop_exit,
|
||||
)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
|
@@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.util.lang.toDateKey
|
||||
import eu.kanade.tachiyomi.util.lang.toLocalDate
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
@@ -45,8 +45,8 @@ import tachiyomi.domain.updates.interactor.GetUpdates
|
||||
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.time.LocalDate
|
||||
import java.time.ZonedDateTime
|
||||
import java.util.Date
|
||||
|
||||
class UpdatesScreenModel(
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
@@ -374,12 +374,12 @@ class UpdatesScreenModel(
|
||||
return items
|
||||
.map { UpdatesUiModel.Item(it) }
|
||||
.insertSeparators { before, after ->
|
||||
val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
|
||||
val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0)
|
||||
val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN
|
||||
val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN
|
||||
when {
|
||||
beforeDate.time != afterDate.time && afterDate.time != 0L -> {
|
||||
UpdatesUiModel.Header(afterDate)
|
||||
}
|
||||
beforeDate.isAfter(afterDate)
|
||||
or afterDate.equals(LocalDate.MIN)
|
||||
or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate)
|
||||
// Return null to avoid adding a separator between two items.
|
||||
else -> null
|
||||
}
|
||||
|
@@ -1,8 +1,10 @@
|
||||
package eu.kanade.tachiyomi.ui.webview
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.assist.AssistContent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.core.net.toUri
|
||||
@@ -35,7 +37,16 @@ class WebViewActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
overrideActivityTransition(
|
||||
Activity.OVERRIDE_TRANSITION_OPEN,
|
||||
R.anim.shared_axis_x_push_enter,
|
||||
R.anim.shared_axis_x_push_exit,
|
||||
)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit)
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (!WebViewUtil.supportsWebView(this)) {
|
||||
@@ -77,7 +88,16 @@ class WebViewActivity : BaseActivity() {
|
||||
|
||||
override fun finish() {
|
||||
super.finish()
|
||||
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
overrideActivityTransition(
|
||||
Activity.OVERRIDE_TRANSITION_CLOSE,
|
||||
R.anim.shared_axis_x_pop_enter,
|
||||
R.anim.shared_axis_x_pop_exit,
|
||||
)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareWebpage(url: String) {
|
||||
|
@@ -6,15 +6,17 @@ import tachiyomi.core.common.i18n.stringResource
|
||||
import tachiyomi.i18n.MR
|
||||
import java.text.DateFormat
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
fun Date.toDateTimestampString(dateFormatter: DateFormat): String {
|
||||
val date = dateFormatter.format(this)
|
||||
val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this)
|
||||
fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String {
|
||||
val date = dateTimeFormatter.format(this)
|
||||
val time = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(this)
|
||||
return "$date $time"
|
||||
}
|
||||
|
||||
@@ -32,52 +34,28 @@ fun Long.convertEpochMillisZone(
|
||||
.toEpochMilli()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get date as time key
|
||||
*
|
||||
* @param date desired date
|
||||
* @return date as time key
|
||||
*/
|
||||
fun Long.toDateKey(): Date {
|
||||
val instant = Instant.ofEpochMilli(this)
|
||||
return Date.from(instant.truncatedTo(ChronoUnit.DAYS))
|
||||
fun Long.toLocalDate(): LocalDate {
|
||||
return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault())
|
||||
}
|
||||
|
||||
fun Date.toRelativeString(
|
||||
fun LocalDate.toRelativeString(
|
||||
context: Context,
|
||||
relative: Boolean = true,
|
||||
dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT),
|
||||
dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT),
|
||||
): String {
|
||||
if (!relative) {
|
||||
return dateFormat.format(this)
|
||||
}
|
||||
val now = Date()
|
||||
val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) -
|
||||
this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY)
|
||||
val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt()
|
||||
val now = LocalDate.now()
|
||||
val difference = ChronoUnit.DAYS.between(this, now)
|
||||
return when {
|
||||
difference < 0 -> dateFormat.format(this)
|
||||
difference < MILLISECONDS_IN_DAY -> context.stringResource(MR.strings.relative_time_today)
|
||||
difference < MILLISECONDS_IN_DAY.times(7) -> context.pluralStringResource(
|
||||
difference < 0 -> difference.toString()
|
||||
difference < 1 -> context.stringResource(MR.strings.relative_time_today)
|
||||
difference < 7 -> context.pluralStringResource(
|
||||
MR.plurals.relative_time,
|
||||
days,
|
||||
days,
|
||||
difference.toInt(),
|
||||
difference.toInt(),
|
||||
)
|
||||
else -> dateFormat.format(this)
|
||||
}
|
||||
}
|
||||
|
||||
private const val MILLISECONDS_IN_DAY = 86_400_000L
|
||||
|
||||
private val Date.timeWithOffset: Long
|
||||
get() {
|
||||
return Calendar.getInstance().run {
|
||||
time = this@timeWithOffset
|
||||
val dstOffset = get(Calendar.DST_OFFSET)
|
||||
this@timeWithOffset.time + timeZone.rawOffset + dstOffset
|
||||
}
|
||||
}
|
||||
|
||||
private fun Long.floorNearest(to: Long): Long {
|
||||
return this.floorDiv(to) * to
|
||||
}
|
||||
|
@@ -6,14 +6,17 @@ naming:
|
||||
constantPattern: '[A-Z][A-Za-z0-9]*'
|
||||
|
||||
complexity:
|
||||
LongMethod:
|
||||
ignoreAnnotated: [ 'Composable' ]
|
||||
LongParameterList:
|
||||
functionThreshold: 6
|
||||
constructorThreshold: 7
|
||||
ignoreDefaultParameters: true
|
||||
ignoreAnnotated: [ 'Composable' ]
|
||||
|
||||
style:
|
||||
MagicNumber:
|
||||
ignorePropertyDeclaration: true
|
||||
ignoreCompanionObjectPropertyDeclaration: true
|
||||
ReturnCount:
|
||||
excludeGuardClauses: true
|
||||
UnusedPrivateMember:
|
||||
ignoreAnnotated: [ 'Preview' ]
|
||||
|
@@ -1,26 +1,10 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx10248m -XX:MaxPermSize=256m
|
||||
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
||||
org.gradle.jvmargs=-Xmx5120m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
org.gradle.parallel=true
|
||||
|
||||
org.gradle.caching=true
|
||||
android.nonTransitiveRClass=false
|
||||
android.useAndroidX=true
|
||||
|
||||
kotlin.code.style=official
|
||||
kotlin.mpp.androidSourceSetLayoutVersion=2
|
||||
|
||||
android.useAndroidX=true
|
||||
android.nonTransitiveRClass=false
|
||||
org.gradle.caching=true
|
||||
org.gradle.configureondemand=true
|
||||
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
|
||||
org.gradle.parallel=true
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[versions]
|
||||
agp_version = "8.2.2"
|
||||
lifecycle_version = "2.6.2"
|
||||
lifecycle_version = "2.7.0"
|
||||
paging_version = "3.2.1"
|
||||
|
||||
[libraries]
|
||||
@@ -25,9 +25,9 @@ workmanager = "androidx.work:work-runtime:2.9.0"
|
||||
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
|
||||
paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" }
|
||||
|
||||
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.2"
|
||||
test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha02"
|
||||
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha02"
|
||||
benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3"
|
||||
test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03"
|
||||
test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03"
|
||||
test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-beta01"
|
||||
|
||||
[bundles]
|
||||
|
@@ -37,7 +37,7 @@ junrar = "com.github.junrar:junrar:7.5.5"
|
||||
|
||||
sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" }
|
||||
sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" }
|
||||
sqlite-android = "com.github.requery:sqlite-android:3.43.0"
|
||||
sqlite-android = "com.github.requery:sqlite-android:3.45.0"
|
||||
|
||||
preferencektx = "androidx.preference:preference-ktx:1.2.1"
|
||||
|
||||
@@ -61,7 +61,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013
|
||||
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
||||
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
|
||||
insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
|
||||
compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.1.0"
|
||||
compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0"
|
||||
|
||||
swipe = "me.saket.swipe:swipe:1.2.0"
|
||||
|
||||
|
Reference in New Issue
Block a user