diff --git a/TODO.md b/TODO.md index 529ec0a..f4f54be 100644 --- a/TODO.md +++ b/TODO.md @@ -29,6 +29,7 @@ x CI pipeline (gradle?) - Add NSFW filter - Translate all strings - Swipe player down and up +- implement preferences using data stores Issues: - Server change doesn't work until restart (retrofit) diff --git a/app/src/main/java/net/schueller/peertube/common/Constants.kt b/app/src/main/java/net/schueller/peertube/common/Constants.kt index 20731c9..0941cef 100644 --- a/app/src/main/java/net/schueller/peertube/common/Constants.kt +++ b/app/src/main/java/net/schueller/peertube/common/Constants.kt @@ -3,8 +3,7 @@ package net.schueller.peertube.common object Constants { const val PREF_LANG_APP_KEY = "pref_language_app" - const val PREF_THEME_KEY = "pref_theme" - const val PREF_DARK_MODE_KEY = "pref_dark_mode" + const val PREF_SHOW_NSFW_KEY = "pref_show_nsfw" const val PREF_VIDEO_LANG_KEY = "pref_show_nsfw" const val PREF_VIDEO_SPEED_KEY = "pref_video_speed" @@ -43,7 +42,14 @@ object Constants { const val APP_BACKGROUND_AUDIO_INTENT = "BACKGROUND_AUDIO" + const val PREF_THEME_KEY = "pref_theme" + const val PREF_DARK_MODE_KEY = "pref_dark_mode_2" + const val PREF_DARK_MODE_DARK = "dark" + const val PREF_DARK_MODE_LIGHT = "light" + const val PREF_DARK_MODE_AUTO = "auto" + // legacy color prefs + const val COLOR_PREF_DEFAULT = "AppTheme.RED" const val COLOR_PREF_RED = "AppTheme.RED" const val COLOR_PREF_PINK = "AppTheme.PINK" const val COLOR_PREF_PURPLE = "AppTheme.PURPLE" diff --git a/app/src/main/java/net/schueller/peertube/di/AppModule.kt b/app/src/main/java/net/schueller/peertube/di/AppModule.kt index 6e01297..7ab2cfe 100644 --- a/app/src/main/java/net/schueller/peertube/di/AppModule.kt +++ b/app/src/main/java/net/schueller/peertube/di/AppModule.kt @@ -19,6 +19,8 @@ import net.schueller.peertube.feature_server_address.domain.repository.ServerAdd import net.schueller.peertube.feature_server_address.domain.repository.ServerRepository import net.schueller.peertube.feature_video.domain.repository.VideoRepository import net.schueller.peertube.feature_server_address.domain.use_case.* +import net.schueller.peertube.feature_settings.settings.data.repository.SettingsRepositoryImpl +import net.schueller.peertube.feature_settings.settings.domain.repository.SettingsRepository import net.schueller.peertube.feature_video.data.remote.auth.LoginService import net.schueller.peertube.feature_video.data.remote.auth.Session import net.schueller.peertube.feature_video.data.repository.RetrofitInstance @@ -78,6 +80,12 @@ object AppModule { return VideoRepositoryImpl(api) } + @Provides + @Singleton + fun provideSettingsRepository(@ApplicationContext context: Context): SettingsRepository { + return SettingsRepositoryImpl(context) + } + @Provides @Singleton fun provideServerInstanceApi(): ServerInstanceApi { diff --git a/app/src/main/java/net/schueller/peertube/feature_settings/settings/data/repository/SettingsRepositoryImpl.kt b/app/src/main/java/net/schueller/peertube/feature_settings/settings/data/repository/SettingsRepositoryImpl.kt new file mode 100644 index 0000000..ec35d46 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/feature_settings/settings/data/repository/SettingsRepositoryImpl.kt @@ -0,0 +1,35 @@ +package net.schueller.peertube.feature_settings.settings.data.repository + +import android.content.Context +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import net.schueller.peertube.feature_settings.settings.domain.repository.SettingsRepository +import net.schueller.peertube.presentation.dataStore + +class SettingsRepositoryImpl(private val context: Context) : SettingsRepository { + + override suspend fun getBooleanSettings(key: String, default: Boolean): Flow { + val dataStoreKey = booleanPreferencesKey(key) + return context.dataStore.data.map { preferences -> + preferences[dataStoreKey] ?: default + } + } + + override suspend fun getStringSettings(key: String, default: String): Flow { + val dataStoreKey = stringPreferencesKey(key) + return context.dataStore.data.map { preferences -> + preferences[dataStoreKey] ?: default + } + } + + override suspend fun getIntegerSettings(key: String, default: Int): Flow { + val dataStoreKey = intPreferencesKey(key) + return context.dataStore.data.map { preferences -> + preferences[dataStoreKey] ?: default + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/feature_settings/settings/domain/repository/SettingsRepository.kt b/app/src/main/java/net/schueller/peertube/feature_settings/settings/domain/repository/SettingsRepository.kt new file mode 100644 index 0000000..b25a7bc --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/feature_settings/settings/domain/repository/SettingsRepository.kt @@ -0,0 +1,11 @@ +package net.schueller.peertube.feature_settings.settings.domain.repository + +import kotlinx.coroutines.flow.Flow + +interface SettingsRepository { + + suspend fun getBooleanSettings(key: String, default: Boolean): Flow + suspend fun getStringSettings(key: String, default: String): Flow + suspend fun getIntegerSettings(key: String, default: Int): Flow + +} \ No newline at end of file diff --git a/app/src/main/java/net/schueller/peertube/feature_settings/settings/SettingsScreen.kt b/app/src/main/java/net/schueller/peertube/feature_settings/settings/presentation/SettingsScreen.kt similarity index 99% rename from app/src/main/java/net/schueller/peertube/feature_settings/settings/SettingsScreen.kt rename to app/src/main/java/net/schueller/peertube/feature_settings/settings/presentation/SettingsScreen.kt index 0ed48f7..105bc8b 100644 --- a/app/src/main/java/net/schueller/peertube/feature_settings/settings/SettingsScreen.kt +++ b/app/src/main/java/net/schueller/peertube/feature_settings/settings/presentation/SettingsScreen.kt @@ -1,4 +1,4 @@ -package net.schueller.peertube.feature_settings.settings +package net.schueller.peertube.feature_settings.settings.presentation import androidx.compose.material.* import androidx.compose.runtime.Composable @@ -7,7 +7,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -import com.jamal.composeprefs.ui.GroupHeader import com.jamal.composeprefs.ui.PrefsScreen import com.jamal.composeprefs.ui.prefs.* import net.schueller.peertube.presentation.dataStore diff --git a/app/src/main/java/net/schueller/peertube/feature_video/presentation/video/VideoListScreen.kt b/app/src/main/java/net/schueller/peertube/feature_video/presentation/video/VideoListScreen.kt index c5f5fd5..e481da7 100644 --- a/app/src/main/java/net/schueller/peertube/feature_video/presentation/video/VideoListScreen.kt +++ b/app/src/main/java/net/schueller/peertube/feature_video/presentation/video/VideoListScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.Scaffold +import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -15,6 +16,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -35,6 +37,7 @@ import net.schueller.peertube.feature_video.presentation.video.components.appBar import net.schueller.peertube.feature_video.presentation.video.components.videoPlay.VideoPlayScreen import net.schueller.peertube.feature_video.presentation.video.events.VideoPlayEvent import net.schueller.peertube.feature_video.presentation.video.player.ExoPlayerHolder +import net.schueller.peertube.presentation.ui.theme.PeertubeTheme import kotlin.math.roundToInt @OptIn(ExperimentalMaterialApi::class) diff --git a/app/src/main/java/net/schueller/peertube/presentation/MainActivity.kt b/app/src/main/java/net/schueller/peertube/presentation/MainActivity.kt index 8942c3b..bfc0650 100644 --- a/app/src/main/java/net/schueller/peertube/presentation/MainActivity.kt +++ b/app/src/main/java/net/schueller/peertube/presentation/MainActivity.kt @@ -15,10 +15,8 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStore @@ -27,7 +25,6 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument -import coil.annotation.ExperimentalCoilApi import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState import com.google.android.exoplayer2.ui.PlayerNotificationManager @@ -40,7 +37,7 @@ import net.schueller.peertube.common.Constants.PREF_BACKGROUND_STOP_KEY import net.schueller.peertube.common.Constants.PREF_BACK_PAUSE_KEY import net.schueller.peertube.common.VideoHelper import net.schueller.peertube.feature_server_address.presentation.address_add_edit.AddEditAddressScreen -import net.schueller.peertube.feature_settings.settings.SettingsScreen +import net.schueller.peertube.feature_settings.settings.presentation.SettingsScreen import net.schueller.peertube.presentation.ui.theme.PeertubeTheme import net.schueller.peertube.feature_video.presentation.video.VideoListScreen import net.schueller.peertube.feature_server_address.presentation.ServerAddressScreen @@ -64,7 +61,8 @@ class MainActivity : ComponentActivity() { @OptIn(ExperimentalPermissionsApi::class, androidx.compose.material.ExperimentalMaterialApi::class, - androidx.compose.ui.ExperimentalComposeUiApi::class + androidx.compose.ui.ExperimentalComposeUiApi::class, + coil.annotation.ExperimentalCoilApi::class ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -95,7 +93,9 @@ class MainActivity : ComponentActivity() { // } // ) - Surface(color = MaterialTheme.colors.background) { + Surface( + color = MaterialTheme.colors.background + ) { val navController = rememberNavController() NavHost( navController = navController, diff --git a/app/src/main/java/net/schueller/peertube/presentation/ui/theme/Theme.kt b/app/src/main/java/net/schueller/peertube/presentation/ui/theme/Theme.kt index de8deb0..16dc422 100644 --- a/app/src/main/java/net/schueller/peertube/presentation/ui/theme/Theme.kt +++ b/app/src/main/java/net/schueller/peertube/presentation/ui/theme/Theme.kt @@ -1,97 +1,78 @@ package net.schueller.peertube.presentation.ui.theme -import android.content.Context import android.util.Log import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.booleanPreferencesKey -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import net.schueller.peertube.common.Constants +import androidx.hilt.navigation.compose.hiltViewModel import net.schueller.peertube.common.Constants.COLOR_PREF_BLUE import net.schueller.peertube.common.Constants.COLOR_PREF_GREEN import net.schueller.peertube.common.Constants.COLOR_PREF_RED -import net.schueller.peertube.presentation.dataStore - +import net.schueller.peertube.common.Constants.PREF_DARK_MODE_AUTO +import net.schueller.peertube.common.Constants.PREF_DARK_MODE_DARK @Composable fun PeertubeTheme( useDarkTheme: Boolean = isSystemInDarkTheme(), + viewModel: ThemeViewModel = hiltViewModel(), content: @Composable() () -> Unit ) { - val context = LocalContext.current - val sharedPreferences = context.getSharedPreferences(context.packageName + "_preferences", Context.MODE_PRIVATE) - - - // TODO: get pref out of dataStore, migrate old prefs -// LocalContext.current.dataStore.data.map { -// -// } - - - -// val EXAMPLE_COUNTER = booleanPreferencesKey(Constants.PREF_DARK_MODE_KEY) -// val useDarkMode: Flow = LocalContext.current.dataStore.data -// .map { preferences -> -// preferences[EXAMPLE_COUNTER] ?: useDarkTheme -// } -// - - - val useDarkMode = sharedPreferences.getBoolean( - Constants.PREF_DARK_MODE_KEY, + val useDarkMode = if (viewModel.themeState.value.darkMode == PREF_DARK_MODE_AUTO) { useDarkTheme - ) - - Log.v("TH", "userdark: "+useDarkMode) - - val theme = sharedPreferences.getString( - Constants.PREF_THEME_KEY, - COLOR_PREF_BLUE - ) - Log.v("TH", "theme: "+theme) + } else viewModel.themeState.value.darkMode == PREF_DARK_MODE_DARK + Log.v("TH", "useDarkMode : "+useDarkMode) + Log.v("TH", "viewModel : "+viewModel.themeState.value.currentTheme) // Support existing saved preferences in older version // https://material-foundation.github.io/material-theme-builder/ val colors = if (!useDarkMode) { - when (theme) { + when (viewModel.themeState.value.currentTheme) { COLOR_PREF_BLUE -> { + Log.v("TH", "use COLOR_PREF_BLUE : "+COLOR_PREF_BLUE) + net.schueller.peertube.presentation.ui.theme.colors.blue.LightThemeColors } COLOR_PREF_RED -> { + Log.v("TH", "use COLOR_PREF_RED : "+COLOR_PREF_RED) + net.schueller.peertube.presentation.ui.theme.colors.red.LightThemeColors } COLOR_PREF_GREEN -> { + Log.v("TH", "use COLOR_PREF_GREEN : "+COLOR_PREF_GREEN) + net.schueller.peertube.presentation.ui.theme.colors.green.LightThemeColors } else -> { + Log.v("TH", "else : ") + net.schueller.peertube.presentation.ui.theme.colors.def.LightThemeColors } } } else { - when (theme) { + when (viewModel.themeState.value.currentTheme) { COLOR_PREF_BLUE -> { + Log.v("TH", "use COLOR_PREF_BLUE : "+COLOR_PREF_BLUE) net.schueller.peertube.presentation.ui.theme.colors.blue.DarkThemeColors } COLOR_PREF_RED -> { + Log.v("TH", "use COLOR_PREF_RED : "+COLOR_PREF_RED) net.schueller.peertube.presentation.ui.theme.colors.red.DarkThemeColors } COLOR_PREF_GREEN -> { - Log.v("TH", "use green : "+COLOR_PREF_GREEN) - + Log.v("TH", "use COLOR_PREF_GREEN : "+COLOR_PREF_GREEN) net.schueller.peertube.presentation.ui.theme.colors.green.DarkThemeColors } else -> { + Log.v("TH", "else : ") net.schueller.peertube.presentation.ui.theme.colors.def.DarkThemeColors } } } + Log.v("TH", "colors : " + colors.primary) + MaterialTheme( colorScheme = colors, typography = AppTypography, diff --git a/app/src/main/java/net/schueller/peertube/presentation/ui/theme/ThemeState.kt b/app/src/main/java/net/schueller/peertube/presentation/ui/theme/ThemeState.kt new file mode 100644 index 0000000..1103cef --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/presentation/ui/theme/ThemeState.kt @@ -0,0 +1,9 @@ +package net.schueller.peertube.presentation.ui.theme + +import net.schueller.peertube.common.Constants.COLOR_PREF_DEFAULT +import net.schueller.peertube.common.Constants.PREF_DARK_MODE_AUTO + +data class ThemeState( + val darkMode: String = PREF_DARK_MODE_AUTO, + val currentTheme: String = COLOR_PREF_DEFAULT +) diff --git a/app/src/main/java/net/schueller/peertube/presentation/ui/theme/ThemeViewModel.kt b/app/src/main/java/net/schueller/peertube/presentation/ui/theme/ThemeViewModel.kt new file mode 100644 index 0000000..40fc956 --- /dev/null +++ b/app/src/main/java/net/schueller/peertube/presentation/ui/theme/ThemeViewModel.kt @@ -0,0 +1,39 @@ +package net.schueller.peertube.presentation.ui.theme + +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import net.schueller.peertube.common.Constants.COLOR_PREF_DEFAULT +import net.schueller.peertube.common.Constants.PREF_DARK_MODE_AUTO +import net.schueller.peertube.common.Constants.PREF_DARK_MODE_KEY +import net.schueller.peertube.common.Constants.PREF_THEME_KEY +import net.schueller.peertube.feature_settings.settings.domain.repository.SettingsRepository +import javax.inject.Inject + +@HiltViewModel +class ThemeViewModel @Inject constructor(private val settingsRepository: SettingsRepository): ViewModel() { + + private val _themeState = mutableStateOf(ThemeState()) + val themeState: State = _themeState + + init { + viewModelScope.launch { + settingsRepository + .getStringSettings(PREF_THEME_KEY, COLOR_PREF_DEFAULT).collect { + _themeState.value = _themeState.value.copy( + currentTheme = it + ) + } + settingsRepository + .getStringSettings(PREF_DARK_MODE_KEY, PREF_DARK_MODE_AUTO).collect { + _themeState.value = _themeState.value.copy( + darkMode = it + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 9a6c6ba..0000000 --- a/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..1e661d2 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,10 +1,4 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 #FF000000 - #FFFFFFFF \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 365b6c6..4f97caa 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,17 +1,6 @@ - - -