feat: Start using moko for i18n

This commit is contained in:
Ahmad Ansori Palembani 2024-06-16 07:18:12 +07:00
parent 872cc92c93
commit 6d531fbb5c
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
19 changed files with 171 additions and 44 deletions

View file

@ -9,16 +9,18 @@ kotlin {
sourceSets {
val commonMain by getting {
dependencies {
api(libs.okhttp)
api(libs.okhttp.logging.interceptor)
api(libs.okhttp.dnsoverhttps)
api(libs.okhttp.brotli)
api(libs.okio)
implementation(projects.i18n)
api(libs.bundles.logging)
}
}
val androidMain by getting {
dependencies {
api(libs.okhttp)
api(libs.okhttp.logging.interceptor)
api(libs.okhttp.dnsoverhttps)
api(libs.okhttp.brotli)
api(libs.okio)
api(androidx.core)
api(androidx.annotation)
api(libs.rxjava)

View file

@ -5,7 +5,6 @@ import android.content.Context
import android.webkit.WebView
import android.widget.Toast
import androidx.core.content.ContextCompat
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.network.AndroidCookieJar
import eu.kanade.tachiyomi.util.system.WebViewClientCompat
import eu.kanade.tachiyomi.util.system.isOutdated
@ -15,6 +14,8 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import yokai.i18n.MR
import yokai.util.lang.getMString
import java.io.IOException
import java.util.concurrent.*
@ -48,7 +49,7 @@ class CloudflareInterceptor(
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
// we don't crash the entire app
catch (e: CloudflareBypassException) {
throw IOException(context.getString(R.string.failed_to_bypass_cloudflare))
throw IOException(context.getMString(MR.strings.failed_to_bypass_cloudflare))
} catch (e: Exception) {
throw IOException(e)
}
@ -130,7 +131,7 @@ class CloudflareInterceptor(
if (!cloudflareBypassed) {
// Prompt user to update WebView if it seems too outdated
if (isWebViewOutdated) {
context.toast(R.string.please_update_webview, Toast.LENGTH_LONG)
context.toast(MR.strings.please_update_webview, Toast.LENGTH_LONG)
}
throw CloudflareBypassException()

View file

@ -5,7 +5,6 @@ import android.os.Build
import android.webkit.WebSettings
import android.webkit.WebView
import android.widget.Toast
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.launchUI
@ -15,6 +14,7 @@ import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import yokai.i18n.MR
import java.util.*
import java.util.concurrent.*
@ -56,7 +56,7 @@ abstract class WebViewInterceptor(
if (!WebViewUtil.supportsWebView(context)) {
launchUI {
context.toast(R.string.webview_is_required, Toast.LENGTH_LONG)
context.toast(MR.strings.webview_is_required, Toast.LENGTH_LONG)
}
return response
}

View file

@ -0,0 +1,28 @@
package eu.kanade.tachiyomi.util.system
import android.content.res.Resources
import android.util.TypedValue
/**
* Converts to dp.
*/
val Int.pxToDp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
val Float.pxToDp: Float
get() = (this / Resources.getSystem().displayMetrics.density)
/**
* Converts to px.
*/
val Int.dpToPx: Int
get() = this.toFloat().dpToPx.toInt()
val Int.spToPx: Int
get() = this.toFloat().spToPx.toInt()
val Float.spToPx: Float
get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, this, Resources.getSystem().displayMetrics)
val Float.dpToPx: Float
get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)

View file

@ -0,0 +1,199 @@
package eu.kanade.tachiyomi.util.system
import android.annotation.SuppressLint
import android.app.Activity
import android.hardware.display.DisplayManager
import android.os.Build
import android.view.Display
import android.view.Window
import androidx.core.content.getSystemService
import androidx.core.view.WindowInsetsCompat
import co.touchlab.kermit.Logger
object DeviceUtil {
val isMiui by lazy {
getSystemProperty("ro.miui.ui.version.name")?.isNotEmpty() ?: false
}
/**
* Extracts the MIUI major version code from a string like "V12.5.3.0.QFGMIXM".
*
* @return MIUI major version code (e.g., 13) or null if can't be parsed.
*/
val miuiMajorVersion by lazy {
if (!isMiui) return@lazy null
Build.VERSION.INCREMENTAL
.substringBefore('.')
.trimStart('V')
.toIntOrNull()
}
@SuppressLint("PrivateApi")
fun isMiuiOptimizationDisabled(): Boolean {
val sysProp = getSystemProperty("persist.sys.miui_optimization")
if (sysProp == "0" || sysProp == "false") {
return true
}
return try {
Class.forName("android.miui.AppOpsUtils")
.getDeclaredMethod("isXOptMode")
.invoke(null) as Boolean
} catch (e: Exception) {
false
}
}
val isSamsung by lazy {
Build.MANUFACTURER.equals("samsung", ignoreCase = true)
}
val oneUiVersion by lazy {
try {
val semPlatformIntField = Build.VERSION::class.java.getDeclaredField("SEM_PLATFORM_INT")
val version = semPlatformIntField.getInt(null) - 90000
if (version < 0) {
1.0
} else {
((version / 10000).toString() + "." + version % 10000 / 100).toDouble()
}
} catch (e: Exception) {
null
}
}
val invalidDefaultBrowsers = listOf(
"android",
"com.huawei.android.internal.app",
"com.zui.resolver",
)
@SuppressLint("PrivateApi")
private fun getSystemProperty(key: String?): String? {
return try {
Class.forName("android.os.SystemProperties")
.getDeclaredMethod("get", String::class.java)
.invoke(null, key) as String
} catch (e: Exception) {
Logger.w(e) { "Unable to use SystemProperties.get" }
null
}
}
val isVivo by lazy {
val prop = getSystemProperty("ro.vivo.os.name")
!prop.isNullOrBlank() &&
prop.contains("funtouch", true)
}
fun setLegacyCutoutMode(window: Window, mode: LegacyCutoutMode) {
when (mode) {
LegacyCutoutMode.SHORT_EDGES -> {
// Vivo doesn't support this, user had to set it from Settings
/*
if (isVivo) {
}
*/
}
LegacyCutoutMode.NEVER -> {
// Vivo doesn't support this, user had to set it from Settings
/*
if (isVivo) {
}
*/
}
}
}
fun hasCutout(context: Activity?): CutoutSupport {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (context?.getSystemService<DisplayManager>()
?.getDisplay(Display.DEFAULT_DISPLAY)?.cutout != null)
return CutoutSupport.EXTENDED
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val displayCutout = context?.window?.decorView?.rootWindowInsets?.displayCutout
if (displayCutout?.safeInsetTop != null || displayCutout?.safeInsetBottom != null)
return CutoutSupport.MODERN
} else if (isVivo) {
// https://swsdl.vivo.com.cn/appstore/developer/uploadfile/20180328/20180328152252602.pdf
try {
@SuppressLint("PrivateApi")
val ftFeature = context?.classLoader
?.loadClass("android.util.FtFeature")
val isFeatureSupportMethod = ftFeature?.getMethod(
"isFeatureSupport",
Int::class.javaPrimitiveType,
)
val isNotchOnScreen = 0x00000020
val isSupported = isFeatureSupportMethod?.invoke(ftFeature, isNotchOnScreen) as Boolean
if (isSupported) return CutoutSupport.LEGACY
} catch (_: Exception) {
}
} else if (isMiui) {
try {
@SuppressLint("PrivateApi")
val sysProp = context?.classLoader?.loadClass("android.os.SystemProperties")
val method = sysProp?.getMethod("getInt", String::class.java, Int::class.javaPrimitiveType)
val rt = method?.invoke(sysProp, "ro.miui.notch", 0) as Int
if (rt == 1) return CutoutSupport.LEGACY
} catch (_: Exception) {
}
}
return CutoutSupport.NONE
}
fun getCutoutHeight(context: Activity?, cutoutSupport: CutoutSupport): Number {
return when (cutoutSupport) {
CutoutSupport.MODERN -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P)
throw IllegalStateException("Modern cutout only available on Android P or higher")
context?.window?.decorView?.rootWindowInsets?.displayCutout?.safeInsetTop ?: 0
}
CutoutSupport.EXTENDED -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
throw IllegalStateException("Extended cutout only available on Android Q or higher")
context?.window?.decorView?.rootWindowInsets?.displayCutout?.boundingRectTop?.height()?.toFloat() ?: 0f
}
CutoutSupport.LEGACY -> {
if (isVivo) {
val insetCompat = context?.window?.decorView?.rootWindowInsets?.let {
WindowInsetsCompat.toWindowInsetsCompat(it)
}
val statusBarHeight = insetCompat?.getInsets(WindowInsetsCompat.Type.statusBars())?.top
?: 24.dpToPx // 24dp is "standard" height for Android since Marshmallow
var notchHeight = 32.dpToPx
if (notchHeight < statusBarHeight) {
notchHeight = statusBarHeight
}
notchHeight
} else if (isMiui) {
val resourceId = context?.resources?.getIdentifier("notch_height",
"dimen", "android") ?: 0
if (resourceId > 0) {
context?.resources?.getDimensionPixelSize(resourceId) ?: 0
} else {
0
}
} else {
0
}
}
else -> 0
}
}
enum class CutoutSupport {
NONE,
LEGACY, // Pre-Android P, the start of this hell
MODERN, // Android P
EXTENDED, // Android Q
}
enum class LegacyCutoutMode {
SHORT_EDGES,
NEVER,
}
}

View file

@ -3,6 +3,8 @@ package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.widget.Toast
import androidx.annotation.StringRes
import dev.icerock.moko.resources.StringResource
import yokai.util.lang.getMString
/**
* Display a toast in this context.
@ -14,6 +16,16 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT)
Toast.makeText(this, resource, duration).show()
}
/**
* Display a toast in this context.
*
* @param resource the text resource.
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(resource: StringResource, duration: Int = Toast.LENGTH_SHORT) {
toast(getMString(resource), duration)
}
/**
* Display a toast in this context.
*

View file

@ -0,0 +1,8 @@
package yokai.util.lang
import android.content.Context
import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.desc.Resource
import dev.icerock.moko.resources.desc.StringDesc
fun Context.getMString(stringRes: StringResource): String = StringDesc.Resource(stringRes).toString(this)

View file

@ -1,13 +1,10 @@
package eu.kanade.tachiyomi.network
import android.os.Build
import androidx.annotation.RequiresApi
import eu.kanade.tachiyomi.core.preference.PreferenceStore
class NetworkPreferences(private val preferenceStore: PreferenceStore) {
fun dohProvider() = preferenceStore.getInt("doh_provider", -1)
@RequiresApi(Build.VERSION_CODES.GINGERBREAD)
fun defaultUserAgent() = preferenceStore.getString("default_user_agent", NetworkHelper.DEFAULT_USER_AGENT)
}