Skip to content

Commit

Permalink
Add environment selector
Browse files Browse the repository at this point in the history
  • Loading branch information
LaurentTreguier committed Jul 24, 2024
1 parent a1e642c commit d4dd32a
Show file tree
Hide file tree
Showing 17 changed files with 441 additions and 94 deletions.
34 changes: 33 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ plugins {
alias(libs.plugins.kotlin)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.compose)
alias(libs.plugins.hilt)
alias(libs.plugins.sentry)
alias(libs.plugins.openapi)
alias(libs.plugins.protobuf)
alias(libs.plugins.ksp)
}

enum class VersionSuffix(val value: Int) {
Expand Down Expand Up @@ -96,7 +98,25 @@ android {
}

buildTypes {
val environmentType = "app.fyreplace.fyreplace.protos.Environment"

debug {
buildConfigField(
environmentType,
"ENVIRONMENT_DEFAULT",
"$environmentType.LOCAL"
)
}

release {
val environment = if (getVersionNumberSuffix() == VersionSuffix.DEV) "DEV" else "MAIN"

buildConfigField(
environmentType,
"ENVIRONMENT_DEFAULT",
"$environmentType.$environment"
)

isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
Expand Down Expand Up @@ -137,6 +157,7 @@ android {
}

buildFeatures {
buildConfig = true
compose = true
}

Expand Down Expand Up @@ -167,7 +188,14 @@ openApiGenerate {
generateApiTests = false
}

tasks.named { it.matches(Regex("(compile.*kotlin)|(.*sentry.*)", RegexOption.IGNORE_CASE)) }
tasks.named {
it.matches(
Regex(
"((compile|ksp).*kotlin)|(.*sentry.*)",
RegexOption.IGNORE_CASE
)
)
}
.all { dependsOn(tasks.named("openApiGenerate")) }

protobuf {
Expand All @@ -191,7 +219,9 @@ dependencies {
implementation(libs.androidx.animation)
implementation(libs.androidx.core)
implementation(libs.androidx.datastore)
implementation(libs.androidx.hilt.navigation.compose)
implementation(libs.androidx.lifecycle.runtime)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.material3)
implementation(libs.androidx.material3.adaptive)
implementation(libs.androidx.material.icons.extended)
Expand All @@ -200,6 +230,7 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.coil.compose)
implementation(libs.hilt)
implementation(libs.ktor.client)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.moshi)
Expand All @@ -211,4 +242,5 @@ dependencies {
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.ui.test.junit4)
ksp(libs.hilt.compiler)
}
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".App"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/kotlin/app/fyreplace/fyreplace/App.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package app.fyreplace.fyreplace

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class App : Application()
2 changes: 2 additions & 0 deletions app/src/main/kotlin/app/fyreplace/fyreplace/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import app.fyreplace.fyreplace.ui.views.navigation.Destination
import app.fyreplace.fyreplace.ui.views.navigation.SideNavigation
import app.fyreplace.fyreplace.ui.views.navigation.asDestination
import app.fyreplace.fyreplace.ui.views.navigation.sail
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package app.fyreplace.fyreplace.data

import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer
import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
import androidx.datastore.dataStore
import app.fyreplace.fyreplace.BuildConfig
import app.fyreplace.fyreplace.protos.Connection
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream

object ConnectionSerializer : Serializer<Connection> {
override val defaultValue: Connection
get() = Connection.getDefaultInstance()
.toBuilder()
.setEnvironment(BuildConfig.ENVIRONMENT_DEFAULT)
.build()

val corruptionHandler = ReplaceFileCorruptionHandler { defaultValue }

override suspend fun readFrom(input: InputStream): Connection {
try {
return Connection.parseFrom(input)
} catch (exception: IOException) {
throw CorruptionException("Cannot read proto.", exception)
}
}

override suspend fun writeTo(t: Connection, output: OutputStream) = t.writeTo(output)
}

val Context.connectionStore by dataStore(
fileName = "connection.pb",
serializer = ConnectionSerializer,
corruptionHandler = ConnectionSerializer.corruptionHandler
)
17 changes: 17 additions & 0 deletions app/src/main/kotlin/app/fyreplace/fyreplace/extensions/Context.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app.fyreplace.fyreplace.extensions

import android.content.Context
import android.content.ContextWrapper
import androidx.activity.ComponentActivity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext

@Suppress("RecursivePropertyAccessor")
val Context.activity
get(): ComponentActivity? = when (this) {
is ComponentActivity -> this
is ContextWrapper -> baseContext.activity
else -> null
}

val activity @Composable get() = LocalContext.current.activity
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package app.fyreplace.fyreplace.ui.screens

import android.annotation.SuppressLint
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -25,10 +22,6 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
Expand All @@ -38,16 +31,26 @@ import androidx.compose.ui.res.integerResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.fyreplace.fyreplace.R
import app.fyreplace.fyreplace.ui.theme.AppTheme
import app.fyreplace.fyreplace.ui.views.navigation.Destination
import kotlin.math.min
import app.fyreplace.fyreplace.ui.views.settings.EnvironmentSelector
import app.fyreplace.fyreplace.viewmodels.screens.LoginViewModel

@ExperimentalSharedTransitionApi
@Composable
fun SharedTransitionScope.LoginScreen(visibilityScope: AnimatedVisibilityScope) {
val viewModel = hiltViewModel<LoginViewModel>()
val identifier by viewModel.identifier.collectAsStateWithLifecycle()
val canSubmit by viewModel.canSubmit.collectAsStateWithLifecycle()
val keyboard = LocalSoftwareKeyboardController.current

fun submit() {
keyboard?.hide()
}

Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
Expand All @@ -71,12 +74,15 @@ fun SharedTransitionScope.LoginScreen(visibilityScope: AnimatedVisibilityScope)
)
)

var identifier by rememberSaveable { mutableStateOf("") }
val maxLength = integerResource(R.integer.email_max_length)
val keyboard = LocalSoftwareKeyboardController.current

fun submit() {
keyboard?.hide()
Box(
modifier = Modifier
.padding(bottom = 16.dp)
.sharedElement(
rememberSharedContentState(key = "environment-selector"),
visibilityScope
)
) {
EnvironmentSelector()
}

OutlinedTextField(
Expand All @@ -89,15 +95,15 @@ fun SharedTransitionScope.LoginScreen(visibilityScope: AnimatedVisibilityScope)
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(onDone = { submit() }),
onValueChange = { identifier = it.substring(0, min(maxLength, it.length)) },
onValueChange = viewModel::updateIdentifier,
modifier = Modifier
.widthIn(
integerResource(R.integer.form_min_width).dp,
integerResource(R.integer.form_max_width).dp
)
.padding(bottom = 32.dp)
.sharedElement(
rememberSharedContentState(key = "field"),
rememberSharedContentState(key = "first-field"),
visibilityScope
)
.testTag("login:identifier")
Expand All @@ -110,7 +116,7 @@ fun SharedTransitionScope.LoginScreen(visibilityScope: AnimatedVisibilityScope)
)
) {
Button(
enabled = identifier.isNotBlank() && identifier.length >= integerResource(R.integer.username_min_length),
enabled = canSubmit,
onClick = ::submit,
modifier = Modifier
.padding(bottom = 32.dp)
Expand All @@ -121,18 +127,3 @@ fun SharedTransitionScope.LoginScreen(visibilityScope: AnimatedVisibilityScope)
}
}
}

@SuppressLint("UnusedContentLambdaTargetStateParameter")
@OptIn(ExperimentalSharedTransitionApi::class)
@Preview(showSystemUi = true, showBackground = true)
@Composable
fun LoginScreenPreview() {
AppTheme {
SharedTransitionLayout {
val state = remember { 0 }
AnimatedContent(state, label = "Preview") {
LoginScreen(this)
}
}
}
}
Loading

0 comments on commit d4dd32a

Please sign in to comment.