From ccc9491649c6de56c0195d4a0138fc9ca52f6bf9 Mon Sep 17 00:00:00 2001 From: Kunchok Tashi Date: Tue, 29 Sep 2026 20:43:07 -0400 Subject: [PATCH 1/2] login flow uodated --- CLAUDE.md | 4 ++ app/build.gradle | 4 +- .../tibetankeyboard/analytics/AppAnalytics.kt | 2 + .../tibetankeyboard/auth/AuthManager.kt | 19 ++++++ .../tibetankeyboard/ui/chat/ChatActivity.kt | 2 +- .../ui/compose/components/AppIcons.kt | 2 + .../ui/compose/components/Components.kt | 38 ++++++++++++ .../tibetankeyboard/ui/home/HomeActivity.kt | 11 ++++ .../tibetankeyboard/ui/home/HomeScreen.kt | 59 +++++++++++++++++-- .../tibetankeyboard/ui/home/HomeViewModel.kt | 12 ++++ .../ui/settings/SettingsActivity.kt | 4 +- .../ui/settings/SettingsScreen.kt | 22 ++++++- .../ui/settings/SettingsViewModel.kt | 19 ++++-- app/src/main/res/values/strings.xml | 5 +- app/src/main/res/values/themes.xml | 30 +++++++++- 15 files changed, 215 insertions(+), 18 deletions(-) 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/java/com/kharagedition/tibetankeyboard/analytics/AppAnalytics.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/analytics/AppAnalytics.kt index 9e79bcd..937c6d8 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/analytics/AppAnalytics.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/analytics/AppAnalytics.kt @@ -443,6 +443,8 @@ object AppAnalytics { const val ABOUT = "about" const val UPGRADE = "upgrade" const val JOURNEY = "journey" + const val SIGN_IN = "sign_in" + const val ACCOUNT = "account" } /** Stable source labels for [logJourneyOpened] (where the Journey screen was opened from). */ diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/auth/AuthManager.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/auth/AuthManager.kt index aeddbb5..345ae47 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/auth/AuthManager.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/auth/AuthManager.kt @@ -13,6 +13,7 @@ import com.google.firebase.auth.ktx.auth import com.google.firebase.ktx.Firebase import com.kharagedition.tibetankeyboard.R import com.kharagedition.tibetankeyboard.analytics.AppAnalytics +import com.kharagedition.tibetankeyboard.ui.home.HomeActivity import com.kharagedition.tibetankeyboard.ui.login.LoginActivity import com.kharagedition.tibetankeyboard.ui.subscription.KeyboardPremiumActivity import com.kharagedition.tibetankeyboard.ui.subscription.PremiumActivity @@ -42,6 +43,8 @@ class AuthManager(private val context: Context) { return userPreferences.getUserName().takeIf { it.isNotEmpty() } ?: "User" } + fun getCurrentUserEmail(): String = userPreferences.getUserEmail() + /** * Get current user photo URL */ @@ -120,6 +123,22 @@ class AuthManager(private val context: Context) { if (finishCaller) (context as? Activity)?.finish() } + /** + * Back to Home, closing the screens above it. Where a sign-out lands: Home needs no account + * and its header offers "Sign in" again, while a login screen would read as a demand. + */ + fun redirectToHome() { + val intent = Intent(context, HomeActivity::class.java) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) + + if (context !is Activity) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + + context.startActivity(intent) + (context as? Activity)?.finish() + } + /** * Open the paywall. Safe from a non-Activity context such as the IME service. [source] is the * [AppAnalytics.UpgradeSource] that triggered it, carried through every purchase event. diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/chat/ChatActivity.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/chat/ChatActivity.kt index a29f9b3..3e8b3b1 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/chat/ChatActivity.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/chat/ChatActivity.kt @@ -97,8 +97,8 @@ class ChatActivity : AppCompatActivity() { private fun signOut() { authManager.signOut { - authManager.redirectToLogin() showToast(getString(R.string.signed_out)) + authManager.redirectToHome() } } diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/AppIcons.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/AppIcons.kt index 1ff2949..76cac3c 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/AppIcons.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/AppIcons.kt @@ -18,6 +18,7 @@ import androidx.compose.material.icons.rounded.Lock import androidx.compose.material.icons.rounded.MoreHoriz import androidx.compose.material.icons.rounded.Notifications import androidx.compose.material.icons.rounded.Palette +import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.SwapHoriz import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Share @@ -56,6 +57,7 @@ object AppIcons { val Sound: ImageVector = Icons.Rounded.VolumeUp val Bell: ImageVector = Icons.Rounded.Notifications val Logout: ImageVector = Icons.AutoMirrored.Rounded.Logout + val Person: ImageVector = Icons.Rounded.Person val Copy: ImageVector = Icons.Rounded.ContentCopy val Send: ImageVector = Icons.AutoMirrored.Rounded.Send val Globe: ImageVector = Icons.Rounded.Language diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/Components.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/Components.kt index 5170608..f44337f 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/Components.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/compose/components/Components.kt @@ -25,12 +25,14 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage import com.kharagedition.tibetankeyboard.ui.compose.theme.LocalTibetanFont import com.kharagedition.tibetankeyboard.ui.compose.theme.TibetanColors import com.kharagedition.tibetankeyboard.ui.compose.theme.TibetanTokens @@ -152,6 +154,42 @@ fun CharCounter( ) } +/** + * The signed-in user's picture in a gold ring. Their initial sits underneath, so it shows while + * the picture loads and stays if there is none. + */ +@Composable +fun AccountAvatar( + name: String, + photoUrl: String?, + modifier: Modifier = Modifier, + size: Dp = 40.dp, +) { + Box( + modifier = modifier + .size(size) + .clip(CircleShape) + .background(TibetanColors.Brown600) + .border(1.5.dp, TibetanColors.Gold400, CircleShape), + contentAlignment = Alignment.Center, + ) { + Text( + name.trim().take(1).uppercase().ifEmpty { "?" }, + color = TibetanColors.Gold300, + fontSize = (size.value * 0.42f).sp, + fontWeight = FontWeight.ExtraBold, + ) + if (!photoUrl.isNullOrBlank()) { + AsyncImage( + model = photoUrl, + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize().padding(1.5.dp).clip(CircleShape), + ) + } + } +} + /** Small rounded badge, e.g. "PRO". */ @Composable fun PillBadge( diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeActivity.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeActivity.kt index bede653..4127bbe 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeActivity.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeActivity.kt @@ -130,6 +130,7 @@ class HomeActivity : InputMethodActivity() { super.onResume() refreshSetupState() viewModel.refreshJourney() + viewModel.refreshAccount() // CRITICAL: sync purchases on resume to acknowledge pending subscriptions // (prevents Google Play auto-cancelling after 3 days). if (viewModel.isUserAuthenticated()) { @@ -205,6 +206,16 @@ class HomeActivity : InputMethodActivity() { AppAnalytics.logJourneyOpened(AppAnalytics.JourneySource.HOME) startActivity(Intent(this, JourneyActivity::class.java)) }, + onSignIn = { + AppAnalytics.logHomeAction(AppAnalytics.HomeAction.SIGN_IN) + // Home stays underneath, so signing in (or backing out) lands back here. + AuthManager(this).redirectToLogin(finishCaller = false, returnAfterLogin = true) + }, + onAccount = { + AppAnalytics.logHomeAction(AppAnalytics.HomeAction.ACCOUNT) + // Plan, manage subscription and sign out live in Settings โ†’ Account. + startActivity(Intent(this, SettingsActivity::class.java)) + }, ) /** diff --git a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeScreen.kt b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeScreen.kt index b94df1c..c5a8672 100644 --- a/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/kharagedition/tibetankeyboard/ui/home/HomeScreen.kt @@ -14,6 +14,7 @@ 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.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.BasicTextField import androidx.compose.material3.Icon @@ -34,15 +35,18 @@ import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.viewinterop.AndroidView import com.bumptech.glide.Glide import com.kharagedition.tibetankeyboard.R import com.kharagedition.tibetankeyboard.analytics.AppAnalytics +import com.kharagedition.tibetankeyboard.ui.compose.components.AccountAvatar import com.kharagedition.tibetankeyboard.ui.compose.components.AppIcons import com.kharagedition.tibetankeyboard.ui.compose.components.BoText import com.kharagedition.tibetankeyboard.ui.compose.components.IconTile @@ -61,8 +65,13 @@ data class HomeUiState( val isPremium: Boolean = false, val streakDays: Int = 0, val wordsToday: Int = 0, + /** Who is signed in; null when signed out. */ + val account: HomeAccount? = null, ) +/** The signed-in user as the Home header shows them. */ +data class HomeAccount(val name: String, val photoUrl: String?) + /** Callbacks for Home actions, owned by the Activity. */ class HomeActions( val onEnableKeyboard: () -> 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/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/strings.xml b/app/src/main/res/values/strings.xml index 4a7968a..c0db407 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,6 +52,8 @@ Welcome Back Sign in to continue chatting Continue with Google + Sign in + Keep PRO and your streak on every device By signing in, you agree to our Terms of Service and Privacy Policy @@ -62,7 +64,7 @@ Clear Chat Profile - Sign Out + Sign out Loading... @@ -245,7 +247,6 @@ Subscription Premium plan Free plan - Logout Done AI Assistant Online ยท Tibetan diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index cd1478a..f7ed06f 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,7 +1,9 @@ + + + + + + + + + +