mirror of
https://gitlab.futo.org/keyboard/latinime.git
synced 2024-09-28 14:54:30 +01:00
Add payment menu
This commit is contained in:
parent
5c9bada7ae
commit
7d5b12feaf
@ -169,5 +169,27 @@
|
||||
android:value="Neural network model training"/>
|
||||
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".payment.PaymentCompleteActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/payment_complete"
|
||||
android:clearTaskOnLaunch="false"
|
||||
android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="futo-voice-input" />
|
||||
<data android:scheme="futo-keyboard" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".payment.PaymentActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/payment"
|
||||
android:clearTaskOnLaunch="false"
|
||||
android:launchMode="singleInstance" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
20
java/res/drawable/dollar_sign.xml
Normal file
20
java/res/drawable/dollar_sign.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,1L12,23"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M17,5H9.5a3.5,3.5 0,0 0,0 7h5a3.5,3.5 0,0 1,0 7H6"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#ffffff"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
@ -45,4 +45,31 @@
|
||||
<string name="try_typing">Try typing here…</string>
|
||||
<string name="externally_imported_model">Externally imported model</string>
|
||||
<string name="spacebar_default_text">Pre-Alpha</string>
|
||||
|
||||
<string name="payment_text_1">You\'ve been using FUTO Keyboard for <xliff:g name="days" example="30">%d</xliff:g> days. If you find this app useful, please consider paying to support future development of FUTO software.</string>
|
||||
<string name="payment_text_1_alt">Thank you for trying out FUTO Keyboard! If you find this app useful, please consider paying to support future development of FUTO software.</string>
|
||||
<string name="payment_text_2">FUTO is dedicated to making good software that doesn\'t abuse you. This app will never serve you ads or collect your personal data.</string>
|
||||
<string name="thank_you_for_purchasing_keyboard">Thank you for purchasing FUTO Keyboard!</string>
|
||||
<string name="payment_pending_body">Your payment is still pending, but it should clear soon.</string>
|
||||
<string name="purchase_will_help_body">Your purchase will help continued development of FUTO Keyboard, and other FUTO projects.</string>
|
||||
<string name="thank_you">Thank you</string>
|
||||
<string name="payment_pending">Payment Pending</string>
|
||||
<string name="payment_failed_body">Unfortunately, your payment has failed for one reason or another. Please contact us if you need help. You can reach us at keyboard@futo.org, with the button below, or with Send Feedback in Help</string>
|
||||
<string name="payment_failed_body_ex">There may have been a problem with your payment. If it did go through, you should receive an email with a license key. Please contact us if you need help. You can reach us at keyboard@futo.org, with the button below, or with Share Feedback at the main screen.</string>
|
||||
<string name="pay_via_x">Pay via <xliff:g name="paymentMethod" example="Play Store">%s</xliff:g></string>
|
||||
<string name="pay">Pay</string>
|
||||
<string name="i_already_paid">I already paid</string>
|
||||
<string name="i_already_paid_2">I already paid (tap again)</string>
|
||||
<string name="remind_me_in_x">"Remind me in "</string>
|
||||
<string name="in_x_days">" days"</string>
|
||||
<string name="developer_mode_payment_methods">You are on the Developer release, so you are seeing all payment methods</string>
|
||||
<string name="pay_now">Pay Now</string>
|
||||
<string name="unpaid_indicator">Unpaid</string>
|
||||
<string name="unpaid_title">Unpaid FUTO Keyboard</string>
|
||||
<string name="payment_error">Payment Error</string>
|
||||
<string name="payment_complete">Payment Complete</string>
|
||||
<string name="payment_title">Payment</string>
|
||||
<string name="payment">Payment</string>
|
||||
<string name="license_check_failed">Failed to verify license. If you believe this to be an error, please contact us.</string>
|
||||
<string name="thank_you_for_using_paid">Thank you for using the paid version of FUTO Keyboard!</string>
|
||||
</resources>
|
@ -0,0 +1,43 @@
|
||||
package org.futo.inputmethod.latin.payment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import kotlinx.coroutines.launch
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.PaymentScreenSwitch
|
||||
import org.futo.inputmethod.latin.uix.theme.UixThemeAuto
|
||||
|
||||
|
||||
class PaymentActivity : ComponentActivity() {
|
||||
private fun updateContent() {
|
||||
setContent {
|
||||
UixThemeAuto {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
PaymentScreenSwitch(onExit = {
|
||||
finish()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package org.futo.inputmethod.latin.payment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import kotlinx.coroutines.launch
|
||||
import org.futo.inputmethod.latin.R
|
||||
import org.futo.inputmethod.latin.uix.dataStore
|
||||
import org.futo.inputmethod.latin.uix.getSetting
|
||||
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
||||
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
||||
import org.futo.inputmethod.latin.uix.settings.SettingsActivity
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.EXT_LICENSE_KEY
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.IS_ALREADY_PAID
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.IS_PAYMENT_PENDING
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.PaymentThankYouScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.startAppActivity
|
||||
import org.futo.inputmethod.latin.uix.theme.UixThemeAuto
|
||||
import org.futo.inputmethod.updates.openURI
|
||||
|
||||
class PaymentCompleteActivity : ComponentActivity() {
|
||||
private fun updateContent() {
|
||||
setContent {
|
||||
UixThemeAuto {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
PaymentThankYouScreen(onExit = {
|
||||
startAppActivity(SettingsActivity::class.java, clearTop = true)
|
||||
finish()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onPaid(license: String) {
|
||||
lifecycleScope.launch {
|
||||
dataStore.edit {
|
||||
it[IS_ALREADY_PAID.key] = true
|
||||
it[IS_PAYMENT_PENDING.key] = false
|
||||
it[EXT_LICENSE_KEY.key] = license
|
||||
}
|
||||
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
updateContent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onInvalidKey() {
|
||||
lifecycleScope.launch {
|
||||
if(applicationContext.getSetting(IS_ALREADY_PAID)) {
|
||||
finish()
|
||||
} else {
|
||||
setContent {
|
||||
UixThemeAuto {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
getString(R.string.license_check_failed),
|
||||
modifier = Modifier.padding(8.dp)
|
||||
)
|
||||
|
||||
NavigationItem(title = "Email keyboard@futo.org", style = NavigationItemStyle.Mail, navigate = {
|
||||
openURI("mailto:keyboard@futo.org")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val targetData = intent.dataString
|
||||
if((targetData?.startsWith("futo-keyboard://license/") == true) || (targetData?.startsWith("futo-voice-input://license/") == true)) {
|
||||
/*if(StatePayment.instance.setPaymentLicenseUrl(targetData)) {
|
||||
onPaid(targetData)
|
||||
} else {
|
||||
onInvalidKey()
|
||||
}*/
|
||||
TODO()
|
||||
} else {
|
||||
Log.e("PaymentCompleteActivity", "futo-keyboard launched with invalid targetData $targetData")
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowForward
|
||||
import androidx.compose.material.icons.filled.Send
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
@ -397,7 +398,8 @@ enum class NavigationItemStyle {
|
||||
HomeSecondary,
|
||||
HomeTertiary,
|
||||
MiscNoArrow,
|
||||
Misc
|
||||
Misc,
|
||||
Mail
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -412,15 +414,19 @@ fun NavigationItem(title: String, style: NavigationItemStyle, navigate: () -> Un
|
||||
NavigationItemStyle.HomePrimary -> MaterialTheme.colorScheme.primaryContainer
|
||||
NavigationItemStyle.HomeSecondary -> MaterialTheme.colorScheme.secondaryContainer
|
||||
NavigationItemStyle.HomeTertiary -> MaterialTheme.colorScheme.tertiaryContainer
|
||||
NavigationItemStyle.MiscNoArrow -> Color.Transparent
|
||||
NavigationItemStyle.Misc -> Color.Transparent
|
||||
|
||||
NavigationItemStyle.MiscNoArrow,
|
||||
NavigationItemStyle.Misc,
|
||||
NavigationItemStyle.Mail -> Color.Transparent
|
||||
}
|
||||
|
||||
val iconColor = when(style) {
|
||||
NavigationItemStyle.HomePrimary -> MaterialTheme.colorScheme.onPrimaryContainer
|
||||
NavigationItemStyle.HomeSecondary -> MaterialTheme.colorScheme.onSecondaryContainer
|
||||
NavigationItemStyle.HomeTertiary -> MaterialTheme.colorScheme.onTertiaryContainer
|
||||
NavigationItemStyle.MiscNoArrow -> MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f)
|
||||
|
||||
NavigationItemStyle.MiscNoArrow,
|
||||
NavigationItemStyle.Mail,
|
||||
NavigationItemStyle.Misc -> MaterialTheme.colorScheme.onBackground.copy(alpha = 0.75f)
|
||||
}
|
||||
|
||||
@ -440,6 +446,7 @@ fun NavigationItem(title: String, style: NavigationItemStyle, navigate: () -> Un
|
||||
) {
|
||||
when(style) {
|
||||
NavigationItemStyle.Misc -> Icon(Icons.Default.ArrowForward, contentDescription = "Go")
|
||||
NavigationItemStyle.Mail -> Icon(Icons.Default.Send, contentDescription = "Send")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,14 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.futo.inputmethod.latin.uix.SettingsKey
|
||||
import org.futo.inputmethod.latin.uix.dataStore
|
||||
import org.futo.inputmethod.latin.uix.getSetting
|
||||
|
||||
data class DataStoreItem<T>(val value: T, val setValue: (T) -> Job)
|
||||
|
||||
@Composable
|
||||
fun <T> useDataStoreValueNullable(key: Preferences.Key<T>, default: T): T? {
|
||||
fun <T> useDataStoreValueNullable(key: Preferences.Key<T>, default: T): T {
|
||||
val context = LocalContext.current
|
||||
|
||||
val initialValue = remember {
|
||||
@ -48,6 +49,11 @@ fun <T> useDataStoreValueNullable(key: Preferences.Key<T>, default: T): T? {
|
||||
return valueFlow.collectAsState(initial = initialValue).value
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> useDataStoreValueNullable(v: SettingsKey<T>): T {
|
||||
return useDataStoreValueNullable(key = v.key, default = v.default)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> useDataStore(key: Preferences.Key<T>, default: T): DataStoreItem<T> {
|
||||
val context = LocalContext.current
|
||||
@ -72,6 +78,12 @@ fun <T> useDataStore(key: Preferences.Key<T>, default: T): DataStoreItem<T> {
|
||||
return DataStoreItem(value, setValue)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun <T> useDataStore(key: SettingsKey<T>): DataStoreItem<T> {
|
||||
return useDataStore(key.key, key.default)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun useSharedPrefsBool(key: String, default: Boolean): DataStoreItem<Boolean> {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
@ -17,6 +17,8 @@ import org.futo.inputmethod.latin.uix.settings.pages.DeveloperScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.HelpScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.HomeScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.LanguagesScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.PaymentScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.PaymentThankYouScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.PredictiveTextScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.ThemeScreen
|
||||
import org.futo.inputmethod.latin.uix.settings.pages.TypingScreen
|
||||
@ -54,6 +56,8 @@ fun SettingsNavigator(
|
||||
composable("help") { HelpScreen(navController) }
|
||||
composable("developer") { DeveloperScreen(navController) }
|
||||
composable("blacklist") { BlacklistScreen(navController) }
|
||||
composable("payment") { PaymentScreen(navController) { navController.navigateUp() } }
|
||||
composable("paid") { PaymentThankYouScreen { navController.navigateUp() } }
|
||||
dialog("error/{title}/{body}") {
|
||||
ErrorDialog(
|
||||
it.arguments?.getString("title")?.urlDecode() ?: stringResource(R.string.unknown_error),
|
||||
|
@ -17,7 +17,7 @@ fun AdvancedParametersScreen(navController: NavHostController = rememberNavContr
|
||||
ScrollableList {
|
||||
ScreenTitle("Advanced Parameters", showBack = true, navController)
|
||||
|
||||
Tip("Options below are experimental and may be removed or changed in the future as internal workings of the app change. Changing these values may have an adverse impact on your experience.")
|
||||
Tip("Options below are experimental and may be removed or changed in the future as internal workings of the app change. Changing these values may have an adverse impact on your experience.\n\nNote: These only affect English")
|
||||
|
||||
SettingSlider(
|
||||
title = "Transformer LM strength",
|
||||
|
@ -21,6 +21,8 @@ import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
||||
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
||||
import org.futo.inputmethod.latin.uix.settings.ScrollableList
|
||||
import org.futo.inputmethod.latin.uix.settings.SettingToggleDataStore
|
||||
import org.futo.inputmethod.latin.uix.settings.SettingToggleRaw
|
||||
import org.futo.inputmethod.latin.uix.settings.useDataStore
|
||||
|
||||
|
||||
val IS_DEVELOPER = booleanPreferencesKey("isDeveloperMode")
|
||||
@ -53,5 +55,49 @@ fun DeveloperScreen(navController: NavHostController = rememberNavController())
|
||||
},
|
||||
icon = painterResource(id = R.drawable.close)
|
||||
)
|
||||
|
||||
ScreenTitle(title = "Payment stuff")
|
||||
|
||||
SettingToggleDataStore(title = "Is paid", setting = IS_ALREADY_PAID)
|
||||
SettingToggleDataStore(title = "Is payment pending", setting = IS_PAYMENT_PENDING)
|
||||
SettingToggleDataStore(title = "Has seen paid notice", setting = HAS_SEEN_PAID_NOTICE)
|
||||
SettingToggleDataStore(title = "Force show notice", setting = FORCE_SHOW_NOTICE)
|
||||
|
||||
val reminder = useDataStore(NOTICE_REMINDER_TIME)
|
||||
val currTime = System.currentTimeMillis() / 1000L
|
||||
|
||||
val subtitleValue = if (reminder.value > currTime) {
|
||||
val diffDays = (reminder.value - currTime) / 60.0 / 60.0 / 24.0
|
||||
"Reminding in ${"%.2f".format(diffDays)} days"
|
||||
} else {
|
||||
"Reminder unset"
|
||||
}
|
||||
SettingToggleRaw(
|
||||
"Reminder Time",
|
||||
reminder.value > currTime,
|
||||
{
|
||||
if (!it) {
|
||||
reminder.setValue(0L)
|
||||
}
|
||||
},
|
||||
subtitleValue,
|
||||
reminder.value <= currTime,
|
||||
{ }
|
||||
)
|
||||
|
||||
val licenseKey = useDataStore(EXT_LICENSE_KEY)
|
||||
SettingToggleRaw(
|
||||
"Ext License Key",
|
||||
licenseKey.value != EXT_LICENSE_KEY.default,
|
||||
{
|
||||
if(!it) {
|
||||
licenseKey.setValue(EXT_LICENSE_KEY.default)
|
||||
}
|
||||
},
|
||||
licenseKey.value,
|
||||
licenseKey.value == EXT_LICENSE_KEY.default,
|
||||
{ }
|
||||
)
|
||||
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ fun HelpScreen(navController: NavHostController = rememberNavController()) {
|
||||
NavigationItem(title = "FUTO Chat", style = NavigationItemStyle.Misc, navigate = {
|
||||
context.openURI("https://chat.futo.org/")
|
||||
})
|
||||
NavigationItem(title = "Email keyboard@futo.org", style = NavigationItemStyle.Misc, navigate = {
|
||||
NavigationItem(title = "Email keyboard@futo.org", style = NavigationItemStyle.Mail, navigate = {
|
||||
context.openURI("mailto:keyboard@futo.org")
|
||||
})
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -28,7 +29,7 @@ import org.futo.inputmethod.latin.R
|
||||
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
||||
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
||||
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
||||
import org.futo.inputmethod.latin.uix.settings.useDataStore
|
||||
import org.futo.inputmethod.latin.uix.settings.useDataStoreValueNullable
|
||||
import org.futo.inputmethod.latin.uix.theme.Typography
|
||||
import org.futo.inputmethod.updates.ConditionalUpdate
|
||||
|
||||
@ -58,7 +59,9 @@ fun AndroidTextInput() {
|
||||
fun HomeScreen(navController: NavHostController = rememberNavController()) {
|
||||
val context = LocalContext.current
|
||||
val scrollState = rememberScrollState()
|
||||
val isDeveloper = useDataStore(key = IS_DEVELOPER, default = false)
|
||||
val isDeveloper = useDataStoreValueNullable(key = IS_DEVELOPER, default = false)
|
||||
val isPaid = useDataStoreValueNullable(IS_ALREADY_PAID)
|
||||
|
||||
|
||||
Column {
|
||||
Column(
|
||||
@ -71,6 +74,7 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
|
||||
ScreenTitle("FUTO Keyboard Settings")
|
||||
|
||||
ConditionalUpdate(navController)
|
||||
ConditionalUnpaidNoticeWithNav(navController)
|
||||
|
||||
NavigationItem(
|
||||
title = "Languages",
|
||||
@ -107,6 +111,16 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
|
||||
icon = painterResource(id = R.drawable.eye)
|
||||
)
|
||||
|
||||
UnpaidNoticeCondition(showOnlyIfReminder = true) {
|
||||
NavigationItem(
|
||||
title = stringResource(R.string.payment),
|
||||
style = NavigationItemStyle.HomePrimary,
|
||||
navigate = { navController.navigate("payment") },
|
||||
icon = painterResource(R.drawable.dollar_sign)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
NavigationItem(
|
||||
title = "Help & Feedback",
|
||||
style = NavigationItemStyle.HomePrimary,
|
||||
@ -114,7 +128,7 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
|
||||
icon = painterResource(id = R.drawable.help_circle)
|
||||
)
|
||||
|
||||
if(isDeveloper.value || LocalInspectionMode.current) {
|
||||
if(isDeveloper || LocalInspectionMode.current) {
|
||||
NavigationItem(
|
||||
title = "Developer Settings",
|
||||
style = NavigationItemStyle.HomeSecondary,
|
||||
@ -124,7 +138,18 @@ fun HomeScreen(navController: NavHostController = rememberNavController()) {
|
||||
}
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
if(isPaid || LocalInspectionMode.current) {
|
||||
Text(
|
||||
stringResource(R.string.thank_you_for_using_paid),
|
||||
style = Typography.bodyMedium,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
|
||||
Text(
|
||||
"v${BuildConfig.VERSION_NAME}",
|
||||
style = Typography.labelSmall,
|
||||
|
@ -0,0 +1,483 @@
|
||||
package org.futo.inputmethod.latin.uix.settings.pages
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment.Companion.Center
|
||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import kotlinx.coroutines.launch
|
||||
import org.futo.inputmethod.latin.BuildConfig
|
||||
import org.futo.inputmethod.latin.R
|
||||
import org.futo.inputmethod.latin.payment.PaymentActivity
|
||||
import org.futo.inputmethod.latin.uix.SettingsKey
|
||||
import org.futo.inputmethod.latin.uix.setSetting
|
||||
import org.futo.inputmethod.latin.uix.settings.NavigationItem
|
||||
import org.futo.inputmethod.latin.uix.settings.NavigationItemStyle
|
||||
import org.futo.inputmethod.latin.uix.settings.ScreenTitle
|
||||
import org.futo.inputmethod.latin.uix.settings.ScrollableList
|
||||
import org.futo.inputmethod.latin.uix.settings.useDataStore
|
||||
import org.futo.inputmethod.latin.uix.settings.useDataStoreValueNullable
|
||||
import org.futo.inputmethod.latin.uix.theme.Typography
|
||||
import org.futo.inputmethod.updates.openURI
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
|
||||
val IS_ALREADY_PAID = SettingsKey(booleanPreferencesKey("already_paid"), false)
|
||||
val IS_PAYMENT_PENDING = SettingsKey(booleanPreferencesKey("payment_pending"), false)
|
||||
val HAS_SEEN_PAID_NOTICE = SettingsKey(booleanPreferencesKey("seen_paid_notice"), false)
|
||||
val FORCE_SHOW_NOTICE = SettingsKey(booleanPreferencesKey("force_show_notice"), false)
|
||||
val NOTICE_REMINDER_TIME = SettingsKey(longPreferencesKey("notice_reminder_time"), 0L)
|
||||
val EXT_LICENSE_KEY = SettingsKey(stringPreferencesKey("license_key"), "")
|
||||
|
||||
fun <T> Context.startAppActivity(activity: Class<T>, clearTop: Boolean = false) {
|
||||
val intent = Intent(this, activity)
|
||||
|
||||
if(this !is Activity) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
if(clearTop) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun useNumberOfDaysInstalled(): MutableIntState {
|
||||
if (LocalInspectionMode.current) {
|
||||
return remember { mutableIntStateOf(55) }
|
||||
}
|
||||
|
||||
val dayCount = remember { mutableIntStateOf(-1) }
|
||||
|
||||
val context = LocalContext.current
|
||||
LaunchedEffect(Unit) {
|
||||
val packageManager = context.packageManager
|
||||
val packageInfo = packageManager.getPackageInfo(context.packageName, 0)
|
||||
|
||||
val firstInstallTime = packageInfo.firstInstallTime
|
||||
|
||||
val currentTime = System.currentTimeMillis()
|
||||
|
||||
val diff = (currentTime - firstInstallTime) / (1000 * 60 * 60 * 24)
|
||||
dayCount.intValue = diff.toInt()
|
||||
}
|
||||
|
||||
return dayCount
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ParagraphText(it: String) {
|
||||
Text(it, modifier = Modifier.padding(16.dp, 8.dp), style = Typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaymentText() {
|
||||
val numDaysInstalled = useNumberOfDaysInstalled()
|
||||
|
||||
// Doesn't make sense to say "You've been using for ... days" if it's less than seven days
|
||||
if(numDaysInstalled.intValue >= 7) {
|
||||
ParagraphText(stringResource(R.string.payment_text_1, numDaysInstalled.value))
|
||||
} else {
|
||||
ParagraphText(stringResource(R.string.payment_text_1_alt))
|
||||
}
|
||||
|
||||
ParagraphText(stringResource(R.string.payment_text_2))
|
||||
}
|
||||
|
||||
suspend fun pushNoticeReminderTime(context: Context, days: Float) {
|
||||
// If the user types in a crazy high number, the long can't store such a large value and it won't suppress the reminder
|
||||
// 21x the age of the universe ought to be enough for a payment notice reminder
|
||||
// Also take the absolute value in the case of a negative number
|
||||
val clampedDays = if (days.absoluteValue >= 1.06751991E14f) {
|
||||
1.06751991E14f
|
||||
} else {
|
||||
days.absoluteValue
|
||||
}
|
||||
|
||||
context.setSetting(NOTICE_REMINDER_TIME,
|
||||
System.currentTimeMillis() / 1000L + (clampedDays * 60.0 * 60.0 * 24.0).toLong())
|
||||
}
|
||||
|
||||
const val TRIAL_PERIOD_DAYS = 30
|
||||
|
||||
@Composable
|
||||
fun UnpaidNoticeCondition(
|
||||
force: Boolean = LocalInspectionMode.current,
|
||||
showOnlyIfReminder: Boolean = false,
|
||||
inner: @Composable () -> Unit
|
||||
) {
|
||||
val numDaysInstalled = useNumberOfDaysInstalled()
|
||||
val forceShowNotice = useDataStoreValueNullable(FORCE_SHOW_NOTICE)
|
||||
val isAlreadyPaid = useDataStoreValueNullable(IS_ALREADY_PAID)
|
||||
val pushReminderTime = useDataStoreValueNullable(NOTICE_REMINDER_TIME)
|
||||
val currentTime = System.currentTimeMillis() / 1000L
|
||||
|
||||
val reminderTimeIsUp = (currentTime >= pushReminderTime)
|
||||
|
||||
val displayCondition = if(showOnlyIfReminder) {
|
||||
// Either the reminder time is not up, or we're not past the trial period
|
||||
(!isAlreadyPaid) && ((!reminderTimeIsUp) || (!forceShowNotice && numDaysInstalled.intValue < TRIAL_PERIOD_DAYS))
|
||||
} else {
|
||||
// The trial period time is over
|
||||
(forceShowNotice || (numDaysInstalled.intValue >= TRIAL_PERIOD_DAYS))
|
||||
// and the current time is past the reminder time
|
||||
&& reminderTimeIsUp
|
||||
// and we have not already paid
|
||||
&& (!isAlreadyPaid)
|
||||
}
|
||||
if (force || displayCondition) {
|
||||
inner()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun ConditionalUnpaidNoticeInVoiceInputWindow(onClose: (() -> Unit)? = null) {
|
||||
val context = LocalContext.current
|
||||
|
||||
UnpaidNoticeCondition {
|
||||
TextButton(onClick = {
|
||||
context.startAppActivity(PaymentActivity::class.java)
|
||||
if (onClose != null) onClose()
|
||||
}) {
|
||||
Text(stringResource(R.string.unpaid_indicator), color = MaterialTheme.colorScheme.onSurface)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun UnpaidNotice(onPay: () -> Unit = { }, onAlreadyPaid: () -> Unit = { }) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant, modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp, 8.dp), shape = RoundedCornerShape(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(8.dp, 0.dp)) {
|
||||
Text(
|
||||
stringResource(R.string.unpaid_title),
|
||||
modifier = Modifier.padding(8.dp),
|
||||
style = Typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
|
||||
PaymentText()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.align(CenterHorizontally)
|
||||
) {
|
||||
|
||||
Box(modifier = Modifier.weight(1.0f)) {
|
||||
Button(onClick = onPay, modifier = Modifier.align(Center)) {
|
||||
Text(stringResource(R.string.pay_now))
|
||||
}
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.weight(1.0f)) {
|
||||
Button(
|
||||
onClick = onAlreadyPaid, colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondary,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondary
|
||||
), modifier = Modifier.align(Center)
|
||||
) {
|
||||
Text(stringResource(R.string.i_already_paid))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun ConditionalUnpaidNoticeWithNav(navController: NavController = rememberNavController()) {
|
||||
UnpaidNoticeCondition {
|
||||
UnpaidNotice(onPay = {
|
||||
navController.navigate("payment")
|
||||
}, onAlreadyPaid = {
|
||||
navController.navigate("payment")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun PaymentThankYouScreen(onExit: () -> Unit = { }) {
|
||||
val hasSeenPaidNotice = useDataStore(HAS_SEEN_PAID_NOTICE)
|
||||
val isPending = useDataStore(IS_PAYMENT_PENDING)
|
||||
|
||||
ScrollableList {
|
||||
ScreenTitle(
|
||||
if (isPending.value) {
|
||||
stringResource(R.string.payment_pending)
|
||||
} else {
|
||||
stringResource(R.string.thank_you)
|
||||
},
|
||||
showBack = false
|
||||
)
|
||||
ParagraphText(stringResource(R.string.thank_you_for_purchasing_keyboard))
|
||||
if (isPending.value) {
|
||||
ParagraphText(stringResource(R.string.payment_pending_body))
|
||||
}
|
||||
ParagraphText(stringResource(R.string.purchase_will_help_body))
|
||||
|
||||
Box(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp, 8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
hasSeenPaidNotice.setValue(true)
|
||||
onExit()
|
||||
},
|
||||
modifier = Modifier
|
||||
.align(Center)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.continue_))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun PaymentFailedScreen(onExit: () -> Unit = { }) {
|
||||
val hasSeenPaidNotice = useDataStore(HAS_SEEN_PAID_NOTICE.key, default = true)
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
ScrollableList {
|
||||
ScreenTitle(stringResource(R.string.payment_error), showBack = false)
|
||||
|
||||
@Suppress("KotlinConstantConditions")
|
||||
(ParagraphText(
|
||||
when (BuildConfig.FLAVOR) {
|
||||
"fDroid" -> stringResource(R.string.payment_failed_body_ex)
|
||||
"dev" -> stringResource(R.string.payment_failed_body_ex)
|
||||
"standalone" -> stringResource(R.string.payment_failed_body_ex)
|
||||
else -> stringResource(R.string.payment_failed_body)
|
||||
}
|
||||
))
|
||||
NavigationItem(title = "Email keyboard@futo.org", style = NavigationItemStyle.Mail, navigate = {
|
||||
context.openURI("mailto:keyboard@futo.org")
|
||||
})
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
Button(
|
||||
onClick = {
|
||||
// It would be rude to immediately annoy the user again about paying, so delay the notice forever
|
||||
coroutineScope.launch {
|
||||
pushNoticeReminderTime(context, Float.MAX_VALUE)
|
||||
}
|
||||
|
||||
hasSeenPaidNotice.setValue(false)
|
||||
onExit()
|
||||
},
|
||||
modifier = Modifier.align(Center)
|
||||
) {
|
||||
Text(stringResource(R.string.continue_))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
fun PaymentScreen(
|
||||
navController: NavHostController = rememberNavController(),
|
||||
onExit: () -> Unit = { }
|
||||
) {
|
||||
val isAlreadyPaid = useDataStore(IS_ALREADY_PAID)
|
||||
val pushReminderTime = useDataStore(NOTICE_REMINDER_TIME)
|
||||
val numDaysInstalled = useNumberOfDaysInstalled()
|
||||
val forceShowNotice = useDataStore(FORCE_SHOW_NOTICE)
|
||||
val currentTime = System.currentTimeMillis() / 1000L
|
||||
|
||||
val reminderTimeIsUp = (currentTime >= pushReminderTime.value) && ((numDaysInstalled.intValue >= TRIAL_PERIOD_DAYS) || forceShowNotice.value)
|
||||
|
||||
val onAlreadyPaid = {
|
||||
isAlreadyPaid.setValue(true)
|
||||
navController.navigateUp()
|
||||
navController.navigate("paid", NavOptions.Builder().setLaunchSingleTop(true).build())
|
||||
}
|
||||
|
||||
val counter = remember { mutableIntStateOf(0) }
|
||||
|
||||
ScrollableList {
|
||||
ScreenTitle(stringResource(R.string.payment_title), showBack = true, navController = navController)
|
||||
PaymentText()
|
||||
|
||||
val context = LocalContext.current
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Row(modifier = Modifier.padding(8.dp, 0.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
TODO()
|
||||
}, modifier = Modifier
|
||||
.weight(1.0f)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.pay))
|
||||
}
|
||||
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
counter.intValue += 1
|
||||
if(counter.intValue == 2) {
|
||||
onAlreadyPaid()
|
||||
}
|
||||
}, colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondary,
|
||||
contentColor = MaterialTheme.colorScheme.onSecondary
|
||||
), modifier = Modifier
|
||||
.weight(1.0f)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text(stringResource(
|
||||
when(counter.intValue) {
|
||||
0 -> R.string.i_already_paid
|
||||
else -> R.string.i_already_paid_2
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (reminderTimeIsUp) {
|
||||
val lastValidRemindValue = remember { mutableFloatStateOf(5.0f) }
|
||||
val remindDays = remember { mutableStateOf("5") }
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(CenterHorizontally)
|
||||
.padding(16.dp, 0.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
OutlinedButton(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
pushNoticeReminderTime(context, lastValidRemindValue.floatValue)
|
||||
}
|
||||
onExit()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.remind_me_in_x))
|
||||
Surface(color = MaterialTheme.colorScheme.primaryContainer) {
|
||||
BasicTextField(
|
||||
value = remindDays.value,
|
||||
onValueChange = {
|
||||
remindDays.value = it
|
||||
|
||||
it.toFloatOrNull()?.let { lastValidRemindValue.floatValue = it }
|
||||
},
|
||||
modifier = Modifier
|
||||
.width(32.dp)
|
||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(4.dp),
|
||||
textStyle = Typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface),
|
||||
cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
|
||||
keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number)
|
||||
)
|
||||
}
|
||||
Text(stringResource(R.string.in_x_days))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun PaymentScreenSwitch(
|
||||
navController: NavHostController = rememberNavController(),
|
||||
onExit: () -> Unit = { },
|
||||
startDestination: String = "payment"
|
||||
) {
|
||||
val isAlreadyPaid = useDataStore(IS_ALREADY_PAID)
|
||||
val hasSeenNotice = useDataStore(HAS_SEEN_PAID_NOTICE)
|
||||
val paymentDest = if (!isAlreadyPaid.value && hasSeenNotice.value) {
|
||||
"error"
|
||||
} else if (isAlreadyPaid.value && !hasSeenNotice.value) {
|
||||
"paid"
|
||||
} else {
|
||||
"payment"
|
||||
}
|
||||
|
||||
LaunchedEffect(paymentDest) {
|
||||
if (paymentDest != "payment") {
|
||||
navController.navigate(
|
||||
paymentDest,
|
||||
NavOptions.Builder().setLaunchSingleTop(true).build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination
|
||||
) {
|
||||
composable("payment") {
|
||||
PaymentScreen(navController, onExit)
|
||||
}
|
||||
|
||||
composable("paid") {
|
||||
PaymentThankYouScreen(onExit)
|
||||
}
|
||||
|
||||
composable("error") {
|
||||
PaymentFailedScreen(onExit)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package org.futo.inputmethod.latin.uix.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
@ -10,8 +11,12 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import org.futo.inputmethod.latin.uix.THEME_KEY
|
||||
import org.futo.inputmethod.latin.uix.settings.useDataStoreValueNullable
|
||||
import org.futo.inputmethod.latin.uix.theme.presets.VoiceInputTheme
|
||||
import kotlin.math.sqrt
|
||||
|
||||
val DarkColorScheme = darkColorScheme(
|
||||
@ -85,4 +90,30 @@ fun UixThemeWrapper(colorScheme: ColorScheme, content: @Composable () -> Unit) {
|
||||
typography = Typography,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
fun ThemeOption?.ensureAvailable(context: Context): ThemeOption? {
|
||||
return if(this == null) {
|
||||
null
|
||||
} else {
|
||||
if(!this.available(context)) {
|
||||
null
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UixThemeAuto(content: @Composable () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val themeIdx = useDataStoreValueNullable(THEME_KEY.key, THEME_KEY.default)
|
||||
|
||||
val theme: ThemeOption = themeIdx?.let { ThemeOptions[it].ensureAvailable(context) }
|
||||
?: VoiceInputTheme
|
||||
|
||||
val colors = remember(theme.key) { theme.obtainColors(context) }
|
||||
|
||||
UixThemeWrapper(colorScheme = colors, content)
|
||||
}
|
@ -175,8 +175,8 @@ static inline void showStackTrace() {
|
||||
#endif // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
|
||||
|
||||
#ifdef FLAG_DBG
|
||||
#define DEBUG_DICT true
|
||||
//#define DEBUG_DICT false
|
||||
//#define DEBUG_DICT true
|
||||
#define DEBUG_DICT false
|
||||
#define DEBUG_DICT_FULL false
|
||||
#define DEBUG_EDIT_DISTANCE false
|
||||
#define DEBUG_NODE DEBUG_DICT_FULL
|
||||
|
Loading…
Reference in New Issue
Block a user