feat: font scaler added

This commit is contained in:
2026-06-14 15:59:48 +03:30
parent 1971708b3f
commit f266f4d142
5 changed files with 83 additions and 2 deletions
@@ -12,8 +12,11 @@ import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.approagency.pharmacy.data.local.SessionManager import com.approagency.pharmacy.data.local.SessionManager
@@ -52,11 +55,22 @@ class MainActivity : ComponentActivity(), OtpAutofillController {
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
val themeMode by session.themeMode.collectAsState() val themeMode by session.themeMode.collectAsState()
val fontScale by session.fontScale.collectAsState()
DrugTheme(themeMode = themeMode) { DrugTheme(themeMode = themeMode) {
// مقیاسِ متنِ انتخابیِ کاربر روی fontScaleِ سیستم اعمال می‌شود
// تا کلِ متن‌های اپ یک‌جا بزرگ/کوچک شوند.
val density = LocalDensity.current
CompositionLocalProvider(
LocalDensity provides Density(
density = density.density,
fontScale = density.fontScale * fontScale.scale
)
) {
AppNavGraph() AppNavGraph()
} }
} }
} }
}
/** /**
* هنگام شروع اپ و هر بار بازگشت به اپ (مثلاً پس از بازگشت از درگاه پرداختِ * هنگام شروع اپ و هر بار بازگشت به اپ (مثلاً پس از بازگشت از درگاه پرداختِ
@@ -2,6 +2,7 @@ package com.approagency.pharmacy.data.local
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import com.approagency.pharmacy.domain.model.FontScale
import com.approagency.pharmacy.domain.model.ThemeMode import com.approagency.pharmacy.domain.model.ThemeMode
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -128,6 +129,20 @@ class SessionManager(context: Context) {
runCatching { ThemeMode.valueOf(prefs.getString(KEY_THEME_MODE, null) ?: ThemeMode.SYSTEM.name) } runCatching { ThemeMode.valueOf(prefs.getString(KEY_THEME_MODE, null) ?: ThemeMode.SYSTEM.name) }
.getOrDefault(ThemeMode.SYSTEM) .getOrDefault(ThemeMode.SYSTEM)
// ---------- اندازهٔ متن ----------
private val _fontScale = MutableStateFlow(readFontScale())
val fontScale: StateFlow<FontScale> = _fontScale.asStateFlow()
fun setFontScale(scale: FontScale) {
prefs.edit().putString(KEY_FONT_SCALE, scale.name).apply()
_fontScale.value = scale
}
private fun readFontScale(): FontScale =
runCatching { FontScale.valueOf(prefs.getString(KEY_FONT_SCALE, null) ?: FontScale.NORMAL.name) }
.getOrDefault(FontScale.NORMAL)
private fun readAccount() = AccountState( private fun readAccount() = AccountState(
isLoggedIn = !token.isNullOrBlank(), isLoggedIn = !token.isNullOrBlank(),
mobile = prefs.getString(KEY_MOBILE, null), mobile = prefs.getString(KEY_MOBILE, null),
@@ -147,5 +162,6 @@ class SessionManager(context: Context) {
const val KEY_SUB_TITLE = "subscription_title" const val KEY_SUB_TITLE = "subscription_title"
const val KEY_SUB_EXPIRE = "subscription_expire_at" const val KEY_SUB_EXPIRE = "subscription_expire_at"
const val KEY_THEME_MODE = "theme_mode" const val KEY_THEME_MODE = "theme_mode"
const val KEY_FONT_SCALE = "font_scale"
} }
} }
@@ -0,0 +1,11 @@
package com.approagency.pharmacy.domain.model
/**
* اندازهٔ متنِ انتخابیِ کاربر (بهویژه برای کاربرانِ مسنتر).
* [scale] ضریبی است که روی fontScaleِ دستگاه اعمال میشود.
*/
enum class FontScale(val scale: Float, val label: String) {
NORMAL(1.0f, "معمولی"),
LARGE(1.2f, "بزرگ"),
XLARGE(1.45f, "خیلی بزرگ")
}
@@ -39,6 +39,7 @@ fun MainContainer(
val account by session.account.collectAsState() val account by session.account.collectAsState()
val sheetVisible by sheetController.visible.collectAsState() val sheetVisible by sheetController.visible.collectAsState()
val themeMode by session.themeMode.collectAsState() val themeMode by session.themeMode.collectAsState()
val fontScale by session.fontScale.collectAsState()
val drawerState = rememberDrawerState(DrawerValue.Closed) val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -59,6 +60,8 @@ fun MainContainer(
appVersion = appVersion, appVersion = appVersion,
themeMode = themeMode, themeMode = themeMode,
onThemeModeChange = { session.setThemeMode(it) }, onThemeModeChange = { session.setThemeMode(it) },
fontScale = fontScale,
onFontScaleChange = { session.setFontScale(it) },
onLogout = { showLogoutConfirm = true } onLogout = { showLogoutConfirm = true }
) )
} }
@@ -1,6 +1,8 @@
package com.approagency.pharmacy.presentation.account package com.approagency.pharmacy.presentation.account
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -43,6 +45,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.approagency.pharmacy.data.local.AccountState import com.approagency.pharmacy.data.local.AccountState
import com.approagency.pharmacy.domain.model.FontScale
import com.approagency.pharmacy.domain.model.ThemeMode import com.approagency.pharmacy.domain.model.ThemeMode
import com.vada.caller.ui.theme.dime import com.vada.caller.ui.theme.dime
@@ -60,6 +63,8 @@ fun AccountDrawer(
appVersion: String, appVersion: String,
themeMode: ThemeMode, themeMode: ThemeMode,
onThemeModeChange: (ThemeMode) -> Unit, onThemeModeChange: (ThemeMode) -> Unit,
fontScale: FontScale,
onFontScaleChange: (FontScale) -> Unit,
onLogout: () -> Unit onLogout: () -> Unit
) { ) {
ModalDrawerSheet( ModalDrawerSheet(
@@ -71,6 +76,7 @@ fun AccountDrawer(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.fillMaxWidth() .fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(horizontal = MaterialTheme.dime.lg, vertical = MaterialTheme.dime.xl) .padding(horizontal = MaterialTheme.dime.lg, vertical = MaterialTheme.dime.xl)
) { ) {
Header(account) Header(account)
@@ -121,7 +127,18 @@ fun AccountDrawer(
Spacer(Modifier.height(MaterialTheme.dime.sm)) Spacer(Modifier.height(MaterialTheme.dime.sm))
ThemeModeSelector(selected = themeMode, onSelect = onThemeModeChange) ThemeModeSelector(selected = themeMode, onSelect = onThemeModeChange)
Spacer(Modifier.weight(1f)) Spacer(Modifier.height(MaterialTheme.dime.lg))
// اندازهٔ متن (برای کاربرانِ مسن‌تر)
Text(
text = "اندازهٔ متن",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
Spacer(Modifier.height(MaterialTheme.dime.sm))
FontScaleSelector(selected = fontScale, onSelect = onFontScaleChange)
Spacer(Modifier.height(MaterialTheme.dime.xl))
if (account.isLoggedIn) { if (account.isLoggedIn) {
OutlinedButton( OutlinedButton(
@@ -267,3 +284,23 @@ private fun ThemeModeSelector(
} }
} }
} }
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun FontScaleSelector(
selected: FontScale,
onSelect: (FontScale) -> Unit
) {
val options = FontScale.entries
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
options.forEachIndexed { index, option ->
SegmentedButton(
selected = selected == option,
onClick = { onSelect(option) },
shape = SegmentedButtonDefaults.itemShape(index, options.size),
icon = {},
label = { Text(option.label, style = MaterialTheme.typography.labelSmall) }
)
}
}
}