Parcourir la source

Created master login screen and its architecture for master password after successfully logged in

Khubaib il y a 3 mois
Parent
commit
09dcb47313

+ 12 - 8
app/src/main/java/com/fastest/pass/login/presentation/components/LoginScreen.kt

@@ -1,6 +1,5 @@
 package com.fastest.pass.login.presentation.components
 
-import android.widget.Toast
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.border
@@ -60,7 +59,8 @@ import com.fastest.pass.R
 enum class ClickType {
     SIGNUP_CLICK,
     FORGOT_PASSWORD_CLICK,
-    GO_BACK
+    GO_BACK,
+    MASTER_LOGIN
 }
 
 @Composable
@@ -93,12 +93,14 @@ fun LoginScreen(clickType: (ClickType) -> Unit) {
                         colorResource(id = R.color.light_gray_login),
                     )
             ) {
-                ShowWelcomeText(R.string.enter_master_password)
+                ShowWelcomeText(R.string.login_to_continue)
                 LoginTextField(keyboardController, focusManager)
                 Spacer(modifier = Modifier.height(20.dp))
                 PasswordTextField(keyboardController, focusManager)
                 Spacer(modifier = Modifier.height(25.dp))
-                LoginButton(buttonText = R.string.login)
+                LoginButton(buttonText = R.string.login) { clickType ->
+                    clickType(clickType)
+                }
                 ForgotPasswordText() { clickType ->
                     clickType(clickType)
                 }
@@ -206,7 +208,7 @@ fun ColumnScope.LoginTextField(
         shape = RoundedCornerShape(16.dp),
         placeholder = {
             Text(
-                text = "Enter email address",
+                text = stringResource(id = R.string.enter_email_address),
                 color = colorResource(id = R.color.gray_splash))
         },
 //        label = {
@@ -272,7 +274,7 @@ fun ColumnScope.PasswordTextField(
             .background(color = colorResource(id = R.color.transparent)),
                 shape = RoundedCornerShape(16.dp),
                 placeholder = {
-                    Text(text = "Master password",
+                    Text(text = stringResource(id = R.string.enter_password),
                         color = colorResource(id = R.color.gray_splash))
                 },
 //        label = {
@@ -336,7 +338,7 @@ fun ColumnScope.PasswordTextField(
 }
 
 @Composable
-fun ColumnScope.LoginButton(buttonText: Int) {
+fun ColumnScope.LoginButton(buttonText: Int, clickType: (ClickType) -> Unit) {
     Button(
         modifier = Modifier
             .padding(start = 30.dp, end = 30.dp,)
@@ -344,7 +346,9 @@ fun ColumnScope.LoginButton(buttonText: Int) {
             .fillMaxWidth()
             .height(60.dp)
             .clickable() { },
-        onClick = {},
+        onClick = {
+            clickType.invoke(ClickType.MASTER_LOGIN)
+        },
         shape = RoundedCornerShape(15.dp),
 //            border = BorderStroke(25.dp, colorResource(id = R.color.black)),
         colors = ButtonDefaults.buttonColors(

+ 3 - 0
app/src/main/java/com/fastest/pass/login/presentation/ui/LoginFragment.kt

@@ -47,6 +47,9 @@ class LoginFragment : Fragment() {
                             ClickType.GO_BACK -> {
                                 viewmodel.navigateTo(LoginRoute.GoBack)
                             }
+                            ClickType.MASTER_LOGIN -> {
+                                viewmodel.navigateTo(LoginRoute.OpenMasterLoginScreen)
+                            }
                         }
                     }
                 }

+ 3 - 0
app/src/main/java/com/fastest/pass/login/utils/LoginNavigation.kt

@@ -21,6 +21,9 @@ class LoginNavigation {
                     LoginRoute.GoBack -> {
                         loginFragment.findNavController().popBackStack()
                     }
+                    LoginRoute.OpenMasterLoginScreen -> {
+                        loginFragment.findNavController().navigate(R.id.masterLoginFragment)
+                    }
                 }
 
                 loginFragment.viewmodel.navigateTo(LoginRoute.OpenNoneScreen)

+ 1 - 0
app/src/main/java/com/fastest/pass/login/utils/LoginRoute.kt

@@ -5,4 +5,5 @@ sealed class LoginRoute {
     data object OpenForgotPassword : LoginRoute()
     data object OpenNoneScreen : LoginRoute()
     data object GoBack : LoginRoute()
+    data object OpenMasterLoginScreen : LoginRoute()
 }

+ 19 - 0
app/src/main/java/com/fastest/pass/masterlogin/di/MasterLoginModule.kt

@@ -0,0 +1,19 @@
+package com.fastest.pass.masterlogin.di
+
+import com.fastest.pass.masterlogin.utils.MasterLoginNavigation
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object MasterLoginModule {
+
+    @Provides
+    @Singleton
+    fun provideNavigation(): MasterLoginNavigation {
+        return MasterLoginNavigation()
+    }
+}

+ 301 - 0
app/src/main/java/com/fastest/pass/masterlogin/presentation/components/MasterLoginScreen.kt

@@ -0,0 +1,301 @@
+package com.fastest.pass.masterlogin.presentation.components
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+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.size
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.focus.FocusManager
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.platform.SoftwareKeyboardController
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+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.input.VisualTransformation
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.fastest.pass.R
+import com.fastest.pass.login.presentation.components.CreateAccountText
+import com.fastest.pass.login.presentation.components.ForgotPasswordText
+import com.fastest.pass.login.presentation.components.LoginButton
+import com.fastest.pass.login.presentation.components.LoginTextField
+import com.fastest.pass.login.presentation.components.PasswordTextField
+import com.fastest.pass.login.presentation.components.ShowHeaderLogin
+import com.fastest.pass.login.presentation.components.ShowWelcomeText
+
+enum class ClickType {
+    GO_BACK,
+    HOME_SCREEN
+}
+
+@Composable
+fun MasterLoginScreen(clickType: (ClickType) -> Unit) {
+    val keyboardController = LocalSoftwareKeyboardController.current
+    val focusManager = LocalFocusManager.current
+
+    Box(
+        modifier = Modifier
+            .background(colorResource(id = R.color.blue_login))
+            .fillMaxSize()
+            .statusBarsPadding()
+    ) {
+        Column(
+            modifier = Modifier
+                .fillMaxSize()
+                .padding(top = 0.dp)
+                .statusBarsPadding()
+        ) {
+            ShowMasterLoginHeader(text = stringResource(R.string.login)) { clickType ->
+                clickType(clickType)
+            }
+
+            Column(
+                modifier = Modifier
+                    .fillMaxSize()
+                    .padding(top = 25.dp)
+                    .clip(RoundedCornerShape(topStart = 35.dp, topEnd = 35.dp))
+                    .background(
+                        colorResource(id = R.color.light_gray_login),
+                    ),
+            ) {
+                ShowWelcomeTextMLS(R.string.enter_master_password)
+                Spacer(modifier = Modifier.height(50.dp))
+                PasswordTextFieldMLS(keyboardController, focusManager)
+                Spacer(modifier = Modifier.height(25.dp))
+                MasterLoginButton(buttonText = R.string.unlock_fastestpass) { clickType ->
+                    clickType(clickType)
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.ShowMasterLoginHeader(text: String, clickType: (ClickType) -> Unit) {
+    val c = LocalContext.current
+    Row(
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(top = 30.dp)
+            .fillMaxWidth(),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        IconButton(
+            onClick = {
+                clickType.invoke(ClickType.GO_BACK)
+            },
+            modifier = Modifier
+                .padding(start = 30.dp)
+                .size(24.dp, 24.dp)
+        ) {
+            Image(
+                painter = painterResource(id = R.drawable.arrow_left),
+                contentDescription = "Arrow-Back",
+                modifier = Modifier.size(24.dp, 24.dp)
+            )
+        }
+        Surface(
+            modifier = Modifier
+                .padding(start = 15.dp),
+            color = colorResource(id = R.color.transparent)
+        ) {
+            Text(
+                text = text,
+                color = colorResource(id = R.color.white),
+                style = MaterialTheme.typography.displayLarge.copy(
+                    fontSize = 24.sp
+                ),
+            )
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.ShowWelcomeTextMLS(
+    masterPasswordText: Int,
+) {
+    Text(
+        text = stringResource(id = R.string.master_password_required),
+        color = colorResource(id = R.color.gray_splash),
+        style = MaterialTheme.typography.headlineLarge.copy(
+            fontSize = 26.sp
+        ),
+        lineHeight = 20.sp,
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(start = 30.dp, top = 35.dp)
+    )
+    Text(
+        text = stringResource(id = masterPasswordText),
+        color = colorResource(id = R.color.gray_splash),
+        style = MaterialTheme.typography.displayLarge.copy(
+            fontSize = 18.sp
+        ),
+        lineHeight = 25.sp,
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(start = 30.dp, top = 5.dp, end = 50.dp)
+
+    )
+}
+
+@Composable
+fun ColumnScope.PasswordTextFieldMLS(
+    keyboardController: SoftwareKeyboardController?,
+    focusManager: FocusManager
+) {
+    var passwordText by remember { mutableStateOf("") }
+    var passwordVisible by remember { mutableStateOf(false) }
+
+    TextField(
+        value = passwordText,
+        onValueChange = {
+            passwordText = it
+        },
+        textStyle = MaterialTheme.typography.displaySmall,
+        modifier = Modifier
+            .padding(start = 30.dp, end = 30.dp)
+            .align(Alignment.CenterHorizontally)
+            .fillMaxWidth()
+            .height(60.dp)
+            .border(
+                1.dp,
+                color = colorResource(id = R.color.gray_border_textfield),
+                shape = RoundedCornerShape(16.dp)
+            )
+            .background(color = colorResource(id = R.color.transparent)),
+        shape = RoundedCornerShape(16.dp),
+        placeholder = {
+            Text(text = stringResource(id = R.string.master_password),
+                color = colorResource(id = R.color.gray_splash))
+        },
+//        label = {
+//            Text(text = context.getString(R.string.password),
+//                style = MaterialTheme.typography.customTypography.bodyLarge
+//            )
+//        },
+        maxLines = 1,
+        colors = TextFieldDefaults.colors(
+            focusedLabelColor = colorResource(id = R.color.gray_splash),
+            unfocusedContainerColor = colorResource(id = R.color.transparent),
+            focusedContainerColor = colorResource(id = R.color.transparent),
+            focusedIndicatorColor = colorResource(id = R.color.transparent),
+            disabledIndicatorColor = colorResource(id = R.color.transparent),
+            unfocusedIndicatorColor = colorResource(id = R.color.transparent),
+            cursorColor = colorResource(id = R.color.gray_splash),
+        ),
+        keyboardOptions = KeyboardOptions(
+            keyboardType = KeyboardType.Password,
+            imeAction = ImeAction.Done
+        ),
+        keyboardActions = KeyboardActions(
+            onDone = {
+                focusManager.clearFocus()
+                keyboardController?.hide()
+            }
+        ),
+        visualTransformation =
+        if (passwordVisible) VisualTransformation.Companion.None
+        else PasswordVisualTransformation(),
+
+        trailingIcon = {
+            val description = if (passwordVisible) "Hide Password"
+            else "Show Password"
+
+            IconButton(onClick = {
+                passwordVisible = !passwordVisible
+            })
+            {
+                if (passwordVisible) {
+                    Image(
+                        painter = painterResource(id = R.drawable.eye_open),
+                        contentDescription = description,
+                        modifier = Modifier.size(24.dp)
+                    )
+                }
+                else {
+                    Image(
+                        painter = painterResource(id = R.drawable.eye_slash3x),
+                        contentDescription = description,
+                        modifier = Modifier
+                            .size(24.dp),
+                        colorFilter = ColorFilter.tint(
+                            colorResource(id = R.color.gray_splash)
+                        )
+                    )
+                }
+            }
+        }
+    )
+}
+
+@Composable
+fun ColumnScope.MasterLoginButton(
+    buttonText: Int,
+    clickType: (ClickType) -> Unit
+) {
+    Button(
+        modifier = Modifier
+            .padding(start = 30.dp, end = 30.dp,)
+            .background(colorResource(id = R.color.transparent))
+            .fillMaxWidth()
+            .height(60.dp)
+            .clickable() { },
+        onClick = {
+            clickType.invoke(ClickType.HOME_SCREEN)
+        },
+        shape = RoundedCornerShape(15.dp),
+//            border = BorderStroke(25.dp, colorResource(id = R.color.black)),
+        colors = ButtonDefaults.buttonColors(
+            contentColor = colorResource(id = R.color.white),
+            containerColor = colorResource(id = R.color.red_login_button),
+        ),
+    )
+    {
+        Text(
+            text = stringResource(id = buttonText),
+            style = MaterialTheme.typography.bodyMedium.copy(
+                fontSize = 20.sp,
+                color = colorResource(id = R.color.white)
+            ),
+            textAlign = TextAlign.Center
+        )
+    }
+}

+ 52 - 0
app/src/main/java/com/fastest/pass/masterlogin/presentation/ui/MasterLoginFragment.kt

@@ -0,0 +1,52 @@
+package com.fastest.pass.masterlogin.presentation.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import com.fastest.pass.masterlogin.presentation.components.ClickType
+import com.fastest.pass.masterlogin.presentation.components.MasterLoginScreen
+import com.fastest.pass.masterlogin.presentation.viewmodels.MasterLoginViewModel
+import com.fastest.pass.masterlogin.utils.MasterLoginNavigation
+import com.fastest.pass.masterlogin.utils.MasterLoginRoute
+import com.fastest.pass.ui.theme.FastestPassTheme
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MasterLoginFragment : Fragment() {
+
+    val viewmodel: MasterLoginViewModel by viewModels()
+
+    @Inject
+    lateinit var navigation: MasterLoginNavigation
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        navigation.navigate(this)
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        return ComposeView(requireActivity()).apply {
+            setContent {
+                FastestPassTheme {
+                    MasterLoginScreen { clickType ->
+                        when (clickType) {
+                            ClickType.GO_BACK -> {
+                                viewmodel.navigateTo(MasterLoginRoute.GoBackToLoginScreen)
+                            }
+                            ClickType.HOME_SCREEN -> {}
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 16 - 0
app/src/main/java/com/fastest/pass/masterlogin/presentation/viewmodels/MasterLoginViewModel.kt

@@ -0,0 +1,16 @@
+package com.fastest.pass.masterlogin.presentation.viewmodels
+
+import androidx.lifecycle.ViewModel
+import com.fastest.pass.masterlogin.presentation.ui.MasterLoginFragment
+import com.fastest.pass.masterlogin.utils.MasterLoginRoute
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class MasterLoginViewModel : ViewModel() {
+
+    private val _router = MutableStateFlow<MasterLoginRoute>(MasterLoginRoute.OpenNoneScreen)
+    val router: MutableStateFlow<MasterLoginRoute> = _router
+
+    fun navigateTo(masterLoginRoute: MasterLoginRoute) {
+        _router.value = masterLoginRoute
+    }
+}

+ 24 - 0
app/src/main/java/com/fastest/pass/masterlogin/utils/MasterLoginNavigation.kt

@@ -0,0 +1,24 @@
+package com.fastest.pass.masterlogin.utils
+
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.fragment.findNavController
+import com.fastest.pass.masterlogin.presentation.ui.MasterLoginFragment
+
+class MasterLoginNavigation {
+
+    fun navigate(masterLoginFragment: MasterLoginFragment) {
+        masterLoginFragment.lifecycleScope.launchWhenStarted {
+            masterLoginFragment.viewmodel.router.collect { router ->
+                when (router) {
+                    MasterLoginRoute.OpenNoneScreen -> {}
+                    MasterLoginRoute.OpenHomeScreen -> {}
+                    MasterLoginRoute.GoBackToLoginScreen -> {
+                        masterLoginFragment.findNavController().popBackStack()
+                    }
+                }
+
+                masterLoginFragment.viewmodel.navigateTo(MasterLoginRoute.OpenNoneScreen)
+            }
+        }
+    }
+}

+ 7 - 0
app/src/main/java/com/fastest/pass/masterlogin/utils/MasterLoginRoute.kt

@@ -0,0 +1,7 @@
+package com.fastest.pass.masterlogin.utils
+
+sealed class MasterLoginRoute {
+    data object OpenHomeScreen : MasterLoginRoute()
+    data object GoBackToLoginScreen : MasterLoginRoute()
+    data object OpenNoneScreen : MasterLoginRoute()
+}

+ 30 - 5
app/src/main/java/com/fastest/pass/signup/presentation/components/SignUpScreen.kt

@@ -51,10 +51,6 @@ import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import com.fastest.pass.R
-import com.fastest.pass.login.presentation.components.LoginButton
-import com.fastest.pass.login.presentation.components.LoginTextField
-import com.fastest.pass.login.presentation.components.PasswordTextField
-import com.fastest.pass.login.presentation.components.ShowWelcomeText
 
 enum class ClickType {
     GO_BACK
@@ -90,7 +86,7 @@ fun SignUpScreen(clickType: (ClickType) -> Unit) {
                         colorResource(id = R.color.light_gray_login),
                     )
             ) {
-                ShowWelcomeText(R.string.setup_master_password)
+                ShowWelcomeTextSU(R.string.setup_master_password)
                 SignUpTextField(keyboardController, focusManager)
                 Spacer(modifier = Modifier.height(20.dp))
                 PasswordTextFieldSU(keyboardController, focusManager)
@@ -145,6 +141,35 @@ fun ColumnScope.ShowHeaderSignUp(
 }
 
 @Composable
+fun ColumnScope.ShowWelcomeTextSU(
+    masterPasswordText: Int,
+) {
+    Text(
+        text = stringResource(id = R.string.master_password_required),
+        color = colorResource(id = R.color.gray_splash),
+        style = MaterialTheme.typography.headlineLarge.copy(
+            fontSize = 26.sp
+        ),
+        lineHeight = 20.sp,
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(start = 30.dp, top = 35.dp)
+    )
+    Text(
+        text = stringResource(id = masterPasswordText),
+        color = colorResource(id = R.color.gray_splash),
+        style = MaterialTheme.typography.displayLarge.copy(
+            fontSize = 18.sp
+        ),
+        lineHeight = 25.sp,
+        modifier = Modifier
+            .align(Alignment.Start)
+            .padding(start = 30.dp, top = 5.dp, end = 50.dp)
+
+    )
+}
+
+@Composable
 fun ColumnScope.SignUpTextField(
     keyboardController: SoftwareKeyboardController?,
     focusManager: FocusManager

+ 5 - 0
app/src/main/res/navigation/nav_graph.xml

@@ -27,4 +27,9 @@
         android:name="com.fastest.pass.forgotpassword.presentation.ui.ForgotPasswordFragment"
         android:label="ForgotPasswordFragment" />
 
+    <fragment
+        android:id="@+id/masterLoginFragment"
+        android:name="com.fastest.pass.masterlogin.presentation.ui.MasterLoginFragment"
+        android:label="MasterLoginFragment" />
+
 </navigation>

+ 6 - 0
app/src/main/res/values/strings.xml

@@ -12,4 +12,10 @@
     <string name="forgot_password">Forgot Password</string>
     <string name="email_associated_account">Enter the email address associated with your account.</string>
     <string name="send_code">Send Code</string>
+    <string name="welcome_back">Welcome back!</string>
+    <string name="login_to_continue">Please log in to continue.</string>
+    <string name="enter_email_address">Enter email address"</string>
+    <string name="enter_password">Enter Password</string>
+    <string name="master_password">Master Password</string>
+    <string name="unlock_fastestpass">Unlock FastestPass</string>
 </resources>