diff --git a/CLAUDE.md b/CLAUDE.md index 1951c3b..ef005ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -252,5 +252,9 @@ Claude, so they carry the same middleware chain. resource merge). - **Always device-verify Compose changes** (`installDebug` + screenshot), not just `assembleDebug` โ€” a green build can still crash at runtime (e.g. the empty-font bug). +- **Never start the Android emulator** unless the user asked for it, or said yes when asked. It is + slow to boot and device checks belong on the connected phone. If the phone can't take the build + (a debug build can't replace a Play-installed one: the signatures differ, and uninstalling wipes + the app's data), stop and ask how to verify instead of reaching for the emulator. - A few inline strings remain (Premium feature descriptions, quick-action labels, Tibetan placeholders) โ€” externalize if expanding i18n. diff --git a/app/build.gradle b/app/build.gradle index 2c737c9..6455cc0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,10 +31,10 @@ android { // installed devices on API 23 (0.03%). minSdkVersion 24 targetSdkVersion 36 - versionCode project.hasProperty('versionCode') ? project.property('versionCode').toInteger() : 57 + versionCode project.hasProperty('versionCode') ? project.property('versionCode').toInteger() : 58 // RevenueCat targeting rules match on this (App version >= 2.3.0). - versionName "2.3.0" + versionName "2.3.1" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c85e175..a79782b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,7 +28,8 @@ + android:exported="false" + android:theme="@style/Theme.TibetanKeyboard.Sheet" /> Unit, @@ -75,6 +84,8 @@ class HomeActions( val onAbout: () -> Unit, val onUpgrade: () -> Unit, val onJourney: () -> Unit, + val onSignIn: () -> Unit, + val onAccount: () -> Unit, ) private data class QuickAction( @@ -124,7 +135,7 @@ fun HomeScreen( Box(modifier = Modifier.fillMaxSize()) { ScreenScaffold(horizontalPadding = 18.dp) { Spacer(Modifier.height(8.dp)) - BrandHeader() + BrandHeader(state.account, onSignIn = actions.onSignIn, onAccount = actions.onAccount) Spacer(Modifier.height(18.dp)) SetupCard( state = state, @@ -229,7 +240,7 @@ private fun UpdateReadyBanner(onInstall: () -> Unit, modifier: Modifier = Modifi } @Composable -private fun BrandHeader() { +private fun BrandHeader(account: HomeAccount?, onSignIn: () -> Unit, onAccount: () -> Unit) { Row( modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp), verticalAlignment = Alignment.CenterVertically, @@ -248,10 +259,50 @@ private fun BrandHeader() { modifier = Modifier.size(72.dp,).clip(RoundedCornerShape(10.dp)) ) } - Column { - Text(stringResource(R.string.app_name), color = TibetanColors.Cream, fontSize = 18.sp, fontWeight = FontWeight.ExtraBold) + Column(Modifier.weight(1f)) { + Text( + stringResource(R.string.app_name), + color = TibetanColors.Cream, fontSize = 18.sp, fontWeight = FontWeight.ExtraBold, + maxLines = 1, overflow = TextOverflow.Ellipsis, + ) BoText(stringResource(R.string.bo_app_name), color = TibetanColors.CreamDim, fontSize = 13.sp) } + AccountControl(account, onSignIn = onSignIn, onAccount = onAccount) + } +} + +/** + * Top right of Home: "Sign in" for a signed-out user, their picture once signed in. Without it + * the only way to sign in was to reach a PRO screen first, so a subscriber on a new phone had no + * obvious way back to their PRO. + */ +@Composable +private fun AccountControl(account: HomeAccount?, onSignIn: () -> Unit, onAccount: () -> Unit) { + if (account == null) { + Row( + modifier = Modifier + .clip(TibetanTokens.Pill) + .border(1.dp, TibetanColors.Gold400.copy(alpha = 0.7f), TibetanTokens.Pill) + .clickable(role = Role.Button, onClick = onSignIn) + .padding(horizontal = 14.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Icon(AppIcons.Person, null, tint = TibetanColors.Gold300, modifier = Modifier.size(17.dp)) + Text(stringResource(R.string.sign_in), color = TibetanColors.Gold300, fontSize = 13.sp, fontWeight = FontWeight.Bold) + } + } else { + AccountAvatar( + name = account.name, + photoUrl = account.photoUrl, + modifier = Modifier + .clip(CircleShape) + .clickable( + onClickLabel = stringResource(R.string.title_account), + role = Role.Button, + onClick = onAccount, + ), + ) } } diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeViewModel.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeViewModel.kt index 14bfb66..2b39735 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeViewModel.kt @@ -53,6 +53,18 @@ class HomeViewModel(app: Application) : AndroidViewModel(app) { init { premiumLiveData.observeForever(premiumObserver) RevenueCatManager.getInstance().refreshCustomerInfo() + refreshAccount() + } + + /** Re-read who is signed in (every resume: signing in and out happen on other screens). */ + fun refreshAccount() { + val account = if (authManager.isUserAuthenticated()) { + HomeAccount( + name = authManager.getCurrentUserName(), + photoUrl = authManager.getCurrentUserPhotoUrl().takeIf { it.isNotBlank() }, + ) + } else null + _uiState.update { it.copy(account = account) } } /** Called by the Activity after it reads keyboard-enabled / default-IME status. */ diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginActivity.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginActivity.kt index 8d5ec60..043ebc4 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginActivity.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginActivity.kt @@ -83,6 +83,8 @@ class LoginActivity : AppCompatActivity() { onGoogleSignIn = { signInWithGoogle() }, onTerms = { openTermsOfService() }, onPrivacy = { openPrivacyPolicy() }, + // Swiped away, tapped outside or Back: whatever is underneath is still there. + onDismiss = { finish() }, ) } } diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginScreen.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginScreen.kt index fc36ec3..2b2d267 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginScreen.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/login/LoginScreen.kt @@ -1,5 +1,6 @@ package com.kharagedition.tibetankeyboard.ui.login +import androidx.annotation.StringRes import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -8,98 +9,156 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.Icon +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kharagedition.tibetankeyboard.R import com.kharagedition.tibetankeyboard.ui.compose.components.AppIcons -import com.kharagedition.tibetankeyboard.ui.compose.components.BoText +import com.kharagedition.tibetankeyboard.ui.compose.components.IconTile import com.kharagedition.tibetankeyboard.ui.compose.theme.TibetanColors import com.kharagedition.tibetankeyboard.ui.compose.theme.TibetanTokens +/** One thing an account gives. */ +private data class SignInBenefit( + val icon: ImageVector, + @StringRes val title: Int, + @StringRes val description: Int, +) + +/** Only what the app really ties to the account; typing stats, for one, stay on the phone. */ +private val SignInBenefits = listOf( + SignInBenefit(AppIcons.Crown, R.string.sign_in_benefit_pro, R.string.sign_in_benefit_pro_desc), + SignInBenefit(AppIcons.Bot, R.string.sign_in_benefit_ai, R.string.sign_in_benefit_ai_desc), + SignInBenefit(AppIcons.Star, R.string.sign_in_benefit_community, R.string.sign_in_benefit_community_desc), +) + +/** + * Sign-in as a bottom sheet over the screen that asked for it: the user keeps their place, sees + * what an account is for, and can swipe the sheet away. [LoginActivity]'s window is see-through + * and shows nothing else. + */ +@OptIn(ExperimentalMaterial3Api::class) @Composable fun LoginScreen( isLoading: Boolean, onGoogleSignIn: () -> Unit, onTerms: () -> Unit, onPrivacy: () -> Unit, + onDismiss: () -> Unit, ) { - Box( - modifier = Modifier.fillMaxSize().background(TibetanColors.Bg900).systemBarsPadding(), - contentAlignment = Alignment.Center, + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + shape = RoundedCornerShape(topStart = 28.dp, topEnd = 28.dp), + containerColor = TibetanColors.Bg800, + contentColor = TibetanColors.Cream, + scrimColor = TibetanColors.Espresso.copy(alpha = 0.72f), + dragHandle = { SheetHandle() }, ) { Column( - modifier = Modifier.fillMaxWidth().padding(horizontal = 32.dp), + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(horizontal = 24.dp) + .padding(bottom = 20.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - // brand - Box( - modifier = Modifier.size(80.dp).clip(RoundedCornerShape(22.dp)).background(TibetanTokens.GoldVertical), - contentAlignment = Alignment.Center, - ) { - Icon(AppIcons.Lotus, null, tint = TibetanColors.Espresso, modifier = Modifier.size(44.dp)) - } - Spacer(Modifier.height(20.dp)) - Text(stringResource(R.string.app_name), color = TibetanColors.Cream, fontSize = 24.sp, fontWeight = FontWeight.ExtraBold) - Spacer(Modifier.height(4.dp)) - BoText(stringResource(R.string.bo_app_name), color = TibetanColors.Gold300, fontSize = 16.sp) - - Spacer(Modifier.height(40.dp)) - Text(stringResource(R.string.welcome), color = TibetanColors.Cream, fontSize = 20.sp, fontWeight = FontWeight.Bold) + Image( + painter = painterResource(R.mipmap.ic_launcher), + contentDescription = null, + modifier = Modifier.size(56.dp).clip(RoundedCornerShape(16.dp)), + ) + Spacer(Modifier.height(14.dp)) + Text( + stringResource(R.string.sign_in_title, stringResource(R.string.app_name)), + color = TibetanColors.Cream, fontSize = 21.sp, fontWeight = FontWeight.ExtraBold, + textAlign = TextAlign.Center, + ) Spacer(Modifier.height(6.dp)) Text( - stringResource(R.string.login_subtitle), - color = TibetanColors.CreamDim, fontSize = 14.sp, lineHeight = 21.sp, + stringResource(R.string.sign_in_subtitle), + color = TibetanColors.CreamDim, fontSize = 14.sp, lineHeight = 20.sp, textAlign = TextAlign.Center, ) - Spacer(Modifier.height(28.dp)) + Spacer(Modifier.height(22.dp)) + Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(14.dp)) { + SignInBenefits.forEach { BenefitRow(it) } + } + + Spacer(Modifier.height(24.dp)) GoogleSignInButton(isLoading = isLoading, onClick = onGoogleSignIn) + Spacer(Modifier.height(14.dp)) + LegalLine(onTerms = onTerms, onPrivacy = onPrivacy) } + } +} - // privacy footer - Row( - modifier = Modifier - .align(Alignment.BottomCenter) - .statusBarsPadding() - .padding(horizontal = 32.dp, vertical = 24.dp), - horizontalArrangement = Arrangement.Center, - ) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text(stringResource(R.string.by_signing_in), color = TibetanColors.CreamFaint, fontSize = 12.sp) - Row(horizontalArrangement = Arrangement.Center) { - Text( - stringResource(R.string.terms_of_service)+" ", - color = TibetanColors.Gold300, fontSize = 12.sp, fontWeight = FontWeight.SemiBold, - modifier = Modifier.clickable(onClick = onTerms), - ) - Text(stringResource(R.string.and_word)+" ", color = TibetanColors.CreamFaint, fontSize = 12.sp) - Text( - stringResource(R.string.privacy_policy), - color = TibetanColors.Gold300, fontSize = 12.sp, fontWeight = FontWeight.SemiBold, - modifier = Modifier.clickable(onClick = onPrivacy), - ) - } - } +@Composable +private fun SheetHandle() { + Box( + modifier = Modifier + .padding(top = 12.dp, bottom = 18.dp) + .size(width = 40.dp, height = 4.dp) + .clip(TibetanTokens.Pill) + .background(TibetanColors.Line2), + ) +} + +@Composable +private fun BenefitRow(benefit: SignInBenefit) { + Row(verticalAlignment = Alignment.CenterVertically) { + IconTile(benefit.icon, size = 40.dp, iconSize = 20.dp) + Spacer(Modifier.width(14.dp)) + Column(Modifier.weight(1f)) { + Text( + stringResource(benefit.title), + color = TibetanColors.Cream, fontSize = 14.5.sp, fontWeight = FontWeight.Bold, + ) + Text(stringResource(benefit.description), color = TibetanColors.CreamDim, fontSize = 12.5.sp) + } + } +} + +@Composable +private fun LegalLine(onTerms: () -> Unit, onPrivacy: () -> Unit) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text(stringResource(R.string.by_signing_in), color = TibetanColors.CreamFaint, fontSize = 12.sp) + Row(horizontalArrangement = Arrangement.Center) { + Text( + stringResource(R.string.terms_of_service) + " ", + color = TibetanColors.Gold300, fontSize = 12.sp, fontWeight = FontWeight.SemiBold, + modifier = Modifier.clickable(role = Role.Button, onClick = onTerms), + ) + Text(stringResource(R.string.and_word) + " ", color = TibetanColors.CreamFaint, fontSize = 12.sp) + Text( + stringResource(R.string.privacy_policy), + color = TibetanColors.Gold300, fontSize = 12.sp, fontWeight = FontWeight.SemiBold, + modifier = Modifier.clickable(role = Role.Button, onClick = onPrivacy), + ) } } } @@ -112,7 +171,7 @@ private fun GoogleSignInButton(isLoading: Boolean, onClick: () -> Unit) { .height(54.dp) .clip(RoundedCornerShape(27.dp)) .background(TibetanColors.Cream) - .clickable(enabled = !isLoading, onClick = onClick), + .clickable(enabled = !isLoading, role = Role.Button, onClick = onClick), contentAlignment = Alignment.Center, ) { if (isLoading) { diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsActivity.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsActivity.kt index 322dbc4..94dd7a9 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsActivity.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsActivity.kt @@ -17,6 +17,7 @@ import com.kharagedition.tibetankeyboard.BuildConfig import com.kharagedition.tibetankeyboard.ui.subscription.ManageSubscriptionActivity import com.kharagedition.tibetankeyboard.R import com.kharagedition.tibetankeyboard.analytics.AppAnalytics +import com.kharagedition.tibetankeyboard.auth.AuthManager import com.kharagedition.tibetankeyboard.ui.compose.theme.TibetanKeyboardTheme import com.kharagedition.tibetankeyboard.util.openPremiumUpgrade import com.kharagedition.tibetankeyboard.util.showConfirmationDialog @@ -59,6 +60,7 @@ class SettingsActivity : AppCompatActivity() { onStreakReminder = viewModel::setStreakReminder, onUpgrade = { openPremiumUpgrade(AppAnalytics.UpgradeSource.SETTINGS) }, onManageSubscription = { ManageSubscriptionActivity.open(this) }, + onSignIn = { viewModel.signIn() }, onLogout = { showConfirmationDialog( title = getString(R.string.sign_out), @@ -71,8 +73,8 @@ class SettingsActivity : AppCompatActivity() { private fun signOut() { viewModel.signOut { - viewModel.redirectToLogin() showToast(getString(R.string.signed_out)) + AuthManager(this).redirectToHome() } } diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsScreen.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsScreen.kt index 24eaf7b..21f00ff 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsScreen.kt @@ -60,6 +60,9 @@ data class SettingsState( val streakReminder: Boolean = true, val isPremium: Boolean = false, val isAuthenticated: Boolean = false, + /** The signed-in account, shown at the top of the Account group. */ + val accountName: String = "", + val accountEmail: String = "", ) class SettingsActions( @@ -72,6 +75,7 @@ class SettingsActions( val onStreakReminder: (Boolean) -> Unit, val onUpgrade: () -> Unit, val onManageSubscription: () -> Unit, + val onSignIn: () -> Unit, val onLogout: () -> Unit, ) @@ -146,6 +150,22 @@ fun SettingsScreen( SectionLabel(stringResource(R.string.title_account)) Spacer(Modifier.height(9.dp)) SettingsGroup { + if (state.isAuthenticated) { + SettingsRow( + icon = AppIcons.Person, + title = state.accountName, + subtitle = state.accountEmail.takeIf { it.isNotBlank() }, + ) + } else { + SettingsRow( + icon = AppIcons.Person, + title = stringResource(R.string.sign_in), + subtitle = stringResource(R.string.sign_in_benefit), + onClick = actions.onSignIn, + trailing = { ChevronIcon() }, + ) + } + SettingsDivider() SettingsRow( icon = AppIcons.Crown, title = stringResource(R.string.subscription), @@ -182,7 +202,7 @@ fun SettingsScreen( SettingsDivider() SettingsRow( icon = AppIcons.Logout, - title = stringResource(R.string.logout), + title = stringResource(R.string.sign_out), onClick = actions.onLogout, trailing = { ChevronIcon() }, ) diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsViewModel.kt index 4394013..c9b4b80 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/settings/SettingsViewModel.kt @@ -20,9 +20,7 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) { private val authManager = AuthManager(app) private val premiumLiveData = RevenueCatManager.getInstance().isPremiumUser - private val _uiState = MutableStateFlow( - SettingsPrefs.read(app).copy(isAuthenticated = authManager.isUserAuthenticated()) - ) + private val _uiState = MutableStateFlow(SettingsPrefs.read(app).withAccount()) val uiState: StateFlow = _uiState.asStateFlow() private val premiumObserver = Observer { isPremium -> @@ -68,12 +66,21 @@ class SettingsViewModel(app: Application) : AndroidViewModel(app) { /** Reload prefs in case they changed elsewhere (e.g. legacy preference screen). */ fun reload() { val current = _uiState.value - _uiState.value = SettingsPrefs.read(getApplication()) - .copy(isPremium = current.isPremium, isAuthenticated = authManager.isUserAuthenticated()) + _uiState.value = SettingsPrefs.read(getApplication()).copy(isPremium = current.isPremium).withAccount() + } + + private fun SettingsState.withAccount(): SettingsState { + val signedIn = authManager.isUserAuthenticated() + return copy( + isAuthenticated = signedIn, + accountName = if (signedIn) authManager.getCurrentUserName() else "", + accountEmail = if (signedIn) authManager.getCurrentUserEmail() else "", + ) } fun signOut(onComplete: () -> Unit) = authManager.signOut(onComplete) - fun redirectToLogin() = authManager.redirectToLogin() + /** Sign in from Settings: this screen stays underneath, so the user lands back here. */ + fun signIn() = authManager.redirectToLogin(finishCaller = false, returnAfterLogin = true) fun refreshPremium() = RevenueCatManager.getInstance().refreshCustomerInfo() override fun onCleared() { diff --git a/app/src/main/res/values-mcc402/strings.xml b/app/src/main/res/values-mcc402/strings.xml index e89e362..1296ec3 100644 --- a/app/src/main/res/values-mcc402/strings.xml +++ b/app/src/main/res/values-mcc402/strings.xml @@ -5,7 +5,6 @@ Dzongkha Dzongkha Assistant Ask in English/Dzongkha - Your AI Assistant for Dzongkha Language Enter Dzongkha text to analyze Analyze your Dzongkha text for grammar, spelling, and style Enter text to transliterate… diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4a7968a..31d0471 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,10 +48,19 @@ Typing... App Logo - Your AI Assistant for Tibetan Language Welcome Back Sign in to continue chatting Continue with Google + Sign in + Keep PRO on every device + Sign in to %1$s + One tap with your Google account. + PRO on every device + Your subscription follows your account + AI chat and translate + Ask questions and translate with AI + Community comparison + See how your streak compares By signing in, you agree to our Terms of Service and Privacy Policy @@ -62,7 +71,7 @@ Clear Chat Profile - Sign Out + Sign out Loading... @@ -245,7 +254,6 @@ Subscription Premium plan Free plan - Logout Done AI Assistant Online ยท Tibetan @@ -301,7 +309,6 @@ GitHub Contact Get - Welcome By signing in, you agree to our Terms of Service and diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index cd1478a..8f4357a 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,7 +1,9 @@ + + + + + + + + + +