mirror of
https://github.com/null2264/yokai.git
synced 2025-06-20 18:24:42 +00:00
refactor: Use Compose for About page
This commit is contained in:
parent
37f1f0e330
commit
cab40214d2
19 changed files with 571 additions and 301 deletions
|
@ -21,6 +21,7 @@ The format is simplified version of [Keep a Changelog](https://keepachangelog.co
|
|||
- Refactor EmptyView to use Compose
|
||||
- Refactor Reader ChapterTransition to use Compose (@arkon)
|
||||
- [Experimental] Add modified version of LargeTopAppBar that mimic J2K's ExpandedAppBarLayout
|
||||
- Refactor About page to use Compose
|
||||
|
||||
## [1.9.7]
|
||||
|
||||
|
|
|
@ -5,9 +5,17 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import eu.kanade.tachiyomi.core.preference.Preference
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
fun <T> Preference<T>.collectAsState(): State<T> {
|
||||
val flow = remember(this) { changes() }
|
||||
return flow.collectAsState(initial = get())
|
||||
}
|
||||
|
||||
fun String.asDateFormat(): DateFormat = when (this) {
|
||||
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
else -> SimpleDateFormat(this, Locale.getDefault())
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.BuildConfig
|
|||
import eu.kanade.tachiyomi.core.preference.Preference
|
||||
import eu.kanade.tachiyomi.core.preference.PreferenceStore
|
||||
import eu.kanade.tachiyomi.core.preference.getEnum
|
||||
import eu.kanade.tachiyomi.core.storage.preference.asDateFormat
|
||||
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
|
||||
import eu.kanade.tachiyomi.domain.manga.models.Manga
|
||||
import eu.kanade.tachiyomi.extension.model.InstalledExtensionsOrder
|
||||
|
@ -19,13 +20,12 @@ import eu.kanade.tachiyomi.ui.reader.settings.ReadingModeType
|
|||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||
import eu.kanade.tachiyomi.ui.recents.RecentsPresenter
|
||||
import eu.kanade.tachiyomi.util.system.Themes
|
||||
import java.text.DateFormat
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues as Values
|
||||
|
||||
|
@ -187,10 +187,10 @@ class PreferencesHelper(val context: Context, val preferenceStore: PreferenceSto
|
|||
|
||||
fun anilistScoreType() = preferenceStore.getString("anilist_score_type", "POINT_10")
|
||||
|
||||
fun dateFormat(format: String = preferenceStore.getString(Keys.dateFormat, "").get()): DateFormat = when (format) {
|
||||
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
|
||||
else -> SimpleDateFormat(format, Locale.getDefault())
|
||||
}
|
||||
fun dateFormatRaw() = preferenceStore.getString(Keys.dateFormat, "")
|
||||
|
||||
@Deprecated("Use dateFormatRaw().get().asDateFormat() instead")
|
||||
fun dateFormat(format: String = dateFormatRaw().get()): DateFormat = format.asDateFormat()
|
||||
|
||||
fun appLanguage() = preferenceStore.getString("app_language", "")
|
||||
|
||||
|
|
|
@ -1,173 +1,53 @@
|
|||
package eu.kanade.tachiyomi.ui.more
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.toUri
|
||||
import androidx.preference.PreferenceScreen
|
||||
import co.touchlab.kermit.Logger
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import cafe.adriel.voyager.core.stack.StackEvent
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.transitions.ScreenTransition
|
||||
import eu.kanade.tachiyomi.data.updater.AppDownloadInstallJob
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateNotifier
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
|
||||
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsLegacyController
|
||||
import eu.kanade.tachiyomi.ui.setting.add
|
||||
import eu.kanade.tachiyomi.ui.setting.onClick
|
||||
import eu.kanade.tachiyomi.ui.setting.preference
|
||||
import eu.kanade.tachiyomi.ui.setting.preferenceCategory
|
||||
import eu.kanade.tachiyomi.ui.setting.titleMRes
|
||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||
import eu.kanade.tachiyomi.util.system.isOnline
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.compose.LocalAlertDialog
|
||||
import eu.kanade.tachiyomi.util.compose.LocalBackPress
|
||||
import eu.kanade.tachiyomi.util.system.materialAlertDialog
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.setNegativeButton
|
||||
import eu.kanade.tachiyomi.util.view.setPositiveButton
|
||||
import eu.kanade.tachiyomi.util.view.setTitle
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import soup.compose.material.motion.animation.materialSharedAxisZ
|
||||
import yokai.domain.ComposableAlertDialog
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import yokai.presentation.settings.screen.about.AboutScreen
|
||||
import android.R as AR
|
||||
|
||||
class AboutController : SettingsLegacyController() {
|
||||
class AboutController : BaseComposeController() {
|
||||
|
||||
/**
|
||||
* Checks for new releases
|
||||
*/
|
||||
private val updateChecker by lazy { AppUpdateChecker() }
|
||||
|
||||
private val dateFormat: DateFormat by lazy {
|
||||
preferences.dateFormat()
|
||||
}
|
||||
|
||||
private val isUpdaterEnabled = BuildConfig.INCLUDE_UPDATER
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleMRes = MR.strings.about
|
||||
|
||||
preference {
|
||||
key = "pref_whats_new"
|
||||
titleMRes = MR.strings.whats_new_this_release
|
||||
onClick {
|
||||
val intent = Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
if (BuildConfig.DEBUG) {
|
||||
"https://github.com/null2264/yokai/commits/master"
|
||||
} else {
|
||||
RELEASE_URL
|
||||
}.toUri(),
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
if (isUpdaterEnabled) {
|
||||
preference {
|
||||
key = "pref_check_for_updates"
|
||||
titleMRes = MR.strings.check_for_updates
|
||||
onClick {
|
||||
if (activity!!.isOnline()) {
|
||||
checkVersion()
|
||||
} else {
|
||||
activity!!.toast(MR.strings.no_network_connection)
|
||||
}
|
||||
@Composable
|
||||
override fun ScreenContent() {
|
||||
Navigator(
|
||||
screen = AboutScreen { body, url, isBeta ->
|
||||
NewUpdateDialogController(body, url, isBeta).showDialog(router)
|
||||
},
|
||||
content = {
|
||||
CompositionLocalProvider(
|
||||
LocalAlertDialog provides ComposableAlertDialog(null),
|
||||
LocalBackPress provides router::handleBack,
|
||||
) {
|
||||
ScreenTransition(
|
||||
navigator = it,
|
||||
// FIXME: Mimic J2K's Conductor transition
|
||||
transition = { materialSharedAxisZ(forward = it.lastEvent != StackEvent.Pop) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_version"
|
||||
titleMRes = MR.strings.version
|
||||
summary = if (BuildConfig.DEBUG || BuildConfig.NIGHTLY) {
|
||||
"r" + BuildConfig.COMMIT_COUNT
|
||||
} else {
|
||||
BuildConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
onClick {
|
||||
activity?.let {
|
||||
val deviceInfo = CrashLogUtil(it.localeContext).getDebugInfo()
|
||||
val clipboard = it.getSystemService<ClipboardManager>()!!
|
||||
val appInfo = it.getString(MR.strings.app_info)
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText(appInfo, deviceInfo))
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
view?.snack(context.getString(MR.strings._copied_to_clipboard, appInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_build_time"
|
||||
titleMRes = MR.strings.build_time
|
||||
summary = getFormattedBuildTime(dateFormat)
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
preference {
|
||||
key = "pref_oss"
|
||||
titleMRes = MR.strings.open_source_licenses
|
||||
|
||||
onClick {
|
||||
router.pushController(AboutLicenseController().withFadeTransaction())
|
||||
}
|
||||
}
|
||||
}
|
||||
add(AboutLinksPreference(context))
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks version and shows a user prompt if an update is available.
|
||||
*/
|
||||
private fun checkVersion() {
|
||||
val activity = activity ?: return
|
||||
|
||||
activity.toast(MR.strings.searching_for_updates)
|
||||
viewScope.launch {
|
||||
val result = try {
|
||||
updateChecker.checkForUpdate(activity, true)
|
||||
} catch (error: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
activity.toast(error.message)
|
||||
Logger.e(error) { "Couldn't check new update" }
|
||||
}
|
||||
}
|
||||
when (result) {
|
||||
is AppUpdateResult.NewUpdate -> {
|
||||
val body = result.release.info
|
||||
val url = result.release.downloadLink
|
||||
val isBeta = result.release.preRelease == true
|
||||
|
||||
// Create confirmation window
|
||||
withContext(Dispatchers.Main) {
|
||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||
NewUpdateDialogController(body, url, isBeta).showDialog(router)
|
||||
}
|
||||
}
|
||||
is AppUpdateResult.NoNewUpdate -> {
|
||||
withContext(Dispatchers.Main) {
|
||||
activity.toast(MR.strings.no_new_updates_available)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
|
@ -220,19 +100,4 @@ class AboutController : SettingsLegacyController() {
|
|||
const val IS_BETA = "NewUpdateDialogController.is_beta"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getFormattedBuildTime(dateFormat: DateFormat): String {
|
||||
try {
|
||||
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault())
|
||||
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val buildTime =
|
||||
inputDf.parse(BuildConfig.BUILD_TIME) ?: return BuildConfig.BUILD_TIME
|
||||
|
||||
return buildTime.toTimestampString(dateFormat)
|
||||
} catch (e: ParseException) {
|
||||
return BuildConfig.BUILD_TIME
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.more
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import cafe.adriel.voyager.core.stack.StackEvent
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.transitions.ScreenTransition
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseComposeController
|
||||
import eu.kanade.tachiyomi.util.compose.LocalBackPress
|
||||
import soup.compose.material.motion.animation.materialSharedAxisZ
|
||||
|
||||
class AboutLicenseController : BaseComposeController() {
|
||||
@Composable
|
||||
override fun ScreenContent() {
|
||||
Navigator(
|
||||
screen = AboutLicenseScreen(),
|
||||
content = {
|
||||
CompositionLocalProvider(LocalBackPress provides router::handleBack) {
|
||||
ScreenTransition(
|
||||
navigator = it,
|
||||
transition = { materialSharedAxisZ(forward = it.lastEvent != StackEvent.Pop) },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.more
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.view.compatToolTipText
|
||||
|
||||
class AboutLinksPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
Preference(context, attrs) {
|
||||
|
||||
init {
|
||||
layoutResource = R.layout.pref_about_links
|
||||
isSelectable = false
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
|
||||
/*
|
||||
(holder.itemView as LinearLayout).apply {
|
||||
checkHeightThen {
|
||||
val childCount = (this.getChildAt(0) as ViewGroup).childCount
|
||||
val childCount2 = (this.getChildAt(1) as ViewGroup).childCount
|
||||
val fullCount = childCount + childCount2
|
||||
orientation =
|
||||
if (width >= (56 * fullCount).dpToPx) LinearLayout.HORIZONTAL else LinearLayout.VERTICAL
|
||||
}
|
||||
}
|
||||
*/
|
||||
holder.findViewById(R.id.btn_website).apply {
|
||||
compatToolTipText = (contentDescription.toString())
|
||||
setOnClickListener { context.openInBrowser("https://mihon.app") }
|
||||
}
|
||||
holder.findViewById(R.id.btn_discord).apply {
|
||||
compatToolTipText = (contentDescription.toString())
|
||||
setOnClickListener { context.openInBrowser("https://discord.gg/mihon") }
|
||||
}
|
||||
holder.findViewById(R.id.btn_github).apply {
|
||||
compatToolTipText = (contentDescription.toString())
|
||||
setOnClickListener { context.openInBrowser("https://github.com/null2264/yokai") }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,15 +4,15 @@ import android.os.Build
|
|||
import androidx.preference.PreferenceScreen
|
||||
import androidx.webkit.WebViewCompat
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.ui.more.AboutController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsLegacyController
|
||||
import eu.kanade.tachiyomi.ui.setting.onClick
|
||||
import eu.kanade.tachiyomi.ui.setting.preference
|
||||
import eu.kanade.tachiyomi.ui.setting.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import yokai.i18n.MR
|
||||
import java.text.DateFormat
|
||||
import yokai.i18n.MR
|
||||
import yokai.presentation.settings.screen.about.getFormattedBuildTime
|
||||
|
||||
class DebugController : SettingsLegacyController() {
|
||||
|
||||
|
@ -49,7 +49,7 @@ class DebugController : SettingsLegacyController() {
|
|||
preference {
|
||||
key = "pref_build_time"
|
||||
title = "Build Time"
|
||||
summary = AboutController.getFormattedBuildTime(dateFormat)
|
||||
summary = getFormattedBuildTime(dateFormat)
|
||||
}
|
||||
preference {
|
||||
key = "pref_webview_version"
|
||||
|
|
|
@ -69,7 +69,8 @@ import kotlinx.coroutines.launch
|
|||
import uy.kohesive.injekt.injectLazy
|
||||
import yokai.domain.manga.interactor.GetManga
|
||||
import yokai.i18n.MR
|
||||
import yokai.presentation.component.icons.LocalSource
|
||||
import yokai.presentation.core.icons.CustomIcons
|
||||
import yokai.presentation.core.icons.LocalSource
|
||||
import yokai.util.lang.getString
|
||||
|
||||
/**
|
||||
|
@ -610,7 +611,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||
if (presenter.source is HttpSource) {
|
||||
Icons.Filled.ExploreOff
|
||||
} else {
|
||||
Icons.Filled.LocalSource
|
||||
CustomIcons.LocalSource
|
||||
},
|
||||
message,
|
||||
actions,
|
||||
|
|
|
@ -23,7 +23,6 @@ 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.unit.dp
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import yokai.i18n.MR
|
||||
|
@ -35,14 +34,16 @@ fun YokaiScaffold(
|
|||
onNavigationIconClicked: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
title: String = "",
|
||||
scrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(state = rememberTopAppBarState()),
|
||||
scrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
fab: @Composable () -> Unit = {},
|
||||
navigationIcon: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
navigationIconLabel: String = stringResource(MR.strings.back),
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
appBarType: AppBarType = AppBarType.LARGE,
|
||||
snackbarHost: @Composable () -> Unit = {},
|
||||
content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
val scrollBehaviorOrDefault = scrollBehavior ?: TopAppBarDefaults.enterAlwaysScrollBehavior(state = rememberTopAppBarState())
|
||||
val view = LocalView.current
|
||||
val useDarkIcons = MaterialTheme.colorScheme.surface.luminance() > .5
|
||||
val (color, scrolledColor) = getTopAppBarColor(title)
|
||||
|
@ -56,7 +57,7 @@ fun YokaiScaffold(
|
|||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
modifier = modifier.nestedScroll(scrollBehaviorOrDefault.nestedScrollConnection),
|
||||
floatingActionButton = fab,
|
||||
topBar = {
|
||||
when (appBarType) {
|
||||
|
@ -76,7 +77,7 @@ fun YokaiScaffold(
|
|||
buttonClicked = onNavigationIconClicked,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
scrollBehavior = scrollBehaviorOrDefault,
|
||||
actions = actions,
|
||||
)
|
||||
AppBarType.LARGE -> ExpandedAppBar(
|
||||
|
@ -95,11 +96,12 @@ fun YokaiScaffold(
|
|||
buttonClicked = onNavigationIconClicked,
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
scrollBehavior = scrollBehaviorOrDefault,
|
||||
actions = actions,
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = snackbarHost,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
package yokai.presentation.component.icons
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
private var _localSource: ImageVector? = null
|
||||
|
||||
val Icons.Filled.LocalSource: ImageVector get() {
|
||||
if (_localSource != null) return _localSource!!
|
||||
_localSource = ImageVector.Builder(
|
||||
name = "localSource",
|
||||
defaultWidth = 24.0.dp,
|
||||
defaultHeight = 24.0.dp,
|
||||
viewportWidth = 24.0f,
|
||||
viewportHeight = 24.0f,
|
||||
).apply {
|
||||
path(fill = SolidColor(Color.Black)) {
|
||||
moveTo(12f, 11.55f)
|
||||
curveTo(9.64f, 9.35f, 6.48f, 8f, 3f, 8f)
|
||||
verticalLineToRelative(11f)
|
||||
curveToRelative(3.48f, 0f, 6.64f, 1.35f, 9f, 3.55f)
|
||||
curveToRelative(2.36f, -2.19f, 5.52f, -3.55f, 9f, -3.55f)
|
||||
verticalLineTo(8f)
|
||||
curveToRelative(-3.48f, 0f, -6.64f, 1.35f, -9f, 3.55f)
|
||||
close()
|
||||
moveTo(12f, 8f)
|
||||
curveToRelative(1.66f, 0f, 3f, -1.34f, 3f, -3f)
|
||||
reflectiveCurveToRelative(-1.34f, -3f, -3f, -3f)
|
||||
reflectiveCurveToRelative(-3f, 1.34f, -3f, 3f)
|
||||
reflectiveCurveToRelative(1.34f, 3f, 3f, 3f)
|
||||
close()
|
||||
}
|
||||
}.build()
|
||||
return _localSource!!
|
||||
}
|
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
@ -35,11 +36,12 @@ fun SettingsScaffold(
|
|||
title: String,
|
||||
appBarType: AppBarType? = null,
|
||||
appBarActions: @Composable RowScope.() -> Unit = {},
|
||||
itemsProvider: @Composable () -> List<Preference>,
|
||||
appBarScrollBehavior: TopAppBarScrollBehavior? = null,
|
||||
snackbarHost: @Composable () -> Unit = {},
|
||||
content: @Composable (PaddingValues) -> Unit,
|
||||
) {
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
val useLargeAppBar by preferences.useLargeToolbar().collectAsState()
|
||||
val listState = rememberLazyListState()
|
||||
val onBackPress = LocalBackPress.currentOrThrow
|
||||
val alertDialog = LocalAlertDialog.currentOrThrow
|
||||
|
||||
|
@ -48,14 +50,34 @@ fun SettingsScaffold(
|
|||
title = title,
|
||||
appBarType = appBarType ?: if (useLargeAppBar) AppBarType.LARGE else AppBarType.SMALL,
|
||||
actions = appBarActions,
|
||||
scrollBehavior = enterAlwaysCollapsedScrollBehavior(
|
||||
scrollBehavior = appBarScrollBehavior,
|
||||
snackbarHost = snackbarHost,
|
||||
) { innerPadding ->
|
||||
alertDialog.content?.let { it() }
|
||||
|
||||
content(innerPadding)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsScaffold(
|
||||
title: String,
|
||||
appBarType: AppBarType? = null,
|
||||
appBarActions: @Composable RowScope.() -> Unit = {},
|
||||
itemsProvider: @Composable () -> List<Preference>,
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
SettingsScaffold(
|
||||
title = title,
|
||||
appBarType = appBarType,
|
||||
appBarActions = appBarActions,
|
||||
appBarScrollBehavior = enterAlwaysCollapsedScrollBehavior(
|
||||
state = rememberTopAppBarState(),
|
||||
canScroll = { listState.canScrollForward || listState.canScrollBackward },
|
||||
isAtTop = { listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0 },
|
||||
),
|
||||
) { innerPadding ->
|
||||
alertDialog.content?.let { it() }
|
||||
|
||||
PreferenceScreen(
|
||||
items = itemsProvider(),
|
||||
listState = listState,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package eu.kanade.tachiyomi.ui.more
|
||||
package yokai.presentation.settings.screen.about
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
|
@ -1,4 +1,4 @@
|
|||
package eu.kanade.tachiyomi.ui.more
|
||||
package yokai.presentation.settings.screen.about
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
|
@ -0,0 +1,239 @@
|
|||
package yokai.presentation.settings.screen.about
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Public
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import co.touchlab.kermit.Logger
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.core.storage.preference.asDateFormat
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateNotifier
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateResult
|
||||
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
|
||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||
import eu.kanade.tachiyomi.util.compose.currentOrThrow
|
||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||
import eu.kanade.tachiyomi.util.system.isOnline
|
||||
import eu.kanade.tachiyomi.util.system.localeContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.withUIContext
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
import kotlinx.coroutines.launch
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import yokai.i18n.MR
|
||||
import yokai.presentation.component.preference.widget.TextPreferenceWidget
|
||||
import yokai.presentation.core.components.LinkIcon
|
||||
import yokai.presentation.core.enterAlwaysCollapsedScrollBehavior
|
||||
import yokai.presentation.core.icons.CustomIcons
|
||||
import yokai.presentation.core.icons.Discord
|
||||
import yokai.presentation.core.icons.GitHub
|
||||
import yokai.presentation.settings.SettingsScaffold
|
||||
import yokai.util.Screen
|
||||
import yokai.util.lang.getString
|
||||
|
||||
class AboutScreen(private val showNewUpdateDialog: (String, String, Boolean?) -> Unit) : Screen() {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
val context = LocalContext.current
|
||||
val navigator = LocalNavigator.currentOrThrow
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val scope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
val dateFormat by lazy { preferences.dateFormatRaw().get().asDateFormat() }
|
||||
|
||||
SettingsScaffold(
|
||||
title = stringResource(MR.strings.about),
|
||||
snackbarHost = {
|
||||
SnackbarHost(hostState = snackbarHostState)
|
||||
},
|
||||
appBarScrollBehavior = enterAlwaysCollapsedScrollBehavior(
|
||||
state = rememberTopAppBarState(),
|
||||
canScroll = { listState.canScrollForward || listState.canScrollBackward },
|
||||
isAtTop = { listState.firstVisibleItemIndex == 0 && listState.firstVisibleItemScrollOffset == 0 },
|
||||
),
|
||||
content = { contentPadding ->
|
||||
LazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
state = listState,
|
||||
) {
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.whats_new_this_release),
|
||||
onPreferenceClick = {
|
||||
uriHandler.openUri(if (BuildConfig.DEBUG) SOURCE_URL else RELEASE_URL)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (BuildConfig.INCLUDE_UPDATER) {
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.check_for_updates),
|
||||
onPreferenceClick = {
|
||||
if (context.isOnline()) {
|
||||
scope.launch {
|
||||
context.checkVersion()
|
||||
}
|
||||
} else {
|
||||
context.toast(MR.strings.no_network_connection)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.version),
|
||||
subtitle = getVersionName(),
|
||||
onPreferenceClick = {
|
||||
val deviceInfo = CrashLogUtil(context.localeContext).getDebugInfo()
|
||||
val clipboard = context.getSystemService<ClipboardManager>()!!
|
||||
val appInfo = context.getString(MR.strings.app_info)
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText(appInfo, deviceInfo))
|
||||
scope.launch {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = context.getString(MR.strings._copied_to_clipboard, appInfo),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.version),
|
||||
subtitle = getFormattedBuildTime(dateFormat),
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
HorizontalDivider()
|
||||
|
||||
TextPreferenceWidget(
|
||||
title = stringResource(MR.strings.open_source_licenses),
|
||||
onPreferenceClick = { navigator.push(AboutLicenseScreen()) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
FlowRow(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
LinkIcon(
|
||||
label = "Website",
|
||||
icon = Icons.Outlined.Public,
|
||||
url = "https://mihon.app",
|
||||
)
|
||||
LinkIcon(
|
||||
label = "Discord",
|
||||
icon = CustomIcons.Discord,
|
||||
url = "https://discord.gg/mihon",
|
||||
)
|
||||
LinkIcon(
|
||||
label = "GitHub",
|
||||
icon = CustomIcons.GitHub,
|
||||
url = "https://github.com/null2264/yokai",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun getVersionName(): String = when {
|
||||
BuildConfig.DEBUG -> "Debug ${BuildConfig.COMMIT_SHA}"
|
||||
BuildConfig.NIGHTLY -> "Nightly ${BuildConfig.COMMIT_COUNT} (${BuildConfig.COMMIT_SHA})"
|
||||
else -> "Release ${BuildConfig.VERSION_NAME}"
|
||||
}
|
||||
|
||||
private suspend fun Context.checkVersion() {
|
||||
val updateChecker = AppUpdateChecker()
|
||||
|
||||
withUIContext { toast(MR.strings.searching_for_updates) }
|
||||
|
||||
val result = try {
|
||||
updateChecker.checkForUpdate(this, true)
|
||||
} catch (error: Exception) {
|
||||
withUIContext {
|
||||
toast(error.message)
|
||||
Logger.e(error) { "Couldn't check new update" }
|
||||
}
|
||||
}
|
||||
when (result) {
|
||||
is AppUpdateResult.NewUpdate -> {
|
||||
val body = result.release.info
|
||||
val url = result.release.downloadLink
|
||||
val isBeta = result.release.preRelease == true
|
||||
|
||||
// Create confirmation window
|
||||
withUIContext {
|
||||
AppUpdateNotifier.releasePageUrl = result.release.releaseLink
|
||||
showNewUpdateDialog(body, url, isBeta)
|
||||
}
|
||||
}
|
||||
is AppUpdateResult.NoNewUpdate -> {
|
||||
withUIContext {
|
||||
toast(MR.strings.no_new_updates_available)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFormattedBuildTime(dateFormat: DateFormat): String {
|
||||
try {
|
||||
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault())
|
||||
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
||||
val buildTime =
|
||||
inputDf.parse(BuildConfig.BUILD_TIME) ?: return BuildConfig.BUILD_TIME
|
||||
|
||||
return buildTime.toTimestampString(dateFormat)
|
||||
} catch (e: ParseException) {
|
||||
return BuildConfig.BUILD_TIME
|
||||
}
|
||||
}
|
||||
|
||||
private const val SOURCE_URL = "https://github.com/null2264/yokai/commits/master"
|
|
@ -0,0 +1,31 @@
|
|||
package yokai.presentation.core.components
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun LinkIcon(
|
||||
label: String,
|
||||
icon: ImageVector,
|
||||
url: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
IconButton(
|
||||
modifier = modifier.padding(4.dp),
|
||||
onClick = { uriHandler.openUri(url) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
contentDescription = label,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package yokai.presentation.core.icons
|
||||
|
||||
object CustomIcons
|
|
@ -0,0 +1,86 @@
|
|||
package yokai.presentation.core.icons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
|
||||
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.ImageVector.Builder
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Suppress("UnusedReceiverParameter", "BooleanLiteralArgument")
|
||||
val CustomIcons.Discord: ImageVector
|
||||
get() {
|
||||
if (_discord != null) {
|
||||
return _discord!!
|
||||
}
|
||||
_discord = Builder(
|
||||
name = "Discord",
|
||||
defaultWidth = 24.0.dp,
|
||||
defaultHeight = 24.0.dp,
|
||||
viewportWidth = 24.0f,
|
||||
viewportHeight = 24.0f,
|
||||
).apply {
|
||||
path(
|
||||
fill = SolidColor(Color(0xFF000000)),
|
||||
stroke = null,
|
||||
strokeLineWidth = 0.0f,
|
||||
strokeLineCap = Butt,
|
||||
strokeLineJoin = Miter,
|
||||
strokeLineMiter = 4.0f,
|
||||
pathFillType = NonZero,
|
||||
) {
|
||||
moveTo(20.317f, 4.3698f)
|
||||
arcToRelative(19.7913f, 19.7913f, 0.0f, false, false, -4.8851f, -1.5152f)
|
||||
arcToRelative(0.0741f, 0.0741f, 0.0f, false, false, -0.0785f, 0.0371f)
|
||||
curveToRelative(-0.211f, 0.3753f, -0.4447f, 0.8648f, -0.6083f, 1.2495f)
|
||||
curveToRelative(-1.8447f, -0.2762f, -3.68f, -0.2762f, -5.4868f, 0.0f)
|
||||
curveToRelative(-0.1636f, -0.3933f, -0.4058f, -0.8742f, -0.6177f, -1.2495f)
|
||||
arcToRelative(0.077f, 0.077f, 0.0f, false, false, -0.0785f, -0.037f)
|
||||
arcToRelative(19.7363f, 19.7363f, 0.0f, false, false, -4.8852f, 1.515f)
|
||||
arcToRelative(0.0699f, 0.0699f, 0.0f, false, false, -0.0321f, 0.0277f)
|
||||
curveTo(0.5334f, 9.0458f, -0.319f, 13.5799f, 0.0992f, 18.0578f)
|
||||
arcToRelative(0.0824f, 0.0824f, 0.0f, false, false, 0.0312f, 0.0561f)
|
||||
curveToRelative(2.0528f, 1.5076f, 4.0413f, 2.4228f, 5.9929f, 3.0294f)
|
||||
arcToRelative(0.0777f, 0.0777f, 0.0f, false, false, 0.0842f, -0.0276f)
|
||||
curveToRelative(0.4616f, -0.6304f, 0.8731f, -1.2952f, 1.226f, -1.9942f)
|
||||
arcToRelative(0.076f, 0.076f, 0.0f, false, false, -0.0416f, -0.1057f)
|
||||
curveToRelative(-0.6528f, -0.2476f, -1.2743f, -0.5495f, -1.8722f, -0.8923f)
|
||||
arcToRelative(0.077f, 0.077f, 0.0f, false, true, -0.0076f, -0.1277f)
|
||||
curveToRelative(0.1258f, -0.0943f, 0.2517f, -0.1923f, 0.3718f, -0.2914f)
|
||||
arcToRelative(0.0743f, 0.0743f, 0.0f, false, true, 0.0776f, -0.0105f)
|
||||
curveToRelative(3.9278f, 1.7933f, 8.18f, 1.7933f, 12.0614f, 0.0f)
|
||||
arcToRelative(0.0739f, 0.0739f, 0.0f, false, true, 0.0785f, 0.0095f)
|
||||
curveToRelative(0.1202f, 0.099f, 0.246f, 0.1981f, 0.3728f, 0.2924f)
|
||||
arcToRelative(0.077f, 0.077f, 0.0f, false, true, -0.0066f, 0.1276f)
|
||||
arcToRelative(12.2986f, 12.2986f, 0.0f, false, true, -1.873f, 0.8914f)
|
||||
arcToRelative(0.0766f, 0.0766f, 0.0f, false, false, -0.0407f, 0.1067f)
|
||||
curveToRelative(0.3604f, 0.698f, 0.7719f, 1.3628f, 1.225f, 1.9932f)
|
||||
arcToRelative(0.076f, 0.076f, 0.0f, false, false, 0.0842f, 0.0286f)
|
||||
curveToRelative(1.961f, -0.6067f, 3.9495f, -1.5219f, 6.0023f, -3.0294f)
|
||||
arcToRelative(0.077f, 0.077f, 0.0f, false, false, 0.0313f, -0.0552f)
|
||||
curveToRelative(0.5004f, -5.177f, -0.8382f, -9.6739f, -3.5485f, -13.6604f)
|
||||
arcToRelative(0.061f, 0.061f, 0.0f, false, false, -0.0312f, -0.0286f)
|
||||
close()
|
||||
moveTo(8.02f, 15.3312f)
|
||||
curveToRelative(-1.1825f, 0.0f, -2.1569f, -1.0857f, -2.1569f, -2.419f)
|
||||
curveToRelative(0.0f, -1.3332f, 0.9555f, -2.4189f, 2.157f, -2.4189f)
|
||||
curveToRelative(1.2108f, 0.0f, 2.1757f, 1.0952f, 2.1568f, 2.419f)
|
||||
curveToRelative(0.0f, 1.3332f, -0.9555f, 2.4189f, -2.1569f, 2.4189f)
|
||||
close()
|
||||
moveTo(15.9948f, 15.3312f)
|
||||
curveToRelative(-1.1825f, 0.0f, -2.1569f, -1.0857f, -2.1569f, -2.419f)
|
||||
curveToRelative(0.0f, -1.3332f, 0.9554f, -2.4189f, 2.1569f, -2.4189f)
|
||||
curveToRelative(1.2108f, 0.0f, 2.1757f, 1.0952f, 2.1568f, 2.419f)
|
||||
curveToRelative(0.0f, 1.3332f, -0.946f, 2.4189f, -2.1568f, 2.4189f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
return _discord!!
|
||||
}
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private var _discord: ImageVector? = null
|
|
@ -0,0 +1,68 @@
|
|||
package yokai.presentation.core.icons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
|
||||
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.ImageVector.Builder
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Suppress("UnusedReceiverParameter")
|
||||
val CustomIcons.GitHub: ImageVector
|
||||
get() {
|
||||
if (_github != null) {
|
||||
return _github!!
|
||||
}
|
||||
_github = Builder(
|
||||
name = "GitHub",
|
||||
defaultWidth = 24.0.dp,
|
||||
defaultHeight = 24.0.dp,
|
||||
viewportWidth = 24.0f,
|
||||
viewportHeight = 24.0f,
|
||||
).apply {
|
||||
path(
|
||||
fill = SolidColor(Color(0xFF000000)),
|
||||
stroke = null,
|
||||
strokeLineWidth = 0.0f,
|
||||
strokeLineCap = Butt,
|
||||
strokeLineJoin = Miter,
|
||||
strokeLineMiter = 4.0f,
|
||||
pathFillType = NonZero,
|
||||
) {
|
||||
moveTo(12.0f, 0.297f)
|
||||
curveToRelative(-6.63f, 0.0f, -12.0f, 5.373f, -12.0f, 12.0f)
|
||||
curveToRelative(0.0f, 5.303f, 3.438f, 9.8f, 8.205f, 11.385f)
|
||||
curveToRelative(0.6f, 0.113f, 0.82f, -0.258f, 0.82f, -0.577f)
|
||||
curveToRelative(0.0f, -0.285f, -0.01f, -1.04f, -0.015f, -2.04f)
|
||||
curveToRelative(-3.338f, 0.724f, -4.042f, -1.61f, -4.042f, -1.61f)
|
||||
curveTo(4.422f, 18.07f, 3.633f, 17.7f, 3.633f, 17.7f)
|
||||
curveToRelative(-1.087f, -0.744f, 0.084f, -0.729f, 0.084f, -0.729f)
|
||||
curveToRelative(1.205f, 0.084f, 1.838f, 1.236f, 1.838f, 1.236f)
|
||||
curveToRelative(1.07f, 1.835f, 2.809f, 1.305f, 3.495f, 0.998f)
|
||||
curveToRelative(0.108f, -0.776f, 0.417f, -1.305f, 0.76f, -1.605f)
|
||||
curveToRelative(-2.665f, -0.3f, -5.466f, -1.332f, -5.466f, -5.93f)
|
||||
curveToRelative(0.0f, -1.31f, 0.465f, -2.38f, 1.235f, -3.22f)
|
||||
curveToRelative(-0.135f, -0.303f, -0.54f, -1.523f, 0.105f, -3.176f)
|
||||
curveToRelative(0.0f, 0.0f, 1.005f, -0.322f, 3.3f, 1.23f)
|
||||
curveToRelative(0.96f, -0.267f, 1.98f, -0.399f, 3.0f, -0.405f)
|
||||
curveToRelative(1.02f, 0.006f, 2.04f, 0.138f, 3.0f, 0.405f)
|
||||
curveToRelative(2.28f, -1.552f, 3.285f, -1.23f, 3.285f, -1.23f)
|
||||
curveToRelative(0.645f, 1.653f, 0.24f, 2.873f, 0.12f, 3.176f)
|
||||
curveToRelative(0.765f, 0.84f, 1.23f, 1.91f, 1.23f, 3.22f)
|
||||
curveToRelative(0.0f, 4.61f, -2.805f, 5.625f, -5.475f, 5.92f)
|
||||
curveToRelative(0.42f, 0.36f, 0.81f, 1.096f, 0.81f, 2.22f)
|
||||
curveToRelative(0.0f, 1.606f, -0.015f, 2.896f, -0.015f, 3.286f)
|
||||
curveToRelative(0.0f, 0.315f, 0.21f, 0.69f, 0.825f, 0.57f)
|
||||
curveTo(20.565f, 22.092f, 24.0f, 17.592f, 24.0f, 12.297f)
|
||||
curveToRelative(0.0f, -6.627f, -5.373f, -12.0f, -12.0f, -12.0f)
|
||||
}
|
||||
}
|
||||
.build()
|
||||
return _github!!
|
||||
}
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private var _github: ImageVector? = null
|
|
@ -0,0 +1,56 @@
|
|||
package yokai.presentation.core.icons
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
|
||||
import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.ImageVector.Builder
|
||||
import androidx.compose.ui.graphics.vector.path
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Suppress("UnusedReceiverParameter")
|
||||
val CustomIcons.LocalSource: ImageVector
|
||||
get() {
|
||||
if (_localSource != null) {
|
||||
return _localSource!!
|
||||
}
|
||||
_localSource = Builder(
|
||||
name = "localSource",
|
||||
defaultWidth = 24.0.dp,
|
||||
defaultHeight = 24.0.dp,
|
||||
viewportWidth = 24.0f,
|
||||
viewportHeight = 24.0f,
|
||||
).apply {
|
||||
path(
|
||||
fill = SolidColor(Color(0xFF000000)),
|
||||
stroke = null,
|
||||
strokeLineWidth = 0.0f,
|
||||
strokeLineCap = Butt,
|
||||
strokeLineJoin = Miter,
|
||||
strokeLineMiter = 4.0f,
|
||||
pathFillType = NonZero,
|
||||
) {
|
||||
moveTo(12f, 11.55f)
|
||||
curveTo(9.64f, 9.35f, 6.48f, 8f, 3f, 8f)
|
||||
verticalLineToRelative(11f)
|
||||
curveToRelative(3.48f, 0f, 6.64f, 1.35f, 9f, 3.55f)
|
||||
curveToRelative(2.36f, -2.19f, 5.52f, -3.55f, 9f, -3.55f)
|
||||
verticalLineTo(8f)
|
||||
curveToRelative(-3.48f, 0f, -6.64f, 1.35f, -9f, 3.55f)
|
||||
close()
|
||||
moveTo(12f, 8f)
|
||||
curveToRelative(1.66f, 0f, 3f, -1.34f, 3f, -3f)
|
||||
reflectiveCurveToRelative(-1.34f, -3f, -3f, -3f)
|
||||
reflectiveCurveToRelative(-3f, 1.34f, -3f, 3f)
|
||||
reflectiveCurveToRelative(1.34f, 3f, 3f, 3f)
|
||||
close()
|
||||
}
|
||||
}
|
||||
.build()
|
||||
return _localSource!!
|
||||
}
|
||||
|
||||
@Suppress("ObjectPropertyName")
|
||||
private var _localSource: ImageVector? = null
|
Loading…
Add table
Add a link
Reference in a new issue