Address some build warnings
Cette révision appartient à :
Parent
80d6d412f3
révision
2d7650537d
33 fichiers modifiés avec 102 ajouts et 144 suppressions
|
@ -2,7 +2,6 @@
|
||||||
|-------|----------|---------|------------|---------|
|
|-------|----------|---------|------------|---------|
|
||||||
| [![CI](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml/badge.svg)](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml) | [![stable release](https://img.shields.io/github/release/tachiyomiorg/tachiyomi.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi/releases) | [![latest preview build](https://img.shields.io/github/v/release/tachiyomiorg/tachiyomi-preview.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi-preview/releases) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/tachiyomi) |
|
| [![CI](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml/badge.svg)](https://github.com/tachiyomiorg/tachiyomi/actions/workflows/build_push.yml) | [![stable release](https://img.shields.io/github/release/tachiyomiorg/tachiyomi.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi/releases) | [![latest preview build](https://img.shields.io/github/v/release/tachiyomiorg/tachiyomi-preview.svg?maxAge=3600&label=download)](https://github.com/tachiyomiorg/tachiyomi-preview/releases) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg?label=discord&labelColor=7289da&color=2c2f33&style=flat)](https://discord.gg/tachiyomi) |
|
||||||
|
|
||||||
|
|
||||||
# ![app icon](./.github/readme-images/app-icon.png)Tachiyomi
|
# ![app icon](./.github/readme-images/app-icon.png)Tachiyomi
|
||||||
Tachiyomi is a free and open source manga reader for Android 6.0 and above.
|
Tachiyomi is a free and open source manga reader for Android 6.0 and above.
|
||||||
|
|
||||||
|
|
2
app/proguard-rules.pro
externe
2
app/proguard-rules.pro
externe
|
@ -45,7 +45,7 @@
|
||||||
|
|
||||||
##---------------Begin: proguard configuration for kotlinx.serialization ----------
|
##---------------Begin: proguard configuration for kotlinx.serialization ----------
|
||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
|
-dontnote kotlinx.serialization.** # core serialization annotations
|
||||||
|
|
||||||
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
||||||
-keepclassmembers class kotlinx.serialization.json.** {
|
-keepclassmembers class kotlinx.serialization.json.** {
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
|
||||||
<!-- Storage -->
|
<!-- Storage -->
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
|
||||||
<!-- For background jobs -->
|
<!-- For background jobs -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
@ -20,10 +21,12 @@
|
||||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||||
<!-- To view extension packages in API 30+ -->
|
<!-- To view extension packages in API 30+ -->
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES" />
|
<uses-permission android:name="android.permission.READ_APP_SPECIFIC_LOCALES"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import eu.kanade.core.preference.asToggleableState
|
import eu.kanade.core.preference.asToggleableState
|
||||||
import eu.kanade.presentation.category.visualName
|
import eu.kanade.presentation.category.visualName
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import tachiyomi.core.preference.CheckboxState
|
import tachiyomi.core.preference.CheckboxState
|
||||||
import tachiyomi.domain.category.model.Category
|
import tachiyomi.domain.category.model.Category
|
||||||
|
@ -39,7 +40,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||||
fun CategoryCreateDialog(
|
fun CategoryCreateDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onCreate: (String) -> Unit,
|
onCreate: (String) -> Unit,
|
||||||
categories: List<Category>,
|
categories: ImmutableList<Category>,
|
||||||
) {
|
) {
|
||||||
var name by remember { mutableStateOf("") }
|
var name by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ fun CategoryCreateDialog(
|
||||||
fun CategoryRenameDialog(
|
fun CategoryRenameDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onRename: (String) -> Unit,
|
onRename: (String) -> Unit,
|
||||||
categories: List<Category>,
|
categories: ImmutableList<Category>,
|
||||||
category: Category,
|
category: Category,
|
||||||
) {
|
) {
|
||||||
var name by remember { mutableStateOf(category.name) }
|
var name by remember { mutableStateOf(category.name) }
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -16,11 +17,13 @@ import tachiyomi.presentation.core.util.isScrollingUp
|
||||||
fun CategoryFloatingActionButton(
|
fun CategoryFloatingActionButton(
|
||||||
lazyListState: LazyListState,
|
lazyListState: LazyListState,
|
||||||
onCreate: () -> Unit,
|
onCreate: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
text = { Text(text = stringResource(MR.strings.action_add)) },
|
text = { Text(text = stringResource(MR.strings.action_add)) },
|
||||||
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
|
icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) },
|
||||||
onClick = onCreate,
|
onClick = onCreate,
|
||||||
expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
|
expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(),
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package eu.kanade.presentation.components
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.pluralStringResource
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
@ -13,18 +15,22 @@ fun DownloadDropdownMenu(
|
||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onDownloadClicked: (DownloadAction) -> Unit,
|
onDownloadClicked: (DownloadAction) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val options = persistentListOf(
|
||||||
|
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1),
|
||||||
|
DownloadAction.NEXT_5_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 5, 5),
|
||||||
|
DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 10, 10),
|
||||||
|
DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 25, 25),
|
||||||
|
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
|
||||||
|
)
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
listOfNotNull(
|
options.map { (downloadAction, string) ->
|
||||||
DownloadAction.NEXT_1_CHAPTER to pluralStringResource(MR.plurals.download_amount, 1, 1),
|
|
||||||
DownloadAction.NEXT_5_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 5, 5),
|
|
||||||
DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 10, 10),
|
|
||||||
DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 25, 25),
|
|
||||||
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
|
|
||||||
).map { (downloadAction, string) ->
|
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(text = string) },
|
text = { Text(text = string) },
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package eu.kanade.presentation.components
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ScrollState
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
|
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
|
||||||
import androidx.compose.material.icons.outlined.RadioButtonChecked
|
import androidx.compose.material.icons.outlined.RadioButtonChecked
|
||||||
|
@ -22,12 +25,17 @@ import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import androidx.compose.material3.DropdownMenu as ComposeDropdownMenu
|
import androidx.compose.material3.DropdownMenu as ComposeDropdownMenu
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DropdownMenu but overlaps anchor and has width constraints to better
|
||||||
|
* match non-Compose implementation.
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun DropdownMenu(
|
fun DropdownMenu(
|
||||||
expanded: Boolean,
|
expanded: Boolean,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
offset: DpOffset = DpOffset(8.dp, (-56).dp),
|
offset: DpOffset = DpOffset(8.dp, (-56).dp),
|
||||||
|
scrollState: ScrollState = rememberScrollState(),
|
||||||
properties: PopupProperties = PopupProperties(focusable = true),
|
properties: PopupProperties = PopupProperties(focusable = true),
|
||||||
content: @Composable ColumnScope.() -> Unit,
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
) {
|
) {
|
||||||
|
@ -36,6 +44,7 @@ fun DropdownMenu(
|
||||||
onDismissRequest = onDismissRequest,
|
onDismissRequest = onDismissRequest,
|
||||||
modifier = modifier.sizeIn(minWidth = 196.dp, maxWidth = 196.dp),
|
modifier = modifier.sizeIn(minWidth = 196.dp, maxWidth = 196.dp),
|
||||||
offset = offset,
|
offset = offset,
|
||||||
|
scrollState = scrollState,
|
||||||
properties = properties,
|
properties = properties,
|
||||||
content = content,
|
content = content,
|
||||||
)
|
)
|
||||||
|
@ -45,6 +54,7 @@ fun DropdownMenu(
|
||||||
fun RadioMenuItem(
|
fun RadioMenuItem(
|
||||||
text: @Composable () -> Unit,
|
text: @Composable () -> Unit,
|
||||||
isChecked: Boolean,
|
isChecked: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
|
@ -64,6 +74,7 @@ fun RadioMenuItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,25 +82,29 @@ fun RadioMenuItem(
|
||||||
fun NestedMenuItem(
|
fun NestedMenuItem(
|
||||||
text: @Composable () -> Unit,
|
text: @Composable () -> Unit,
|
||||||
children: @Composable ColumnScope.(() -> Unit) -> Unit,
|
children: @Composable ColumnScope.(() -> Unit) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var nestedExpanded by remember { mutableStateOf(false) }
|
var nestedExpanded by remember { mutableStateOf(false) }
|
||||||
val closeMenu = { nestedExpanded = false }
|
val closeMenu = { nestedExpanded = false }
|
||||||
|
|
||||||
DropdownMenuItem(
|
Box {
|
||||||
text = text,
|
DropdownMenuItem(
|
||||||
onClick = { nestedExpanded = true },
|
text = text,
|
||||||
trailingIcon = {
|
onClick = { nestedExpanded = true },
|
||||||
Icon(
|
trailingIcon = {
|
||||||
imageVector = Icons.AutoMirrored.Outlined.ArrowRight,
|
Icon(
|
||||||
contentDescription = null,
|
imageVector = Icons.AutoMirrored.Outlined.ArrowRight,
|
||||||
)
|
contentDescription = null,
|
||||||
},
|
)
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
|
||||||
DropdownMenu(
|
DropdownMenu(
|
||||||
expanded = nestedExpanded,
|
expanded = nestedExpanded,
|
||||||
onDismissRequest = closeMenu,
|
onDismissRequest = closeMenu,
|
||||||
) {
|
modifier = modifier,
|
||||||
children(closeMenu)
|
) {
|
||||||
|
children(closeMenu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,10 +49,10 @@ enum class ChapterDownloadAction {
|
||||||
@Composable
|
@Composable
|
||||||
fun ChapterDownloadIndicator(
|
fun ChapterDownloadIndicator(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
downloadStateProvider: () -> Download.State,
|
downloadStateProvider: () -> Download.State,
|
||||||
downloadProgressProvider: () -> Int,
|
downloadProgressProvider: () -> Int,
|
||||||
onClick: (ChapterDownloadAction) -> Unit,
|
onClick: (ChapterDownloadAction) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
when (val downloadState = downloadStateProvider()) {
|
when (val downloadState = downloadStateProvider()) {
|
||||||
Download.State.NOT_DOWNLOADED -> NotDownloadedIndicator(
|
Download.State.NOT_DOWNLOADED -> NotDownloadedIndicator(
|
||||||
|
@ -109,10 +109,10 @@ private fun NotDownloadedIndicator(
|
||||||
@Composable
|
@Composable
|
||||||
private fun DownloadingIndicator(
|
private fun DownloadingIndicator(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
downloadState: Download.State,
|
downloadState: Download.State,
|
||||||
downloadProgressProvider: () -> Int,
|
downloadProgressProvider: () -> Int,
|
||||||
onClick: (ChapterDownloadAction) -> Unit,
|
onClick: (ChapterDownloadAction) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||||
Box(
|
Box(
|
||||||
|
|
|
@ -182,7 +182,10 @@ private fun RowScope.Button(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
content: (@Composable () -> Unit)? = null,
|
content: (@Composable () -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val animatedWeight by animateFloatAsState(if (toConfirm) 2f else 1f)
|
val animatedWeight by animateFloatAsState(
|
||||||
|
targetValue = if (toConfirm) 2f else 1f,
|
||||||
|
label = "weight",
|
||||||
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(48.dp)
|
.size(48.dp)
|
||||||
|
|
|
@ -20,7 +20,6 @@ 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.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
|
|
@ -11,7 +11,6 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||||
/**
|
/**
|
||||||
* Returns a string of categories name for settings subtitle
|
* Returns a string of categories name for settings subtitle
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
@Composable
|
@Composable
|
||||||
fun getCategoriesLabel(
|
fun getCategoriesLabel(
|
||||||
|
|
|
@ -65,7 +65,6 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||||
allCategories: List<Category>,
|
allCategories: List<Category>,
|
||||||
libraryPreferences: LibraryPreferences,
|
libraryPreferences: LibraryPreferences,
|
||||||
): Preference.PreferenceGroup {
|
): Preference.PreferenceGroup {
|
||||||
val context = LocalContext.current
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
|
val userCategoriesCount = allCategories.filterNot(Category::isSystemCategory).size
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||||
val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) +
|
val ids = listOf(libraryPreferences.defaultCategory().defaultValue()) +
|
||||||
allCategories.fastMap { it.id.toInt() }
|
allCategories.fastMap { it.id.toInt() }
|
||||||
val labels = listOf(stringResource(MR.strings.default_category_summary)) +
|
val labels = listOf(stringResource(MR.strings.default_category_summary)) +
|
||||||
allCategories.fastMap { it.visualName(context) }
|
allCategories.fastMap { it.visualName }
|
||||||
|
|
||||||
return Preference.PreferenceGroup(
|
return Preference.PreferenceGroup(
|
||||||
title = stringResource(MR.strings.categories),
|
title = stringResource(MR.strings.categories),
|
||||||
|
|
|
@ -114,6 +114,7 @@ internal fun Modifier.highlightBackground(highlighted: Boolean): Modifier = comp
|
||||||
} else {
|
} else {
|
||||||
tween(200)
|
tween(200)
|
||||||
},
|
},
|
||||||
|
label = "highlight",
|
||||||
)
|
)
|
||||||
Modifier.background(color = highlight)
|
Modifier.background(color = highlight)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,20 @@ class BackupDecoder(
|
||||||
* Decode a potentially-gzipped backup.
|
* Decode a potentially-gzipped backup.
|
||||||
*/
|
*/
|
||||||
fun decode(uri: Uri): Backup {
|
fun decode(uri: Uri): Backup {
|
||||||
val backupStringSource = context.contentResolver.openInputStream(uri)!!.source().buffer()
|
return context.contentResolver.openInputStream(uri)!!.use { inputStream ->
|
||||||
|
val source = inputStream.source().buffer()
|
||||||
|
|
||||||
val peeked = backupStringSource.peek()
|
val peeked = source.peek().apply {
|
||||||
peeked.require(2)
|
require(2)
|
||||||
val id1id2 = peeked.readShort()
|
}
|
||||||
val backupString = if (id1id2.toInt() == 0x1f8b) { // 0x1f8b is gzip magic bytes
|
val id1id2 = peeked.readShort()
|
||||||
backupStringSource.gzip().buffer()
|
val backupString = if (id1id2.toInt() == 0x1f8b) { // 0x1f8b is gzip magic bytes
|
||||||
} else {
|
source.gzip().buffer()
|
||||||
backupStringSource
|
} else {
|
||||||
}.use { it.readByteArray() }
|
source
|
||||||
|
}.use { it.readByteArray() }
|
||||||
|
|
||||||
return parser.decodeFromByteArray(BackupSerializer, backupString)
|
parser.decodeFromByteArray(BackupSerializer, backupString)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,7 @@ class BackupCreator(
|
||||||
.forEach { it.delete() }
|
.forEach { it.delete() }
|
||||||
|
|
||||||
// Create new file to place backup
|
// Create new file to place backup
|
||||||
dir?.createFile(BackupCreator.getFilename())
|
dir?.createFile(getFilename())
|
||||||
} else {
|
} else {
|
||||||
UniFile.fromUri(context, uri)
|
UniFile.fromUri(context, uri)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class DownloadProvider(
|
||||||
private val storageManager: StorageManager = Injekt.get(),
|
private val storageManager: StorageManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val downloadsDir: UniFile?
|
private val downloadsDir: UniFile?
|
||||||
get() = storageManager.getDownloadsDirectory()
|
get() = storageManager.getDownloadsDirectory()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -246,7 +246,7 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
|
|
||||||
// Mark chapters as read action
|
// Mark chapters as read action
|
||||||
addAction(
|
addAction(
|
||||||
R.drawable.ic_glasses_24dp,
|
R.drawable.ic_done_24dp,
|
||||||
context.stringResource(MR.strings.action_mark_as_read),
|
context.stringResource(MR.strings.action_mark_as_read),
|
||||||
NotificationReceiver.markAsReadPendingBroadcast(
|
NotificationReceiver.markAsReadPendingBroadcast(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -235,7 +235,6 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
private const val NAME = "NotificationReceiver"
|
private const val NAME = "NotificationReceiver"
|
||||||
|
|
||||||
private const val ACTION_SHARE_IMAGE = "$ID.$NAME.SHARE_IMAGE"
|
private const val ACTION_SHARE_IMAGE = "$ID.$NAME.SHARE_IMAGE"
|
||||||
private const val ACTION_DELETE_IMAGE = "$ID.$NAME.DELETE_IMAGE"
|
|
||||||
|
|
||||||
private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP"
|
private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP"
|
||||||
|
|
||||||
|
@ -256,7 +255,6 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
private const val ACTION_DISMISS_NOTIFICATION = "$ID.$NAME.ACTION_DISMISS_NOTIFICATION"
|
private const val ACTION_DISMISS_NOTIFICATION = "$ID.$NAME.ACTION_DISMISS_NOTIFICATION"
|
||||||
|
|
||||||
private const val EXTRA_FILE_LOCATION = "$ID.$NAME.FILE_LOCATION"
|
|
||||||
private const val EXTRA_URI = "$ID.$NAME.URI"
|
private const val EXTRA_URI = "$ID.$NAME.URI"
|
||||||
private const val EXTRA_NOTIFICATION_ID = "$ID.$NAME.NOTIFICATION_ID"
|
private const val EXTRA_NOTIFICATION_ID = "$ID.$NAME.NOTIFICATION_ID"
|
||||||
private const val EXTRA_GROUP_ID = "$ID.$NAME.EXTRA_GROUP_ID"
|
private const val EXTRA_GROUP_ID = "$ID.$NAME.EXTRA_GROUP_ID"
|
||||||
|
@ -361,7 +359,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
it.id == notificationId
|
it.id == notificationId
|
||||||
}?.groupKey
|
}?.groupKey
|
||||||
|
|
||||||
if (groupId != null && groupId != 0 && groupKey != null && groupKey.isNotEmpty()) {
|
if (groupId != null && groupId != 0 && !groupKey.isNullOrEmpty()) {
|
||||||
val notifications = context.notificationManager.activeNotifications.filter {
|
val notifications = context.notificationManager.activeNotifications.filter {
|
||||||
it.groupKey == groupKey
|
it.groupKey == groupKey
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class BangumiInterceptor(val bangumi: Bangumi) : Interceptor {
|
class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
|
|
@ -62,12 +62,3 @@ fun Track.toBangumiStatus() = when (status) {
|
||||||
Bangumi.PLAN_TO_READ -> "wish"
|
Bangumi.PLAN_TO_READ -> "wish"
|
||||||
else -> throw NotImplementedError("Unknown status: $status")
|
else -> throw NotImplementedError("Unknown status: $status")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toTrackStatus(status: String) = when (status) {
|
|
||||||
"do" -> Bangumi.READING
|
|
||||||
"collect" -> Bangumi.COMPLETED
|
|
||||||
"on_hold" -> Bangumi.ON_HOLD
|
|
||||||
"dropped" -> Bangumi.DROPPED
|
|
||||||
"wish" -> Bangumi.PLAN_TO_READ
|
|
||||||
else -> throw NotImplementedError("Unknown status: $status")
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class KitsuInterceptor(val kitsu: Kitsu) : Interceptor {
|
class KitsuInterceptor(private val kitsu: Kitsu) : Interceptor {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import okhttp3.Interceptor
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class ShikimoriInterceptor(val shikimori: Shikimori) : Interceptor {
|
class ShikimoriInterceptor(private val shikimori: Shikimori) : Interceptor {
|
||||||
|
|
||||||
private val json: Json by injectLazy()
|
private val json: Json by injectLazy()
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,6 @@ package eu.kanade.tachiyomi.util.system
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.view.ViewPropertyAnimator
|
|
||||||
import android.view.animation.Animation
|
|
||||||
import androidx.constraintlayout.motion.widget.MotionScene.Transition
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the duration multiplier for general animations on the device
|
* Gets the duration multiplier for general animations on the device
|
||||||
|
@ -12,19 +9,3 @@ import androidx.constraintlayout.motion.widget.MotionScene.Transition
|
||||||
*/
|
*/
|
||||||
val Context.animatorDurationScale: Float
|
val Context.animatorDurationScale: Float
|
||||||
get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
|
get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
|
||||||
|
|
||||||
/** Scale the duration of this [Animation] by [Context.animatorDurationScale] */
|
|
||||||
fun Animation.applySystemAnimatorScale(context: Context) {
|
|
||||||
this.duration = (this.duration * context.animatorDurationScale).toLong()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Scale the duration of this [Transition] by [Context.animatorDurationScale] */
|
|
||||||
fun Transition.applySystemAnimatorScale(context: Context) {
|
|
||||||
// End layout of cover expanding animation tends to break when the transition is less than ~25ms
|
|
||||||
this.duration = (this.duration * context.animatorDurationScale).toInt().coerceAtLeast(25)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Scale the duration of this [ViewPropertyAnimator] by [Context.animatorDurationScale] */
|
|
||||||
fun ViewPropertyAnimator.applySystemAnimatorScale(context: Context): ViewPropertyAnimator = apply {
|
|
||||||
this.duration = (this.duration * context.animatorDurationScale).toLong()
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,18 +17,10 @@ private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
|
||||||
// make sure icons on the nav rail fit
|
// make sure icons on the nav rail fit
|
||||||
private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
|
private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
|
||||||
|
|
||||||
fun Context.isTabletUi(): Boolean {
|
|
||||||
return resources.configuration.isTabletUi()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Configuration.isTabletUi(): Boolean {
|
fun Configuration.isTabletUi(): Boolean {
|
||||||
return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
|
return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Configuration.isAutoTabletUiAvailable(): Boolean {
|
|
||||||
return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
|
// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
|
||||||
fun Context.prepareTabletUiContext(): Context {
|
fun Context.prepareTabletUiContext(): Context {
|
||||||
val configuration = resources.configuration
|
val configuration = resources.configuration
|
||||||
|
|
9
app/src/main/res/drawable/ic_done_24dp.xml
Fichier normal
9
app/src/main/res/drawable/ic_done_24dp.xml
Fichier normal
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFF"
|
||||||
|
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" />
|
||||||
|
</vector>
|
|
@ -1,5 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/recycler"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
|
@ -51,6 +51,7 @@ fun OkHttpClient.Builder.rateLimitHost(
|
||||||
* @param permits [Int] Number of requests allowed within a period of units.
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("UNUSED")
|
||||||
fun OkHttpClient.Builder.rateLimitHost(
|
fun OkHttpClient.Builder.rateLimitHost(
|
||||||
httpUrl: HttpUrl,
|
httpUrl: HttpUrl,
|
||||||
permits: Int,
|
permits: Int,
|
||||||
|
@ -71,5 +72,6 @@ fun OkHttpClient.Builder.rateLimitHost(
|
||||||
* @param permits [Int] Number of requests allowed within a period of units.
|
* @param permits [Int] Number of requests allowed within a period of units.
|
||||||
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
* @param period [Duration] The limiting duration. Defaults to 1.seconds.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("UNUSED")
|
||||||
fun OkHttpClient.Builder.rateLimitHost(url: String, permits: Int, period: Duration = 1.seconds) =
|
fun OkHttpClient.Builder.rateLimitHost(url: String, permits: Int, period: Duration = 1.seconds) =
|
||||||
addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
addInterceptor(RateLimitInterceptor(url.toHttpUrlOrNull()?.host, permits, period))
|
||||||
|
|
|
@ -51,6 +51,7 @@ class InMemoryPreferenceStore(
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T> getObject(
|
override fun <T> getObject(
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: T,
|
defaultValue: T,
|
||||||
|
@ -59,7 +60,7 @@ class InMemoryPreferenceStore(
|
||||||
): Preference<T> {
|
): Preference<T> {
|
||||||
val default = InMemoryPreference(key, null, defaultValue)
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
val data: T? = preferences[key]?.get() as? T
|
val data: T? = preferences[key]?.get() as? T
|
||||||
return if (data == null) default else InMemoryPreference<T>(key, data, defaultValue)
|
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAll(): Map<String, *> {
|
override fun getAll(): Map<String, *> {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import androidx.compose.foundation.gestures.anchoredDraggable
|
||||||
import androidx.compose.foundation.gestures.animateTo
|
import androidx.compose.foundation.gestures.animateTo
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
|
||||||
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.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
@ -78,7 +77,7 @@ fun AdaptiveSheet(
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BoxWithConstraints(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(
|
.clickable(
|
||||||
enabled = true,
|
enabled = true,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
<color name="splash">@color/accent_blue</color>
|
<color name="splash">@color/accent_blue</color>
|
||||||
|
|
||||||
<color name="cover_placeholder">#1F888888</color>
|
<color name="cover_placeholder">#1F888888</color>
|
||||||
|
@ -20,21 +20,7 @@
|
||||||
<color name="background_amoled">#000000</color>
|
<color name="background_amoled">#000000</color>
|
||||||
|
|
||||||
<!-- Material Design Colors -->
|
<!-- Material Design Colors -->
|
||||||
<color name="md_black_1000">#000000</color>
|
|
||||||
<color name="md_black_1000_87">#DE000000</color>
|
|
||||||
<color name="md_black_1000_54">#8A000000</color>
|
|
||||||
<color name="md_black_1000_38">#61000000</color>
|
|
||||||
<color name="md_black_1000_12">#1F000000</color>
|
<color name="md_black_1000_12">#1F000000</color>
|
||||||
<color name="md_black_1000_8">#14000000</color>
|
|
||||||
<color name="md_black_1000_6">#0F000000</color>
|
|
||||||
|
|
||||||
<color name="md_white_1000">#FFFFFFFF</color>
|
|
||||||
<color name="md_white_1000_70">#B3FFFFFF</color>
|
|
||||||
<color name="md_white_1000_54">#8AFFFFFF</color>
|
|
||||||
<color name="md_white_1000_50">#80FFFFFF</color>
|
|
||||||
<color name="md_white_1000_20">#33FFFFFF</color>
|
|
||||||
<color name="md_white_1000_12">#1FFFFFFF</color>
|
<color name="md_white_1000_12">#1FFFFFFF</color>
|
||||||
<color name="md_white_1000_8">#14FFFFFF</color>
|
|
||||||
<color name="md_white_1000_6">#0FFFFFFF</color>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package tachiyomi.presentation.widget
|
package tachiyomi.presentation.widget
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.glance.ImageProvider
|
import androidx.glance.ImageProvider
|
||||||
import androidx.glance.unit.ColorProvider
|
import androidx.glance.unit.ColorProvider
|
||||||
|
|
||||||
class UpdatesGridGlanceWidget : BaseUpdatesGridGlanceWidget() {
|
class UpdatesGridGlanceWidget : BaseUpdatesGridGlanceWidget() {
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
override val foreground = ColorProvider(R.color.appwidget_on_secondary_container)
|
override val foreground = ColorProvider(R.color.appwidget_on_secondary_container)
|
||||||
override val background = ImageProvider(R.drawable.appwidget_background)
|
override val background = ImageProvider(R.drawable.appwidget_background)
|
||||||
override val topPadding = 0.dp
|
override val topPadding = 0.dp
|
||||||
|
|
|
@ -29,36 +29,6 @@ interface SManga : Serializable {
|
||||||
return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct()
|
return genre?.split(", ")?.map { it.trim() }?.filterNot { it.isBlank() }?.distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun copyFrom(other: SManga) {
|
|
||||||
if (other.author != null) {
|
|
||||||
author = other.author
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other.artist != null) {
|
|
||||||
artist = other.artist
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other.description != null) {
|
|
||||||
description = other.description
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other.genre != null) {
|
|
||||||
genre = other.genre
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other.thumbnail_url != null) {
|
|
||||||
thumbnail_url = other.thumbnail_url
|
|
||||||
}
|
|
||||||
|
|
||||||
status = other.status
|
|
||||||
|
|
||||||
update_strategy = other.update_strategy
|
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = other.initialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun copy() = create().also {
|
fun copy() = create().also {
|
||||||
it.url = url
|
it.url = url
|
||||||
it.title = title
|
it.title = title
|
||||||
|
|
|
@ -6,6 +6,7 @@ package eu.kanade.tachiyomi.source.model
|
||||||
*
|
*
|
||||||
* @since extensions-lib 1.4
|
* @since extensions-lib 1.4
|
||||||
*/
|
*/
|
||||||
|
@Suppress("UNUSED")
|
||||||
enum class UpdateStrategy {
|
enum class UpdateStrategy {
|
||||||
/**
|
/**
|
||||||
* Series marked as always update will be included in the library
|
* Series marked as always update will be included in the library
|
||||||
|
|
Référencer dans un nouveau ticket