mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
feat: Initial source repo support
Mostly just getting Compose to work as intended, no functionality at the moment
This commit is contained in:
parent
a1172f31e8
commit
16d5ff12e4
12 changed files with 315 additions and 8 deletions
|
@ -1,14 +1,7 @@
|
|||
package dev.yokai.domain.source
|
||||
|
||||
import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys
|
||||
import java.util.*
|
||||
|
||||
class SourcePreferences(private val preferenceStore: PreferenceStore) {
|
||||
fun lastUsedSources() = preferenceStore.getStringSet("last_used_sources", emptySet())
|
||||
|
||||
fun enabledLanguages() = preferenceStore.getStringSet(
|
||||
PreferenceKeys.enabledLanguages,
|
||||
setOfNotNull("all", "en", Locale.getDefault().language.takeIf { !it.startsWith("en") }),
|
||||
)
|
||||
fun extensionRepos() = preferenceStore.getStringSet("extension_repos", emptySet())
|
||||
}
|
||||
|
|
90
app/src/main/java/dev/yokai/presentation/Scaffold.kt
Normal file
90
app/src/main/java/dev/yokai/presentation/Scaffold.kt
Normal file
|
@ -0,0 +1,90 @@
|
|||
package dev.yokai.presentation
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.LargeTopAppBar
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import dev.yokai.presentation.component.ToolTipButton
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun YokaiScaffold(
|
||||
onNavigationIconClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String = "",
|
||||
scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(state = rememberTopAppBarState()),
|
||||
fab: @Composable () -> Unit = {},
|
||||
navigationIcon: ImageVector = Icons.Filled.ArrowBack,
|
||||
navigationIconLabel: String = stringResource(id = R.string.back),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
val useDarkIcons = MaterialTheme.colorScheme.surface.luminance() > .5
|
||||
val color = getTopAppBarColor(title)
|
||||
|
||||
SideEffect {
|
||||
val activity = view.context as Activity
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
activity.window.statusBarColor = color.toArgb()
|
||||
WindowInsetsControllerCompat(activity.window, view).isAppearanceLightStatusBars = useDarkIcons
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
floatingActionButton = fab,
|
||||
topBar = {
|
||||
LargeTopAppBar(
|
||||
title = {
|
||||
Text(text = title)
|
||||
},
|
||||
modifier = Modifier.statusBarsPadding(),
|
||||
colors = topAppBarColors(
|
||||
containerColor = color,
|
||||
scrolledContainerColor = color,
|
||||
),
|
||||
navigationIcon = {
|
||||
ToolTipButton(
|
||||
toolTipLabel = navigationIconLabel,
|
||||
icon = navigationIcon,
|
||||
buttonClicked = onNavigationIconClicked,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
actions = actions,
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getTopAppBarColor(title: String): Color {
|
||||
return when (title.isEmpty()) {
|
||||
true -> Color.Transparent
|
||||
false -> MaterialTheme.colorScheme.surface.copy(alpha = .7f)
|
||||
}
|
||||
}
|
129
app/src/main/java/dev/yokai/presentation/component/ToolTip.kt
Normal file
129
app/src/main/java/dev/yokai/presentation/component/ToolTip.kt
Normal file
|
@ -0,0 +1,129 @@
|
|||
package dev.yokai.presentation.component
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButtonDefaults
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.PlainTooltipBox
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.minimumInteractiveComponentSize
|
||||
import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ToolTipButton(
|
||||
toolTipLabel: String,
|
||||
modifier: Modifier = Modifier,
|
||||
iconModifier: Modifier = Modifier,
|
||||
icon: ImageVector? = null,
|
||||
painter: Painter? = null,
|
||||
isEnabled: Boolean = true,
|
||||
enabledTint: Color = MaterialTheme.colorScheme.onSurface,
|
||||
buttonClicked: () -> Unit = {},
|
||||
) {
|
||||
require(icon != null || painter != null)
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
PlainTooltipBox(
|
||||
tooltip = { Text(modifier = Modifier.padding(4.dp), style = MaterialTheme.typography.bodyLarge, text = toolTipLabel) },
|
||||
contentColor = MaterialTheme.colorScheme.onSurface,
|
||||
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(8.dp),
|
||||
) {
|
||||
CombinedClickableIconButton(
|
||||
enabled = isEnabled,
|
||||
enabledTint = enabledTint,
|
||||
modifier = modifier
|
||||
.tooltipAnchor()
|
||||
.iconButtonCombinedClickable(
|
||||
toolTipLabel = toolTipLabel,
|
||||
onClick = buttonClicked,
|
||||
isEnabled = isEnabled,
|
||||
onLongClick = {
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
},
|
||||
),
|
||||
) {
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
modifier = iconModifier,
|
||||
contentDescription = toolTipLabel,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painter = painter!!,
|
||||
modifier = iconModifier,
|
||||
contentDescription = toolTipLabel,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CombinedClickableIconButton(
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
enabledTint: Color,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier =
|
||||
modifier
|
||||
.minimumInteractiveComponentSize()
|
||||
.size(40.0.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val contentColor =
|
||||
if (enabled) {
|
||||
enabledTint
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
.copy(alpha = .38f)
|
||||
}
|
||||
CompositionLocalProvider(LocalContentColor provides contentColor, content = content)
|
||||
}
|
||||
}
|
||||
|
||||
fun Modifier.iconButtonCombinedClickable(
|
||||
toolTipLabel: String,
|
||||
isEnabled: Boolean,
|
||||
onLongClick: (() -> Unit)? = null,
|
||||
onDoubleClick: (() -> Unit)? = null,
|
||||
onClick: () -> Unit,
|
||||
) = composed {
|
||||
if (isEnabled) {
|
||||
combinedClickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(
|
||||
bounded = false,
|
||||
radius = 40.dp / 2,
|
||||
),
|
||||
onClickLabel = toolTipLabel,
|
||||
role = Role.Button,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
onDoubleClick = onDoubleClick,
|
||||
)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package dev.yokai.presentation.source
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController
|
||||
|
||||
class SourceRepoController :
|
||||
BaseComposeController() {
|
||||
|
||||
override fun getTitle(): String {
|
||||
return "Extension Repos"
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent() {
|
||||
SourceRepoScreen(
|
||||
title = getTitle().orEmpty(),
|
||||
onBackPress = router::handleBack,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package dev.yokai.presentation.source
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import dev.yokai.presentation.YokaiScaffold
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SourceRepoScreen(
|
||||
title: String,
|
||||
onBackPress: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
YokaiScaffold(
|
||||
onNavigationIconClicked = onBackPress,
|
||||
title = title,
|
||||
fab = {
|
||||
FloatingActionButton(
|
||||
onClick = { context.toast("Test") },
|
||||
) {
|
||||
Icon(Icons.Filled.Add, "Add repo")
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
Text(
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
text = "Hello World!"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -292,6 +292,7 @@ class PreferencesHelper(val context: Context, val preferenceStore: PreferenceSto
|
|||
|
||||
fun automaticExtUpdates() = preferenceStore.getBoolean(Keys.automaticExtUpdates, true)
|
||||
|
||||
// TODO: SourcePref
|
||||
fun installedExtensionsOrder() = preferenceStore.getInt(Keys.installedExtensionsOrder, InstalledExtensionsOrder.Name.value)
|
||||
|
||||
// TODO: SourcePref
|
||||
|
@ -342,6 +343,7 @@ class PreferencesHelper(val context: Context, val preferenceStore: PreferenceSto
|
|||
|
||||
fun migrateFlags() = preferenceStore.getInt("migrate_flags", Int.MAX_VALUE)
|
||||
|
||||
// TODO: SourcePref
|
||||
fun trustedSignatures() = preferenceStore.getStringSet("trusted_signatures", emptySet())
|
||||
|
||||
// using string instead of set so it is ordered
|
||||
|
@ -357,6 +359,7 @@ class PreferencesHelper(val context: Context, val preferenceStore: PreferenceSto
|
|||
|
||||
fun refreshCoversToo() = preferenceStore.getBoolean(Keys.refreshCoversToo, true)
|
||||
|
||||
// TODO: SourcePref
|
||||
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
||||
|
||||
fun recentsViewType() = preferenceStore.getInt("recents_view_type", 0)
|
||||
|
@ -461,6 +464,7 @@ class PreferencesHelper(val context: Context, val preferenceStore: PreferenceSto
|
|||
|
||||
fun appShouldAutoUpdate() = prefs.getInt(Keys.shouldAutoUpdate, AppDownloadInstallJob.ONLY_ON_UNMETERED)
|
||||
|
||||
// TODO: SourcePref
|
||||
fun autoUpdateExtensions() = prefs.getInt(Keys.autoUpdateExtensions, AppDownloadInstallJob.ONLY_ON_UNMETERED)
|
||||
|
||||
fun extensionInstaller() = preferenceStore.getInt("extension_installer", ExtensionInstaller.PACKAGE_INSTALLER)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package eu.kanade.tachiyomi.di
|
||||
|
||||
import android.app.Application
|
||||
import dev.yokai.domain.source.SourcePreferences
|
||||
import eu.kanade.tachiyomi.core.preference.AndroidPreferenceStore
|
||||
import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
|
@ -15,6 +16,10 @@ class PreferenceModule(val application: Application) : InjektModule {
|
|||
AndroidPreferenceStore(application)
|
||||
}
|
||||
|
||||
addSingletonFactory {
|
||||
SourcePreferences(get())
|
||||
}
|
||||
|
||||
addSingletonFactory {
|
||||
PreferencesHelper(
|
||||
context = application,
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import dev.yokai.presentation.theme.YokaiTheme
|
||||
|
||||
abstract class BaseComposeController(bundle: Bundle? = null) :
|
||||
|
@ -16,7 +17,13 @@ abstract class BaseComposeController(bundle: Bundle? = null) :
|
|||
container: ViewGroup,
|
||||
savedViewState: Bundle?
|
||||
): View {
|
||||
hideLegacyAppBar()
|
||||
return ComposeView(container.context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
YokaiTheme {
|
||||
ScreenContent()
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.bluelinelabs.conductor.Controller
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.view.BackHandlerControllerInterface
|
||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||
|
@ -176,4 +177,12 @@ abstract class BaseController(bundle: Bundle? = null) :
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun hideLegacyAppBar() {
|
||||
(activity as? AppCompatActivity)?.findViewById<View>(R.id.app_bar)?.isVisible = false
|
||||
}
|
||||
|
||||
fun showLegacyAppBar() {
|
||||
(activity as? AppCompatActivity)?.findViewById<View>(R.id.app_bar)?.isVisible = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ abstract class BaseLegacyController<VB : ViewBinding>(bundle: Bundle? = null) :
|
|||
val isBindingInitialized get() = this::binding.isInitialized
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View {
|
||||
showLegacyAppBar()
|
||||
binding = createBinding(inflater)
|
||||
binding.root.backgroundColor = binding.root.context.getResourceColor(R.attr.background)
|
||||
return binding.root
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.os.Build
|
|||
import android.provider.Settings
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import dev.yokai.presentation.source.SourceRepoController
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
|
@ -39,6 +40,13 @@ class SettingsBrowseController : SettingsController() {
|
|||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.extensions
|
||||
preference {
|
||||
titleRes = R.string.source_repos
|
||||
onClick { router.pushController(SourceRepoController().withFadeTransaction()) }
|
||||
// TODO: Enable once it's finished
|
||||
summary = "Temporarily disabled, will be enabled once it's fully implemented"
|
||||
isEnabled = BuildConfig.DEBUG
|
||||
}
|
||||
switchPreference {
|
||||
key = PreferenceKeys.automaticExtUpdates
|
||||
titleRes = R.string.check_for_extension_updates
|
||||
|
|
|
@ -905,6 +905,7 @@
|
|||
<string name="nsfw_sources">NSFW (18+) sources</string>
|
||||
<string name="show_in_sources_and_extensions">Show in sources and extensions lists</string>
|
||||
<string name="does_not_prevent_unofficial_nsfw">This does not prevent unofficial or potentially incorrectly flagged extensions from surfacing NSFW (18+) content within the app.</string>
|
||||
<string name="source_repos">Extension Repos</string>
|
||||
|
||||
<!-- About section -->
|
||||
<string name="version">Version</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue