Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ dependencies {
//Permissions
implementation "com.google.accompanist:accompanist-permissions:0.23.1"

// DataStore
implementation "androidx.datastore:datastore-preferences:1.0.0"

// Room
def roomVersion = "2.6.1"
implementation "androidx.room:room-runtime:$roomVersion"
Expand Down
10 changes: 9 additions & 1 deletion app/src/main/java/com/bera/josaahelpertool/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.os.Bundle
import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.runtime.collectAsState
Expand All @@ -17,6 +18,7 @@ import com.bera.josaahelpertool.network.connectivity.ConnectivityObserver
import com.bera.josaahelpertool.network.connectivity.ConnectivityStatus
import com.bera.josaahelpertool.screens.home.NetworkErrorScreen
import com.bera.josaahelpertool.ui.theme.CollegeSearchTheme
import com.bera.josaahelpertool.ui.theme.ThemeViewModel
import dagger.hilt.android.AndroidEntryPoint
import java.io.File
import javax.inject.Inject
Expand All @@ -25,13 +27,19 @@ import javax.inject.Inject
class MainActivity : ComponentActivity() {

@Inject lateinit var connectivityObserver: ConnectivityObserver
private val themeViewModel: ThemeViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
val darkTheme = themeViewModel.shouldUseDarkTheme()
val uiState by themeViewModel.uiState.collectAsState()

CollegeSearchTheme {
CollegeSearchTheme(
darkTheme = darkTheme,
dynamicColor = uiState.dynamicColor
) {

val status by connectivityObserver.observe()
.collectAsState(initial = ConnectivityStatus.Unavailable)
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/com/bera/josaahelpertool/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.bera.josaahelpertool.network.okhttp.CacheInterceptor
import com.bera.josaahelpertool.network.okhttp.ForceCacheInterceptor
import com.bera.josaahelpertool.repository.CutoffRepository
import com.bera.josaahelpertool.repository.UniversityImageRepository
import com.bera.josaahelpertool.ui.theme.ThemeDataStore
import com.bera.josaahelpertool.utils.Constants
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -80,4 +81,9 @@ object AppModule {
@Singleton
@Provides
fun provideContext(@ApplicationContext appContext: Context): Context = appContext

@Singleton
@Provides
fun provideThemeDataStore(@ApplicationContext appContext: Context): ThemeDataStore =
ThemeDataStore(appContext)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.bera.josaahelpertool.screens.home.HomeScreen
import com.bera.josaahelpertool.screens.home.HomeViewModel
import com.bera.josaahelpertool.screens.search.SearchScreen
import com.bera.josaahelpertool.screens.search.SearchViewModel
import com.bera.josaahelpertool.ui.theme.ThemeViewModel

@Composable
fun Navigation() {
Expand All @@ -28,7 +29,8 @@ fun Navigation() {
) {
composable(Routes.HomeScreen.route) {
val homeViewModel = hiltViewModel<HomeViewModel>()
HomeScreen(navController = navController, viewModel = homeViewModel)
val themeViewModel = hiltViewModel<ThemeViewModel>()
HomeScreen(navController = navController, viewModel = homeViewModel, themeViewModel = themeViewModel)
}
composable(Routes.CollegeScreen.route + "/{category}", listOf(navArgument("category") {
type = NavType.StringType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import coil.compose.AsyncImage
import com.bera.josaahelpertool.components.ThemeSwitcher
import com.bera.josaahelpertool.models.ui.TopHalfItem
import com.bera.josaahelpertool.navigation.Routes
import com.bera.josaahelpertool.ui.theme.ThemeViewModel
import com.bera.josaahelpertool.ui.theme.rubikFamily
import com.bera.josaahelpertool.utils.CustomDivider
import com.bera.josaahelpertool.utils.ShimmerListItem
Expand All @@ -79,7 +81,8 @@ import kotlin.reflect.KSuspendFunction2
@Composable
fun HomeScreen(
navController: NavController,
viewModel: HomeViewModel
viewModel: HomeViewModel,
themeViewModel: ThemeViewModel
) {


Expand Down Expand Up @@ -110,6 +113,26 @@ fun HomeScreen(
item {
Spacer(modifier = Modifier.height(12.dp))
}
item {
// Theme Switcher
val isDarkTheme = themeViewModel.shouldUseDarkTheme()

Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.End
) {
ThemeSwitcher(
darkTheme = isDarkTheme,
size = 30.dp,
onClick = { themeViewModel.toggleTheme() }
)
}
}
item {
Spacer(modifier = Modifier.height(12.dp))
}
item {
// Create a search bar to type and search for colleges
Row(
Expand Down
61 changes: 30 additions & 31 deletions app/src/main/java/com/bera/josaahelpertool/ui/theme/Color.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,36 +33,35 @@ val md_theme_light_surfaceTint = Color(0xFF0062A1)
val md_theme_light_outlineVariant = Color(0xFFC2C7CF)
val md_theme_light_scrim = Color(0xFF000000)

//val md_theme_dark_primary = Color(0xFF9DCAFF)
//val md_theme_dark_onPrimary = Color(0xFF003257)
//val md_theme_dark_primaryContainer = Color(0xFF00497B)
//val md_theme_dark_onPrimaryContainer = Color(0xFFD1E4FF)
//val md_theme_dark_secondary = Color(0xFFBAC8DB)
//val md_theme_dark_onSecondary = Color(0xFF253140)
//val md_theme_dark_secondaryContainer = Color(0xFF3B4858)
//val md_theme_dark_onSecondaryContainer = Color(0xFFD6E4F7)
//val md_theme_dark_tertiary = Color(0xFFB2C5FF)
//val md_theme_dark_onTertiary = Color(0xFF002B73)
//val md_theme_dark_tertiaryContainer = Color(0xFF1E438F)
//val md_theme_dark_onTertiaryContainer = Color(0xFFDAE2FF)
//val md_theme_dark_error = Color(0xFFFFB4AB)
//val md_theme_dark_errorContainer = Color(0xFF93000A)
//val md_theme_dark_onError = Color(0xFF690005)
//val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
//val md_theme_dark_background = Color(0xFF1A1C1E)
//val md_theme_dark_onBackground = Color(0xFFE2E2E6)
//val md_theme_dark_surface = Color(0xFF1A1C1E)
//val md_theme_dark_onSurface = Color(0xFFE2E2E6)
//val md_theme_dark_surfaceVariant = Color(0xFF42474E)
//val md_theme_dark_onSurfaceVariant = Color(0xFFC2C7CF)
//val md_theme_dark_outline = Color(0xFF8C9199)
//val md_theme_dark_inverseOnSurface = Color(0xFF1A1C1E)
//val md_theme_dark_inverseSurface = Color(0xFFE2E2E6)
//val md_theme_dark_inversePrimary = Color(0xFF0062A1)
//val md_theme_dark_shadow = Color(0xFF000000)
//val md_theme_dark_surfaceTint = Color(0xFF9DCAFF)
//val md_theme_dark_outlineVariant = Color(0xFF42474E)
//val md_theme_dark_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFF9DCAFF)
val md_theme_dark_onPrimary = Color(0xFF003257)
val md_theme_dark_primaryContainer = Color(0xFF00497B)
val md_theme_dark_onPrimaryContainer = Color(0xFFD1E4FF)
val md_theme_dark_secondary = Color(0xFFBAC8DB)
val md_theme_dark_onSecondary = Color(0xFF253140)
val md_theme_dark_secondaryContainer = Color(0xFF3B4858)
val md_theme_dark_onSecondaryContainer = Color(0xFFD6E4F7)
val md_theme_dark_tertiary = Color(0xFFB2C5FF)
val md_theme_dark_onTertiary = Color(0xFF002B73)
val md_theme_dark_tertiaryContainer = Color(0xFF1E438F)
val md_theme_dark_onTertiaryContainer = Color(0xFFDAE2FF)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1A1C1E)
val md_theme_dark_onBackground = Color(0xFFE2E2E6)
val md_theme_dark_surface = Color(0xFF1A1C1E)
val md_theme_dark_onSurface = Color(0xFFE2E2E6)
val md_theme_dark_surfaceVariant = Color(0xFF42474E)
val md_theme_dark_onSurfaceVariant = Color(0xFFC2C7CF)
val md_theme_dark_outline = Color(0xFF8C9199)
val md_theme_dark_inverseOnSurface = Color(0xFF1A1C1E)
val md_theme_dark_inverseSurface = Color(0xFFE2E2E6)
val md_theme_dark_inversePrimary = Color(0xFF0062A1)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFF9DCAFF)
val md_theme_dark_outlineVariant = Color(0xFF42474E)
val md_theme_dark_scrim = Color(0xFF000000)

val seed = Color(0xFF109CFC)
66 changes: 32 additions & 34 deletions app/src/main/java/com/bera/josaahelpertool/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,42 @@ private val _lightColorScheme = lightColorScheme(
)

private val _darkColorScheme = darkColorScheme(
// primary = md_theme_dark_primary,
// onPrimary = md_theme_dark_onPrimary,
// primaryContainer = md_theme_dark_primaryContainer,
// onPrimaryContainer = md_theme_dark_onPrimaryContainer,
// secondary = md_theme_dark_secondary,
// onSecondary = md_theme_dark_onSecondary,
// secondaryContainer = md_theme_dark_secondaryContainer,
// onSecondaryContainer = md_theme_dark_onSecondaryContainer,
// tertiary = md_theme_dark_tertiary,
// onTertiary = md_theme_dark_onTertiary,
// tertiaryContainer = md_theme_dark_tertiaryContainer,
// onTertiaryContainer = md_theme_dark_onTertiaryContainer,
// error = md_theme_dark_error,
// errorContainer = md_theme_dark_errorContainer,
// onError = md_theme_dark_onError,
// onErrorContainer = md_theme_dark_onErrorContainer,
// background = md_theme_dark_background,
// onBackground = md_theme_dark_onBackground,
// surface = md_theme_dark_surface,
// onSurface = md_theme_dark_onSurface,
// surfaceVariant = md_theme_dark_surfaceVariant,
// onSurfaceVariant = md_theme_dark_onSurfaceVariant,
// outline = md_theme_dark_outline,
// inverseOnSurface = md_theme_dark_inverseOnSurface,
// inverseSurface = md_theme_dark_inverseSurface,
// inversePrimary = md_theme_dark_inversePrimary,
// surfaceTint = md_theme_dark_surfaceTint,
// outlineVariant = md_theme_dark_outlineVariant,
// scrim = md_theme_dark_scrim,
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)

@Composable
fun CollegeSearchTheme(
darkTheme: Boolean = false, // isSystemInDarkTheme() (to implement dark theme)
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = false, // true (to implement dynamicColor)
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
Expand All @@ -98,9 +98,7 @@ fun CollegeSearchTheme(
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor =
if (darkTheme) _darkColorScheme.surface.toArgb() else _lightColorScheme.surface.toArgb()

window.statusBarColor = colorScheme.surface.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.bera.josaahelpertool.ui.theme

import android.content.Context
import android.util.Log
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "app_theme_preferences")

enum class ThemeMode {
SYSTEM, LIGHT, DARK
}

@Singleton
class ThemeDataStore @Inject constructor(
private val context: Context
) {
private val THEME_MODE_KEY = stringPreferencesKey("app_theme_mode")
private val DYNAMIC_COLOR_KEY = booleanPreferencesKey("app_dynamic_color")

val themeMode: Flow<ThemeMode> = context.dataStore.data.map { preferences ->
try {
val themeModeString = preferences[THEME_MODE_KEY]
when (themeModeString) {
ThemeMode.LIGHT.name -> ThemeMode.LIGHT
ThemeMode.DARK.name -> ThemeMode.DARK
else -> ThemeMode.SYSTEM
}
} catch (e: Exception) {
// If there's any error reading the preference, default to SYSTEM
Log.e("ThemeDataStore", "Error reading theme mode preference", e)
ThemeMode.SYSTEM
}
}

val dynamicColor: Flow<Boolean> = context.dataStore.data.map { preferences ->
try {
preferences[DYNAMIC_COLOR_KEY] ?: false
} catch (e: Exception) {
// If there's any error reading the preference, default to false
Log.e("ThemeDataStore", "Error reading dynamic color preference", e)
false
}
}

suspend fun setThemeMode(themeMode: ThemeMode) {
context.dataStore.edit { preferences ->
preferences[THEME_MODE_KEY] = themeMode.name
}
}

suspend fun setDynamicColor(enabled: Boolean) {
context.dataStore.edit { preferences ->
preferences[DYNAMIC_COLOR_KEY] = enabled
}
}

suspend fun clearPreferences() {
context.dataStore.edit { preferences ->
preferences.clear()
}
}
}
Loading