diff --git a/app/build.gradle b/app/build.gradle index 590fe2c..4773f46 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,6 +93,9 @@ dependencies { implementation "androidx.camera:camera-view:$camerax_version" implementation "androidx.camera:camera-extensions:$camerax_version" + // preferences + implementation "androidx.datastore:datastore-preferences:1.0.0" + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/org/fnives/android/qrcodetransfer/MainActivity.kt b/app/src/main/java/org/fnives/android/qrcodetransfer/MainActivity.kt index 1b66299..b59724c 100644 --- a/app/src/main/java/org/fnives/android/qrcodetransfer/MainActivity.kt +++ b/app/src/main/java/org/fnives/android/qrcodetransfer/MainActivity.kt @@ -26,11 +26,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import org.fnives.android.qrcodetransfer.create.CreateQRCode import org.fnives.android.qrcodetransfer.intent.LocalIntentTextProvider import org.fnives.android.qrcodetransfer.read.ReadQRCode +import org.fnives.android.qrcodetransfer.storage.LocalAppPreferencesProvider import org.fnives.android.qrcodetransfer.ui.theme.QRCodeTransferTheme @@ -39,25 +39,27 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - LocalIntentTextProvider(intent) { - QRCodeTransferTheme { - var writerSelected by rememberSaveable { mutableStateOf(true) } - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colors.background, - ) { - Scaffold(bottomBar = { - NavBar( - writerSelected = writerSelected, - setWriterSelected = { writerSelected = it }) - }) { - Box(Modifier.padding(it)) { - AnimatedContent(targetState = writerSelected) { showWriter -> - if (showWriter) { - CreateQRCode() - } else { - ReadQRCode() + LocalAppPreferencesProvider(this) { + LocalIntentTextProvider(intent) { + QRCodeTransferTheme { + var writerSelected by rememberSaveable { mutableStateOf(true) } + // A surface container using the 'background' color from the theme + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colors.background, + ) { + Scaffold(bottomBar = { + NavBar( + writerSelected = writerSelected, + setWriterSelected = { writerSelected = it }) + }) { + Box(Modifier.padding(it)) { + AnimatedContent(targetState = writerSelected) { showWriter -> + if (showWriter) { + CreateQRCode() + } else { + ReadQRCode() + } } } } diff --git a/app/src/main/java/org/fnives/android/qrcodetransfer/create/CreateQRCode.kt b/app/src/main/java/org/fnives/android/qrcodetransfer/create/CreateQRCode.kt index 32618ba..5e70f2b 100644 --- a/app/src/main/java/org/fnives/android/qrcodetransfer/create/CreateQRCode.kt +++ b/app/src/main/java/org/fnives/android/qrcodetransfer/create/CreateQRCode.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn -import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -22,6 +21,7 @@ import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -45,6 +45,7 @@ import kotlinx.coroutines.withContext import org.fnives.android.qrcodetransfer.R import org.fnives.android.qrcodetransfer.SequenceProtocol import org.fnives.android.qrcodetransfer.intent.LocalIntentText +import org.fnives.android.qrcodetransfer.storage.LocalAppPreferences import org.fnives.android.qrcodetransfer.toBitmap @@ -151,9 +152,10 @@ fun QRCodeContentInput( ) { val messageFromIntent = LocalIntentText.current var content by rememberSaveable(messageFromIntent) { mutableStateOf(messageFromIntent.orEmpty()) } - var number by rememberSaveable { mutableStateOf(SequenceProtocol.versionCode) } + val appPreferences = LocalAppPreferences.current + val number by appPreferences.versionCode.collectAsState(initial = SequenceProtocol.versionCode) SequenceProtocol.versionCode = number - var encodeBase64 by rememberSaveable { mutableStateOf(SequenceProtocol.encodeBase64) } + val encodeBase64 by appPreferences.encodeBase64.collectAsState(initial = SequenceProtocol.encodeBase64) SequenceProtocol.encodeBase64 = encodeBase64 val keyboardController = LocalSoftwareKeyboardController.current val coroutineScope = rememberCoroutineScope { Dispatchers.IO } @@ -198,13 +200,14 @@ fun QRCodeContentInput( ) { Column { QRCodeVersionNumberDropdown(number, setVersionNumber = { - number = it + // protocol does additional checks so we follow it's lead SequenceProtocol.versionCode = it + appPreferences.setVersionCode(SequenceProtocol.versionCode) createBitmaps() }) Base64EncodeCheckbox(encode = encodeBase64, setEncode = { - encodeBase64 = it SequenceProtocol.encodeBase64 = it + appPreferences.setEncodeBase64(SequenceProtocol.encodeBase64) createBitmaps() }) } diff --git a/app/src/main/java/org/fnives/android/qrcodetransfer/read/ReadQRCode.kt b/app/src/main/java/org/fnives/android/qrcodetransfer/read/ReadQRCode.kt index c76d3b6..16ca401 100644 --- a/app/src/main/java/org/fnives/android/qrcodetransfer/read/ReadQRCode.kt +++ b/app/src/main/java/org/fnives/android/qrcodetransfer/read/ReadQRCode.kt @@ -14,6 +14,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.LocalTextStyle import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -31,6 +32,7 @@ import java.time.Duration import org.fnives.android.qrcodetransfer.R import org.fnives.android.qrcodetransfer.SequenceProtocol import org.fnives.android.qrcodetransfer.create.Base64EncodeCheckbox +import org.fnives.android.qrcodetransfer.storage.LocalAppPreferences import org.fnives.android.qrcodetransfer.toBinaryBitmap @@ -49,7 +51,8 @@ fun ReadQRCode() { @Composable fun QRCodeReader() { var readState by remember { mutableStateOf(null) } - var encodeBase64 by remember { mutableStateOf(SequenceProtocol.encodeBase64) } + val appPreferences = LocalAppPreferences.current + val encodeBase64 by appPreferences.encodeBase64.collectAsState(initial = SequenceProtocol.encodeBase64) val textScrollState = rememberScrollState() Column { @@ -65,8 +68,8 @@ fun QRCodeReader() { Column { Base64EncodeCheckbox(encode = encodeBase64, setEncode = { SequenceProtocol.encodeBase64 = it + appPreferences.setEncodeBase64(SequenceProtocol.encodeBase64) readState = null - encodeBase64 = it }) Column( Modifier diff --git a/app/src/main/java/org/fnives/android/qrcodetransfer/storage/AppPreferences.kt b/app/src/main/java/org/fnives/android/qrcodetransfer/storage/AppPreferences.kt new file mode 100644 index 0000000..14bc969 --- /dev/null +++ b/app/src/main/java/org/fnives/android/qrcodetransfer/storage/AppPreferences.kt @@ -0,0 +1,62 @@ +package org.fnives.android.qrcodetransfer.storage + +import android.content.Context +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.remember +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.intPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +class AppPreferences(private val context: Context) { + + private val scope = CoroutineScope(Dispatchers.IO) + private val Context.store: DataStore by preferencesDataStore(name = "settings") + private val dataStore get() = context.store + private val versionCodeKey = intPreferencesKey("versionCode") + val versionCode: Flow = dataStore.data + .map { preferences -> + preferences[versionCodeKey] ?: 4 + } + + fun setVersionCode(versionCode: Int) { + scope.launch { + dataStore.edit { + it[versionCodeKey] = versionCode + } + } + } + + private val encodeBase64Key = booleanPreferencesKey("encodeBase64") + val encodeBase64: Flow = dataStore.data + .map { preferences -> + preferences[encodeBase64Key] ?: false + } + + fun setEncodeBase64(encodeBase64: Boolean) { + scope.launch { + dataStore.edit { + it[encodeBase64Key] = encodeBase64 + } + } + } +} + +val LocalAppPreferences = compositionLocalOf { + error("CompositionLocal LocalIntentText not present") +} + +@Composable +fun LocalAppPreferencesProvider(context: Context, content: @Composable () -> Unit) { + val preferences = remember(context) { AppPreferences(context) } + CompositionLocalProvider(LocalAppPreferences provides preferences, content = content) +} \ No newline at end of file