feat(login): replace Facebook with email-password auth screen
- Remove Facebook provider from Login flow - Add EmailLoginScreen with gradient background, email/password fields - Add EmailLoginScreenContract and EmailLoginViewModel with stub logic - Add navigation: Sign in with email -> EmailLoginScreen - Use back arrow icon instead of back text - Move header above email field, add Register button - Update run command to launch app after install - Add ic_arrow_back drawable, update strings Made-with: Cursor
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
# Run app on device
|
# Run app on device
|
||||||
|
|
||||||
Build and install the app on the connected USB Android device.
|
Build, install, and launch the app on the connected USB Android device.
|
||||||
|
|
||||||
## What to do
|
## What to do
|
||||||
|
|
||||||
1. Run `./gradlew installDebug` (use `gradlew.bat` on Windows).
|
1. Run `./gradlew installDebug` (use `gradlew.bat` on Windows).
|
||||||
2. Ensure the device is connected via USB with USB debugging enabled.
|
2. Ensure the device is connected via USB with USB debugging enabled.
|
||||||
3. After a successful install, report the device name and that the app is ready to launch.
|
3. After a successful install, launch the app with `adb shell am start -n org.db3.airmq/.MainActivity`.
|
||||||
|
4. Report the device name and that the app was launched.
|
||||||
|
|||||||
@@ -413,11 +413,11 @@ private fun AirMQButtonsPreviewSocial() {
|
|||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
AirMQSocialButton(
|
AirMQSocialButton(
|
||||||
text = "Sign in with Facebook",
|
text = "Sign in with email",
|
||||||
leadingIconRes = R.drawable.ic_facebook,
|
leadingIconRes = R.drawable.ic_account,
|
||||||
iconTint = Color.White,
|
iconTint = Color.Unspecified,
|
||||||
containerColor = Color(0xFF3B5998),
|
containerColor = Color.White,
|
||||||
contentColor = Color.White,
|
contentColor = Color(0xFF202124),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
package org.db3.airmq.features.login
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import org.db3.airmq.R
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import org.db3.airmq.features.common.AirMQOutlinedLightButton
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreenContract.Action
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreenContract.Event
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreenContract.State
|
||||||
|
import org.db3.airmq.ui.theme.AirMQTheme
|
||||||
|
|
||||||
|
private val LegacyLoginGradientStart = Color(0xFF449CF5)
|
||||||
|
private val LegacyLoginGradientEnd = Color(0xFF5CE4BB)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EmailLoginScreen(
|
||||||
|
onLogInToManage: () -> Unit,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
viewModel: EmailLoginViewModel = hiltViewModel()
|
||||||
|
) {
|
||||||
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
LaunchedEffect(viewModel) {
|
||||||
|
viewModel.actions.collectLatest { action ->
|
||||||
|
when (action) {
|
||||||
|
Action.OpenManage -> onLogInToManage()
|
||||||
|
is Action.ShowMessage -> {
|
||||||
|
Toast.makeText(context, action.message, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EmailLoginScreenContent(
|
||||||
|
uiState = uiState,
|
||||||
|
onEvent = viewModel::onEvent,
|
||||||
|
onBack = onBack
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun EmailLoginScreenContent(
|
||||||
|
uiState: State,
|
||||||
|
onEvent: (Event) -> Unit,
|
||||||
|
onBack: () -> Unit
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(
|
||||||
|
brush = Brush.linearGradient(
|
||||||
|
colorStops = arrayOf(
|
||||||
|
0.0f to LegacyLoginGradientStart,
|
||||||
|
0.35f to LegacyLoginGradientStart,
|
||||||
|
1.0f to LegacyLoginGradientEnd
|
||||||
|
),
|
||||||
|
start = Offset(0f, 0f),
|
||||||
|
end = Offset(1200f, 1200f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.statusBarsPadding()
|
||||||
|
.padding(horizontal = 40.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
IconButton(onClick = onBack) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = R.drawable.ic_arrow_back),
|
||||||
|
contentDescription = stringResource(id = R.string.content_back),
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.text_sign_in_email),
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 36.sp,
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = uiState.email,
|
||||||
|
onValueChange = { onEvent(Event.EmailChanged(it)) },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
stringResource(id = R.string.hint_email),
|
||||||
|
color = Color.White.copy(alpha = 0.7f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Email,
|
||||||
|
imeAction = ImeAction.Next
|
||||||
|
),
|
||||||
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
|
focusedTextColor = Color.White,
|
||||||
|
unfocusedTextColor = Color.White,
|
||||||
|
cursorColor = Color.White,
|
||||||
|
focusedBorderColor = Color.White.copy(alpha = 0.8f),
|
||||||
|
unfocusedBorderColor = Color.White.copy(alpha = 0.55f),
|
||||||
|
focusedContainerColor = Color.White.copy(alpha = 0.1f),
|
||||||
|
unfocusedContainerColor = Color.White.copy(alpha = 0.05f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = uiState.password,
|
||||||
|
onValueChange = { onEvent(Event.PasswordChanged(it)) },
|
||||||
|
singleLine = true,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
placeholder = {
|
||||||
|
Text(
|
||||||
|
stringResource(id = R.string.hint_password),
|
||||||
|
color = Color.White.copy(alpha = 0.7f)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
visualTransformation = PasswordVisualTransformation(),
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Password,
|
||||||
|
imeAction = ImeAction.Done
|
||||||
|
),
|
||||||
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
|
focusedTextColor = Color.White,
|
||||||
|
unfocusedTextColor = Color.White,
|
||||||
|
cursorColor = Color.White,
|
||||||
|
focusedBorderColor = Color.White.copy(alpha = 0.8f),
|
||||||
|
unfocusedBorderColor = Color.White.copy(alpha = 0.55f),
|
||||||
|
focusedContainerColor = Color.White.copy(alpha = 0.1f),
|
||||||
|
unfocusedContainerColor = Color.White.copy(alpha = 0.05f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (uiState.isLoading) {
|
||||||
|
CircularProgressIndicator(color = Color.White)
|
||||||
|
} else {
|
||||||
|
AirMQOutlinedLightButton(
|
||||||
|
text = stringResource(id = R.string.button_sign_in),
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
onClick = { onEvent(Event.SignInClicked) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = { onEvent(Event.RegisterClicked) },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.button_register),
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Composable
|
||||||
|
private fun EmailLoginScreenPreview() {
|
||||||
|
AirMQTheme {
|
||||||
|
EmailLoginScreenContent(
|
||||||
|
uiState = State(),
|
||||||
|
onEvent = {},
|
||||||
|
onBack = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package org.db3.airmq.features.login
|
||||||
|
|
||||||
|
object EmailLoginScreenContract {
|
||||||
|
|
||||||
|
data class State(
|
||||||
|
val email: String = "",
|
||||||
|
val password: String = "",
|
||||||
|
val isLoading: Boolean = false
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed interface Action {
|
||||||
|
data object OpenManage : Action
|
||||||
|
data class ShowMessage(val message: String) : Action
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface Event {
|
||||||
|
data class EmailChanged(val value: String) : Event
|
||||||
|
data class PasswordChanged(val value: String) : Event
|
||||||
|
data object SignInClicked : Event
|
||||||
|
data object RegisterClicked : Event
|
||||||
|
data object BackClicked : Event
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.db3.airmq.features.login
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import org.db3.airmq.R
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreenContract.Action
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreenContract.Event
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreenContract.State
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class EmailLoginViewModel @Inject constructor(
|
||||||
|
@ApplicationContext private val appContext: Context
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _uiState = MutableStateFlow(State())
|
||||||
|
val uiState: StateFlow<State> = _uiState.asStateFlow()
|
||||||
|
|
||||||
|
private val _actions = MutableSharedFlow<Action>(extraBufferCapacity = 1)
|
||||||
|
val actions: SharedFlow<Action> = _actions.asSharedFlow()
|
||||||
|
|
||||||
|
fun onEvent(event: Event) {
|
||||||
|
when (event) {
|
||||||
|
is Event.EmailChanged -> {
|
||||||
|
_uiState.value = _uiState.value.copy(email = event.value)
|
||||||
|
}
|
||||||
|
is Event.PasswordChanged -> {
|
||||||
|
_uiState.value = _uiState.value.copy(password = event.value)
|
||||||
|
}
|
||||||
|
Event.SignInClicked -> {
|
||||||
|
_uiState.value = _uiState.value.copy(isLoading = true)
|
||||||
|
_actions.tryEmit(
|
||||||
|
Action.ShowMessage(appContext.getString(R.string.coming_soon))
|
||||||
|
)
|
||||||
|
_uiState.value = _uiState.value.copy(isLoading = false)
|
||||||
|
}
|
||||||
|
Event.RegisterClicked -> {
|
||||||
|
_actions.tryEmit(
|
||||||
|
Action.ShowMessage(appContext.getString(R.string.coming_soon))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Event.BackClicked -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,11 +56,11 @@ import org.db3.airmq.ui.theme.AirMQTheme
|
|||||||
|
|
||||||
private val LegacyLoginGradientStart = Color(0xFF449CF5)
|
private val LegacyLoginGradientStart = Color(0xFF449CF5)
|
||||||
private val LegacyLoginGradientEnd = Color(0xFF5CE4BB)
|
private val LegacyLoginGradientEnd = Color(0xFF5CE4BB)
|
||||||
private val LegacyFacebookBlue = Color(0xFF3B5998)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginScreen(
|
fun LoginScreen(
|
||||||
onLogInToManage: () -> Unit,
|
onLogInToManage: () -> Unit,
|
||||||
|
onOpenEmailLogin: () -> Unit,
|
||||||
viewModel: LoginViewModel = hiltViewModel()
|
viewModel: LoginViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
@@ -78,6 +78,7 @@ fun LoginScreen(
|
|||||||
is GoogleSignInResult.Error -> viewModel.onEvent(Event.GoogleSignInFailed(result.message))
|
is GoogleSignInResult.Error -> viewModel.onEvent(Event.GoogleSignInFailed(result.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Action.NavigateToEmailLogin -> onOpenEmailLogin()
|
||||||
Action.ShowContinueAnonymousDialog -> showContinueAnonymousDialog = true
|
Action.ShowContinueAnonymousDialog -> showContinueAnonymousDialog = true
|
||||||
Action.OpenPrivacyPolicy -> {
|
Action.OpenPrivacyPolicy -> {
|
||||||
Toast.makeText(context, context.getString(R.string.coming_soon), Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, context.getString(R.string.coming_soon), Toast.LENGTH_SHORT).show()
|
||||||
@@ -216,13 +217,13 @@ private fun LoginScreenContent(
|
|||||||
)
|
)
|
||||||
|
|
||||||
AirMQSocialButton(
|
AirMQSocialButton(
|
||||||
text = stringResource(id = R.string.button_sign_in_facebook),
|
text = stringResource(id = R.string.button_sign_in_email),
|
||||||
leadingIconRes = R.drawable.ic_facebook,
|
leadingIconRes = R.drawable.ic_account,
|
||||||
iconTint = Color.White,
|
iconTint = Color.Unspecified,
|
||||||
containerColor = LegacyFacebookBlue,
|
containerColor = Color.White,
|
||||||
contentColor = Color.White,
|
contentColor = Color(0xFF202124),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
onClick = { onEvent(Event.FacebookClicked) }
|
onClick = { onEvent(Event.EmailClicked) }
|
||||||
)
|
)
|
||||||
|
|
||||||
AirMQOutlinedLightButton(
|
AirMQOutlinedLightButton(
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ object LoginScreenContract {
|
|||||||
sealed interface Action {
|
sealed interface Action {
|
||||||
data object OpenManage : Action
|
data object OpenManage : Action
|
||||||
data object LaunchGoogleSignIn : Action
|
data object LaunchGoogleSignIn : Action
|
||||||
|
data object NavigateToEmailLogin : Action
|
||||||
data object ShowContinueAnonymousDialog : Action
|
data object ShowContinueAnonymousDialog : Action
|
||||||
data object OpenPrivacyPolicy : Action
|
data object OpenPrivacyPolicy : Action
|
||||||
data object OpenTermsAndConditions : Action
|
data object OpenTermsAndConditions : Action
|
||||||
@@ -20,7 +21,7 @@ object LoginScreenContract {
|
|||||||
data class GoogleTokenReceived(val idToken: String) : Event
|
data class GoogleTokenReceived(val idToken: String) : Event
|
||||||
data object GoogleSignInCancelled : Event
|
data object GoogleSignInCancelled : Event
|
||||||
data class GoogleSignInFailed(val message: String? = null) : Event
|
data class GoogleSignInFailed(val message: String? = null) : Event
|
||||||
data object FacebookClicked : Event
|
data object EmailClicked : Event
|
||||||
data object ContinueAnonymousClicked : Event
|
data object ContinueAnonymousClicked : Event
|
||||||
data object ContinueAnonymousConfirmed : Event
|
data object ContinueAnonymousConfirmed : Event
|
||||||
data object ContinueAnonymousDismissed : Event
|
data object ContinueAnonymousDismissed : Event
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ class LoginViewModel @Inject constructor(
|
|||||||
Event.GoogleSignInCancelled -> {
|
Event.GoogleSignInCancelled -> {
|
||||||
_uiState.value = _uiState.value.copy(isLoading = false)
|
_uiState.value = _uiState.value.copy(isLoading = false)
|
||||||
}
|
}
|
||||||
Event.FacebookClicked -> {
|
Event.EmailClicked -> {
|
||||||
_actions.tryEmit(Action.ShowMessage(appContext.getString(R.string.coming_soon)))
|
_actions.tryEmit(Action.NavigateToEmailLogin)
|
||||||
}
|
}
|
||||||
Event.ContinueAnonymousClicked -> {
|
Event.ContinueAnonymousClicked -> {
|
||||||
_actions.tryEmit(Action.ShowContinueAnonymousDialog)
|
_actions.tryEmit(Action.ShowContinueAnonymousDialog)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ import org.db3.airmq.features.device.DeviceScreen
|
|||||||
import org.db3.airmq.features.entry.SplashScreen
|
import org.db3.airmq.features.entry.SplashScreen
|
||||||
import org.db3.airmq.features.entry.WizardScreen
|
import org.db3.airmq.features.entry.WizardScreen
|
||||||
import org.db3.airmq.features.location.LocationScreen
|
import org.db3.airmq.features.location.LocationScreen
|
||||||
|
import org.db3.airmq.features.login.EmailLoginScreen
|
||||||
import org.db3.airmq.features.login.LoginScreen
|
import org.db3.airmq.features.login.LoginScreen
|
||||||
import org.db3.airmq.features.manage.ManageScreen
|
import org.db3.airmq.features.manage.ManageScreen
|
||||||
import org.db3.airmq.features.map.MapScreen
|
import org.db3.airmq.features.map.MapScreen
|
||||||
@@ -204,7 +205,18 @@ fun AirMQNavGraph(modifier: Modifier = Modifier) {
|
|||||||
}
|
}
|
||||||
composable(AirMqRoutes.LOGIN) {
|
composable(AirMqRoutes.LOGIN) {
|
||||||
LoginScreen(
|
LoginScreen(
|
||||||
onLogInToManage = { navController.navigate(AirMqRoutes.MANAGE) }
|
onLogInToManage = { navController.navigate(AirMqRoutes.MANAGE) },
|
||||||
|
onOpenEmailLogin = { navController.navigate(AirMqRoutes.EMAIL_LOGIN) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(AirMqRoutes.EMAIL_LOGIN) {
|
||||||
|
EmailLoginScreen(
|
||||||
|
onLogInToManage = {
|
||||||
|
navController.navigate(AirMqRoutes.MANAGE) {
|
||||||
|
popUpTo(AirMqRoutes.LOGIN) { inclusive = true }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBack = { navController.popBackStack() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(AirMqRoutes.NEWS) {
|
composable(AirMqRoutes.NEWS) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ object AirMqRoutes {
|
|||||||
const val LOCATION = "detail/location"
|
const val LOCATION = "detail/location"
|
||||||
const val SETUP = "detail/setup"
|
const val SETUP = "detail/setup"
|
||||||
const val LOGIN = "detail/login"
|
const val LOGIN = "detail/login"
|
||||||
|
const val EMAIL_LOGIN = "detail/email-login"
|
||||||
const val NEWS = "detail/news"
|
const val NEWS = "detail/news"
|
||||||
const val NEWS_DETAIL = "detail/news/{newsId}"
|
const val NEWS_DETAIL = "detail/news/{newsId}"
|
||||||
const val DEVICE = "detail/device/{deviceId}"
|
const val DEVICE = "detail/device/{deviceId}"
|
||||||
|
|||||||
9
app/src/main/res/drawable/ic_arrow_back.xml
Normal file
9
app/src/main/res/drawable/ic_arrow_back.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
|
||||||
|
</vector>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<string name="button_cancel">Адмяніць</string>
|
<string name="button_cancel">Адмяніць</string>
|
||||||
<string name="text_privacy_policy"><b><u>Палітыка канфідэнцыйнасці</u></b></string>
|
<string name="text_privacy_policy"><b><u>Палітыка канфідэнцыйнасці</u></b></string>
|
||||||
<string name="button_sign_in_google">Увайсці з дапамогай Google</string>
|
<string name="button_sign_in_google">Увайсці з дапамогай Google</string>
|
||||||
<string name="button_sign_in_facebook">Увайсці з дапамогай Facebook</string>
|
<string name="button_sign_in_email">Увайсці па email</string>
|
||||||
<string name="button_look_for_device">Пошук прылады</string>
|
<string name="button_look_for_device">Пошук прылады</string>
|
||||||
<string name="button_finish">Завяршыць</string>
|
<string name="button_finish">Завяршыць</string>
|
||||||
<string name="button_continue_anonym">Працягнуць ананімна</string>
|
<string name="button_continue_anonym">Працягнуць ананімна</string>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
<string name="button_cancel">Отменить</string>
|
<string name="button_cancel">Отменить</string>
|
||||||
<string name="text_privacy_policy"><b><u>Политикой конфиденциальности</u></b></string>
|
<string name="text_privacy_policy"><b><u>Политикой конфиденциальности</u></b></string>
|
||||||
<string name="button_sign_in_google">Войти с помощью Google</string>
|
<string name="button_sign_in_google">Войти с помощью Google</string>
|
||||||
<string name="button_sign_in_facebook">Войти с помощью Facebook</string>
|
<string name="button_sign_in_email">Войти по email</string>
|
||||||
<string name="button_look_for_device">Поиск устройства</string>
|
<string name="button_look_for_device">Поиск устройства</string>
|
||||||
<string name="button_finish">Завершить</string>
|
<string name="button_finish">Завершить</string>
|
||||||
<string name="button_continue_anonym">Продолжить анонимно</string>
|
<string name="button_continue_anonym">Продолжить анонимно</string>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
<string name="button_cancel">Cancel</string>
|
<string name="button_cancel">Cancel</string>
|
||||||
<string name="button_register">Register</string>
|
<string name="button_register">Register</string>
|
||||||
<string name="button_sign_in_google">Sign in with Google</string>
|
<string name="button_sign_in_google">Sign in with Google</string>
|
||||||
<string name="button_sign_in_facebook">Sign in with Facebook</string>
|
<string name="button_sign_in_email">Sign in with email</string>
|
||||||
<string name="button_copy_log">Copy log</string>
|
<string name="button_copy_log">Copy log</string>
|
||||||
<string name="button_look_for_device">Look for a device</string>
|
<string name="button_look_for_device">Look for a device</string>
|
||||||
<string name="button_finish">Finish</string>
|
<string name="button_finish">Finish</string>
|
||||||
@@ -92,7 +92,10 @@
|
|||||||
<string name="button_setup">Add new device</string>
|
<string name="button_setup">Add new device</string>
|
||||||
<string name="button_connecting">Connecting…</string>
|
<string name="button_connecting">Connecting…</string>
|
||||||
|
|
||||||
|
<string name="text_sign_in_email">Sign in with email</string>
|
||||||
|
|
||||||
<!-- Hints -->
|
<!-- Hints -->
|
||||||
|
<string name="hint_email">Email</string>
|
||||||
<string name="hint_password">Password</string>
|
<string name="hint_password">Password</string>
|
||||||
<string name="hint_device_name">Device name</string>
|
<string name="hint_device_name">Device name</string>
|
||||||
<string name="hint_setup_name">Name</string>
|
<string name="hint_setup_name">Name</string>
|
||||||
@@ -142,8 +145,6 @@
|
|||||||
<string name="toast_copied">Copied</string>
|
<string name="toast_copied">Copied</string>
|
||||||
<string name="snackbar_not_registered">Device requires registration</string>
|
<string name="snackbar_not_registered">Device requires registration</string>
|
||||||
|
|
||||||
<string name="facebook_app_id" translatable="false">356744979027664</string>
|
|
||||||
<string name="fb_login_protocol_scheme" translatable="false">fb356744979027664</string>
|
|
||||||
<string name="text_air_quality">Air quality</string>
|
<string name="text_air_quality">Air quality</string>
|
||||||
<string name="pref_fullscreen">Borderless layout</string>
|
<string name="pref_fullscreen">Borderless layout</string>
|
||||||
<string name="text_error">Error</string>
|
<string name="text_error">Error</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user