mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 02:25:21 +00:00
allow user to select a theme preference over light/dark/system (#318)
This commit is contained in:
@@ -2,6 +2,7 @@ package com.bitchat.android
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.bitchat.android.nostr.RelayDirectory
|
import com.bitchat.android.nostr.RelayDirectory
|
||||||
|
import com.bitchat.android.ui.theme.ThemePreferenceManager
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main application class for bitchat Android
|
* Main application class for bitchat Android
|
||||||
@@ -23,5 +24,8 @@ class BitchatApplication : Application() {
|
|||||||
try {
|
try {
|
||||||
com.bitchat.android.nostr.NostrIdentityBridge.getCurrentNostrIdentity(this)
|
com.bitchat.android.nostr.NostrIdentityBridge.getCurrentNostrIdentity(this)
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
|
|
||||||
|
// Initialize theme preference
|
||||||
|
ThemePreferenceManager.init(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,11 @@ import androidx.activity.compose.setContent
|
|||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
@@ -42,6 +40,8 @@ import com.bitchat.android.onboarding.PermissionManager
|
|||||||
import com.bitchat.android.ui.ChatScreen
|
import com.bitchat.android.ui.ChatScreen
|
||||||
import com.bitchat.android.ui.ChatViewModel
|
import com.bitchat.android.ui.ChatViewModel
|
||||||
import com.bitchat.android.ui.theme.BitchatTheme
|
import com.bitchat.android.ui.theme.BitchatTheme
|
||||||
|
import com.bitchat.android.ui.theme.ThemePreference
|
||||||
|
import com.bitchat.android.ui.theme.ThemePreferenceManager
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|||||||
@@ -161,6 +161,40 @@ fun AboutSheet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Appearance section (theme toggle)
|
||||||
|
item {
|
||||||
|
val themePref by com.bitchat.android.ui.theme.ThemePreferenceManager.themeFlow.collectAsState()
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "appearance",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = colorScheme.onSurface.copy(alpha = 0.8f)
|
||||||
|
)
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||||
|
FilterChip(
|
||||||
|
selected = themePref == com.bitchat.android.ui.theme.ThemePreference.System,
|
||||||
|
onClick = { com.bitchat.android.ui.theme.ThemePreferenceManager.set(context, com.bitchat.android.ui.theme.ThemePreference.System) },
|
||||||
|
label = { Text("system", fontFamily = FontFamily.Monospace) }
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
selected = themePref == com.bitchat.android.ui.theme.ThemePreference.Light,
|
||||||
|
onClick = { com.bitchat.android.ui.theme.ThemePreferenceManager.set(context, com.bitchat.android.ui.theme.ThemePreference.Light) },
|
||||||
|
label = { Text("light", fontFamily = FontFamily.Monospace) }
|
||||||
|
)
|
||||||
|
FilterChip(
|
||||||
|
selected = themePref == com.bitchat.android.ui.theme.ThemePreference.Dark,
|
||||||
|
onClick = { com.bitchat.android.ui.theme.ThemePreferenceManager.set(context, com.bitchat.android.ui.theme.ThemePreference.Dark) },
|
||||||
|
label = { Text("dark", fontFamily = FontFamily.Monospace) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Emergency warning
|
// Emergency warning
|
||||||
item {
|
item {
|
||||||
Surface(
|
Surface(
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import com.bitchat.android.R
|
|||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import com.bitchat.android.ui.theme.BASE_FONT_SIZE
|
import com.bitchat.android.ui.theme.BASE_FONT_SIZE
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input components for ChatScreen
|
* Input components for ChatScreen
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.darkColorScheme
|
import androidx.compose.material3.darkColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
// Colors that match the iOS bitchat theme
|
// Colors that match the iOS bitchat theme
|
||||||
@@ -36,13 +38,22 @@ private val LightColorScheme = lightColorScheme(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BitchatTheme(
|
fun BitchatTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean? = null,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
// App-level override from ThemePreferenceManager
|
||||||
darkTheme -> DarkColorScheme
|
val themePref by ThemePreferenceManager.themeFlow.collectAsState(initial = ThemePreference.System)
|
||||||
else -> LightColorScheme
|
val shouldUseDark = when (darkTheme) {
|
||||||
|
true -> true
|
||||||
|
false -> false
|
||||||
|
null -> when (themePref) {
|
||||||
|
ThemePreference.Dark -> true
|
||||||
|
ThemePreference.Light -> false
|
||||||
|
ThemePreference.System -> isSystemInDarkTheme()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val colorScheme = if (shouldUseDark) DarkColorScheme else LightColorScheme
|
||||||
|
|
||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.bitchat.android.ui.theme
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App theme preference: System default, Light, or Dark.
|
||||||
|
*/
|
||||||
|
enum class ThemePreference {
|
||||||
|
System, Light, Dark
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple SharedPreferences-backed manager for theme preference with a StateFlow.
|
||||||
|
* Avoids adding DataStore dependency for now.
|
||||||
|
*/
|
||||||
|
object ThemePreferenceManager {
|
||||||
|
private const val PREFS_NAME = "bitchat_settings"
|
||||||
|
private const val KEY_THEME = "theme_preference"
|
||||||
|
|
||||||
|
private val _themeFlow = MutableStateFlow(ThemePreference.System)
|
||||||
|
val themeFlow: StateFlow<ThemePreference> = _themeFlow
|
||||||
|
|
||||||
|
fun init(context: Context) {
|
||||||
|
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
val saved = prefs.getString(KEY_THEME, ThemePreference.System.name)
|
||||||
|
_themeFlow.value = runCatching { ThemePreference.valueOf(saved!!) }.getOrDefault(ThemePreference.System)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun set(context: Context, preference: ThemePreference) {
|
||||||
|
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
prefs.edit().putString(KEY_THEME, preference.name).apply()
|
||||||
|
_themeFlow.value = preference
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user