Skip to content

Commit

Permalink
Allow modification of appearance settings
Browse files Browse the repository at this point in the history
  • Loading branch information
SaintPatrck committed Apr 14, 2024
1 parent 6146e1d commit 7fc1991
Show file tree
Hide file tree
Showing 11 changed files with 771 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.x8bit.bitwarden.authenticator.data.platform.datasource.disk

import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppTheme
import kotlinx.coroutines.flow.Flow

Expand All @@ -8,6 +9,11 @@ import kotlinx.coroutines.flow.Flow
*/
interface SettingsDiskSource {

/**
* The currently persisted app language (or `null` if not set).
*/
var appLanguage: AppLanguage?

/**
* The currently persisted app theme (or `null` if not set).
*/
Expand All @@ -18,6 +24,16 @@ interface SettingsDiskSource {
*/
val appThemeFlow: Flow<AppTheme>

/**
* The currently persisted setting for getting login item icons (or `null` if not set).
*/
var isIconLoadingDisabled: Boolean?

/**
* Emits updates that track [isIconLoadingDisabled].
*/
val isIconLoadingDisabledFlow: Flow<Boolean?>

/**
* Stores the threshold at which users are alerted that an items validity period is nearing
* expiration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ package com.x8bit.bitwarden.authenticator.data.platform.datasource.disk
import android.content.SharedPreferences
import com.x8bit.bitwarden.authenticator.data.platform.datasource.disk.BaseDiskSource.Companion.BASE_KEY
import com.x8bit.bitwarden.authenticator.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppTheme
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.onSubscription

private const val APP_THEME_KEY = "$BASE_KEY:theme"
private const val APP_LANGUAGE_KEY = "$BASE_KEY:appLocale"
private const val SCREEN_CAPTURE_ALLOW_KEY = "$BASE_KEY:screenCaptureAllowed"
private const val ACCOUNT_BIOMETRIC_INTEGRITY_VALID_KEY = "$BASE_KEY:accountBiometricIntegrityValid"
private const val ALERT_THRESHOLD_SECONDS_KEY = "$BASE_KEY:alertThresholdSeconds"
private const val DISABLE_ICON_LOADING_KEY = "$BASE_KEY:disableFavicon"

/**
* Primary implementation of [SettingsDiskSource].
Expand All @@ -26,9 +29,24 @@ class SettingsDiskSourceImpl(
private val mutableScreenCaptureAllowedFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>()

private val mutableIsIconLoadingDisabledFlow =
bufferedMutableSharedFlow<Boolean?>()

private val mutableAlertThresholdSecondsFlow =
bufferedMutableSharedFlow<Int>()

override var appLanguage: AppLanguage?
get() = getString(key = APP_LANGUAGE_KEY)
?.let { storedValue ->
AppLanguage.entries.firstOrNull { storedValue == it.localeName }
}
set(value) {
putString(
key = APP_LANGUAGE_KEY,
value = value?.localeName,
)
}

override var appTheme: AppTheme
get() = getString(key = APP_THEME_KEY)
?.let { storedValue ->
Expand All @@ -47,6 +65,17 @@ class SettingsDiskSourceImpl(
get() = mutableAppThemeFlow
.onSubscription { emit(appTheme) }

override var isIconLoadingDisabled: Boolean?
get() = getBoolean(key = DISABLE_ICON_LOADING_KEY)
set(value) {
putBoolean(key = DISABLE_ICON_LOADING_KEY, value = value)
mutableIsIconLoadingDisabledFlow.tryEmit(value)
}

override val isIconLoadingDisabledFlow: Flow<Boolean?>
get() = mutableIsIconLoadingDisabledFlow
.onSubscription { emit(getBoolean(DISABLE_ICON_LOADING_KEY)) }

override fun storeAlertThresholdSeconds(thresholdSeconds: Int) {
putInt(
ALERT_THRESHOLD_SECONDS_KEY,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
package com.x8bit.bitwarden.authenticator.data.platform.repository

import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppTheme
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow

/**
* Provides an API for observing and modifying settings state.
*/
interface SettingsRepository {

/**
* The [AppLanguage] for the current user.
*/
var appLanguage: AppLanguage

/**
* The currently stored [AppTheme].
*/
Expand All @@ -21,10 +28,20 @@ interface SettingsRepository {
/**
* The currently stored expiration alert threshold.
*/
var authenticatorAlertThresholdSeconds : Int
var authenticatorAlertThresholdSeconds: Int

/**
* Tracks changes to the expiration alert threshold.
*/
val authenticatorAlertThresholdSecondsFlow: StateFlow<Int>

/**
* The current setting for getting login item icons.
*/
var isIconLoadingDisabled: Boolean

/**
* Emits updates that track the [isIconLoadingDisabled] value.
*/
val isIconLoadingDisabledFlow: Flow<Boolean>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package com.x8bit.bitwarden.authenticator.data.platform.repository

import com.x8bit.bitwarden.authenticator.data.platform.datasource.disk.SettingsDiskSource
import com.x8bit.bitwarden.authenticator.data.platform.manager.DispatcherManager
import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppLanguage
import com.x8bit.bitwarden.authenticator.ui.platform.feature.settings.appearance.model.AppTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
Expand All @@ -19,6 +21,12 @@ class SettingsRepositoryImpl(

private val unconfinedScope = CoroutineScope(dispatcherManager.unconfined)

override var appLanguage: AppLanguage
get() = settingsDiskSource.appLanguage ?: AppLanguage.DEFAULT
set(value) {
settingsDiskSource.appLanguage = value
}

override var appTheme: AppTheme by settingsDiskSource::appTheme

override var authenticatorAlertThresholdSeconds = settingsDiskSource.getAlertThresholdSeconds()
Expand All @@ -41,5 +49,21 @@ class SettingsRepositoryImpl(
started = SharingStarted.Eagerly,
initialValue = settingsDiskSource.getAlertThresholdSeconds(),
)
override var isIconLoadingDisabled: Boolean
get() = settingsDiskSource.isIconLoadingDisabled ?: false
set(value) {
settingsDiskSource.isIconLoadingDisabled = value
}

override val isIconLoadingDisabledFlow: StateFlow<Boolean>
get() = settingsDiskSource
.isIconLoadingDisabledFlow
.map { it ?: false }
.stateIn(
scope = unconfinedScope,
started = SharingStarted.Eagerly,
initialValue = settingsDiskSource
.isIconLoadingDisabled
?: false,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.x8bit.bitwarden.authenticator.ui.platform.components.row

import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp

/**
* Represents a clickable row of text and can contains an optional [content] that appears to the
* right of the [text].
*
* @param text The label for the row as a [String].
* @param onClick The callback when the row is clicked.
* @param modifier The modifier to be applied to the layout.
* @param description An optional description label to be displayed below the [text].
* @param withDivider Indicates if a divider should be drawn on the bottom of the row, defaults
* to `false`.
* @param content The content of the [BitwardenTextRow].
*/
@Composable
fun BitwardenTextRow(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier,
description: String? = null,
withDivider: Boolean = false,
content: (@Composable () -> Unit)? = null,
) {
Box(
contentAlignment = Alignment.BottomCenter,
modifier = modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(color = MaterialTheme.colorScheme.primary),
onClick = onClick,
)
.semantics(mergeDescendants = true) { },
) {
Row(
modifier = Modifier
.defaultMinSize(minHeight = 56.dp)
.padding(start = 16.dp, end = 24.dp, top = 8.dp, bottom = 8.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier
.padding(end = 16.dp)
.weight(1f),
) {
Text(
text = text,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface,
)
description?.let {
Text(
text = it,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
content?.invoke()
}
if (withDivider) {
HorizontalDivider(
modifier = Modifier.padding(start = 16.dp),
thickness = 1.dp,
color = MaterialTheme.colorScheme.outlineVariant,
)
}
}
}
Loading

0 comments on commit 7fc1991

Please sign in to comment.