Add theme persistence and comprehensive Android design guidelines
This commit adds persistent theme storage and comprehensive documentation for Android development. Theme Persistence: - Created ThemeStorage with platform-specific implementations (SharedPreferences/UserDefaults) - Updated ThemeManager.initialize() to load saved theme on app start - Integrated ThemeStorage initialization in MainActivity and MainViewController - Theme selection now persists across app restarts Documentation (CLAUDE.md): - Added comprehensive Android Design System section - Documented all 11 themes and theme management - Provided color system guidelines (use MaterialTheme.colorScheme) - Documented spacing system (AppSpacing/AppRadius constants) - Added standard component usage examples (StandardCard, FormTextField, etc.) - Included screen patterns (Scaffold, pull-to-refresh, lists) - Provided button and dialog patterns - Listed key design principles for Android development Build Status: - ✅ Android builds successfully - ✅ iOS builds successfully - ✅ Theme persistence works on both platforms 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,9 @@ import com.example.mycrib.storage.TokenManager
|
||||
import com.example.mycrib.storage.TokenStorage
|
||||
import com.example.mycrib.storage.TaskCacheManager
|
||||
import com.example.mycrib.storage.TaskCacheStorage
|
||||
import com.example.mycrib.storage.ThemeStorage
|
||||
import com.example.mycrib.storage.ThemeStorageManager
|
||||
import com.example.mycrib.ui.theme.ThemeManager
|
||||
import com.example.mycrib.fcm.FCMManager
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -42,6 +45,10 @@ class MainActivity : ComponentActivity(), SingletonImageLoader.Factory {
|
||||
// Initialize TaskCacheStorage for offline task caching
|
||||
TaskCacheStorage.initialize(TaskCacheManager.getInstance(applicationContext))
|
||||
|
||||
// Initialize ThemeStorage and ThemeManager
|
||||
ThemeStorage.initialize(ThemeStorageManager.getInstance(applicationContext))
|
||||
ThemeManager.initialize()
|
||||
|
||||
// Handle deep link from intent
|
||||
handleDeepLink(intent)
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.example.mycrib.storage
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
|
||||
/**
|
||||
* Android implementation of theme storage using SharedPreferences.
|
||||
*/
|
||||
actual class ThemeStorageManager(context: Context) {
|
||||
private val prefs: SharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
|
||||
actual fun saveThemeId(themeId: String) {
|
||||
prefs.edit().putString(KEY_THEME_ID, themeId).apply()
|
||||
}
|
||||
|
||||
actual fun getThemeId(): String? {
|
||||
return prefs.getString(KEY_THEME_ID, null)
|
||||
}
|
||||
|
||||
actual fun clearThemeId() {
|
||||
prefs.edit().remove(KEY_THEME_ID).apply()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PREFS_NAME = "mycrib_theme_prefs"
|
||||
private const val KEY_THEME_ID = "theme_id"
|
||||
|
||||
@Volatile
|
||||
private var instance: ThemeStorageManager? = null
|
||||
|
||||
fun getInstance(context: Context): ThemeStorageManager {
|
||||
return instance ?: synchronized(this) {
|
||||
instance ?: ThemeStorageManager(context.applicationContext).also { instance = it }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.example.mycrib.storage
|
||||
|
||||
/**
|
||||
* Cross-platform theme storage for persisting theme selection.
|
||||
* Uses platform-specific implementations (SharedPreferences on Android, UserDefaults on iOS).
|
||||
*/
|
||||
object ThemeStorage {
|
||||
private var manager: ThemeStorageManager? = null
|
||||
|
||||
fun initialize(themeManager: ThemeStorageManager) {
|
||||
manager = themeManager
|
||||
}
|
||||
|
||||
fun saveThemeId(themeId: String) {
|
||||
manager?.saveThemeId(themeId)
|
||||
}
|
||||
|
||||
fun getThemeId(): String? {
|
||||
return manager?.getThemeId()
|
||||
}
|
||||
|
||||
fun clearThemeId() {
|
||||
manager?.clearThemeId()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Platform-specific theme storage interface.
|
||||
* Each platform implements this using their native storage mechanisms.
|
||||
*/
|
||||
expect class ThemeStorageManager {
|
||||
fun saveThemeId(themeId: String)
|
||||
fun getThemeId(): String?
|
||||
fun clearThemeId()
|
||||
}
|
||||
@@ -3,11 +3,11 @@ package com.example.mycrib.ui.theme
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import com.example.mycrib.storage.ThemeStorage
|
||||
|
||||
/**
|
||||
* ThemeManager - Singleton for managing app themes
|
||||
* Matches iOS ThemeManager functionality
|
||||
* TODO: Add DataStore persistence
|
||||
* Matches iOS ThemeManager functionality with persistent storage
|
||||
*/
|
||||
object ThemeManager {
|
||||
private const val DEFAULT_THEME_ID = "default"
|
||||
@@ -19,16 +19,28 @@ object ThemeManager {
|
||||
private set
|
||||
|
||||
/**
|
||||
* Set theme by ID
|
||||
* Initialize theme manager and load saved theme
|
||||
* Call this after ThemeStorage.initialize()
|
||||
*/
|
||||
fun initialize() {
|
||||
val savedThemeId = ThemeStorage.getThemeId()
|
||||
if (savedThemeId != null) {
|
||||
val savedTheme = AppThemes.getThemeById(savedThemeId)
|
||||
currentTheme = savedTheme
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set theme by ID and persist the selection
|
||||
*/
|
||||
fun setTheme(themeId: String) {
|
||||
val newTheme = AppThemes.getThemeById(themeId)
|
||||
currentTheme = newTheme
|
||||
// TODO: Persist theme selection
|
||||
ThemeStorage.saveThemeId(themeId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set theme by ThemeColors object
|
||||
* Set theme by ThemeColors object and persist the selection
|
||||
*/
|
||||
fun setTheme(theme: ThemeColors) {
|
||||
setTheme(theme.id)
|
||||
@@ -42,7 +54,7 @@ object ThemeManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to default theme
|
||||
* Reset to default theme and persist the selection
|
||||
*/
|
||||
fun resetToDefault() {
|
||||
setTheme(DEFAULT_THEME_ID)
|
||||
|
||||
@@ -5,6 +5,9 @@ import com.example.mycrib.storage.TokenManager
|
||||
import com.example.mycrib.storage.TokenStorage
|
||||
import com.example.mycrib.storage.TaskCacheManager
|
||||
import com.example.mycrib.storage.TaskCacheStorage
|
||||
import com.example.mycrib.storage.ThemeStorage
|
||||
import com.example.mycrib.storage.ThemeStorageManager
|
||||
import com.example.mycrib.ui.theme.ThemeManager
|
||||
|
||||
fun MainViewController() = ComposeUIViewController {
|
||||
// Initialize TokenStorage with iOS TokenManager
|
||||
@@ -13,5 +16,9 @@ fun MainViewController() = ComposeUIViewController {
|
||||
// Initialize TaskCacheStorage for offline task caching
|
||||
TaskCacheStorage.initialize(TaskCacheManager.getInstance())
|
||||
|
||||
// Initialize ThemeStorage and ThemeManager
|
||||
ThemeStorage.initialize(ThemeStorageManager.getInstance())
|
||||
ThemeManager.initialize()
|
||||
|
||||
App()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.example.mycrib.storage
|
||||
|
||||
import platform.Foundation.NSUserDefaults
|
||||
|
||||
/**
|
||||
* iOS implementation of theme storage using NSUserDefaults.
|
||||
*/
|
||||
actual class ThemeStorageManager {
|
||||
private val defaults = NSUserDefaults.standardUserDefaults
|
||||
|
||||
actual fun saveThemeId(themeId: String) {
|
||||
defaults.setObject(themeId, forKey = KEY_THEME_ID)
|
||||
defaults.synchronize()
|
||||
}
|
||||
|
||||
actual fun getThemeId(): String? {
|
||||
return defaults.stringForKey(KEY_THEME_ID)
|
||||
}
|
||||
|
||||
actual fun clearThemeId() {
|
||||
defaults.removeObjectForKey(KEY_THEME_ID)
|
||||
defaults.synchronize()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY_THEME_ID = "theme_id"
|
||||
|
||||
private val instance by lazy { ThemeStorageManager() }
|
||||
|
||||
fun getInstance(): ThemeStorageManager = instance
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user