diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1d9e149d..da109e5a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,5 +1,4 @@ // import org.gradle.api.GradleException -import com.android.build.api.dsl.ApplicationExtension import org.jetbrains.kotlin.gradle.dsl.JvmTarget import java.io.FileInputStream import java.text.SimpleDateFormat diff --git a/app/src/main/kotlin/com/jeeldobariya/passcodes/navigation/NavigationRoot.kt b/app/src/main/kotlin/com/jeeldobariya/passcodes/navigation/NavigationRoot.kt index 97c1ad35..3ab444e5 100644 --- a/app/src/main/kotlin/com/jeeldobariya/passcodes/navigation/NavigationRoot.kt +++ b/app/src/main/kotlin/com/jeeldobariya/passcodes/navigation/NavigationRoot.kt @@ -13,16 +13,21 @@ import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.jeeldobariya.passcodes.core.datastore.AppSettings import com.jeeldobariya.passcodes.core.datastore.appDatastore -import com.jeeldobariya.passcodes.core.feature_flags.FeatureFlagsSettings -import com.jeeldobariya.passcodes.core.feature_flags.featureFlagsDatastore import com.jeeldobariya.passcodes.core.navigation.Route -import com.jeeldobariya.passcodes.password_manager.ui.PasswordManagerScreen -import com.jeeldobariya.passcodes.password_manager.ui.SavePasswordScreen -import com.jeeldobariya.passcodes.password_manager.ui.UpdatePasswordScreen -import com.jeeldobariya.passcodes.ui.AboutScreen +import com.jeeldobariya.passcodes.password_manager.ui.ClassicalLoadPasswordScreen +import com.jeeldobariya.passcodes.password_manager.ui.ClassicalPasswordManagerScreen +import com.jeeldobariya.passcodes.password_manager.ui.ClassicalSavePasswordScreen +import com.jeeldobariya.passcodes.password_manager.ui.ClassicalUpdatePasswordScreen +import com.jeeldobariya.passcodes.password_manager.ui.ClassicalViewPasswordScreen +import com.jeeldobariya.passcodes.password_manager.ui.ModernPasswordManagerScreen +import com.jeeldobariya.passcodes.password_manager.ui.ModernSavePasswordScreen +import com.jeeldobariya.passcodes.password_manager.ui.ModernUpdatePasswordScreen +import com.jeeldobariya.passcodes.ui.ClassicalAboutScreen import com.jeeldobariya.passcodes.ui.ClassicalMainScreen -import com.jeeldobariya.passcodes.ui.MainScreen -import com.jeeldobariya.passcodes.ui.SettingsScreen +import com.jeeldobariya.passcodes.ui.ClassicalSettingsScreen +import com.jeeldobariya.passcodes.ui.ModernAboutScreen +import com.jeeldobariya.passcodes.ui.ModernMainScreen +import com.jeeldobariya.passcodes.ui.ModernSettingsScreen @Composable @@ -38,34 +43,42 @@ private fun ModernNavigationRoot(backStack: NavBackStack, navigateTo: (R ), entryProvider = entryProvider { entry { - MainScreen(navigateTo) + ModernMainScreen(navigateTo) } entry { - SettingsScreen() + ModernSettingsScreen() } entry { - AboutScreen() + ModernAboutScreen() } entry { - PasswordManagerScreen(navigateTo) + ModernPasswordManagerScreen(navigateTo) + } + + entry { + ModernPasswordManagerScreen(navigateTo) } entry { - SavePasswordScreen() + ModernSavePasswordScreen() + } + + entry { // Theoretically, Should be treated as `UpdatePassword` Route in Modern Layout... + ModernUpdatePasswordScreen(it.id) } entry { - UpdatePasswordScreen(it.id) + ModernUpdatePasswordScreen(it.id) } } ) } @Composable -private fun ClassicNavigationRoot(backStack: NavBackStack, navigateTo: (Route) -> Unit) { +private fun ClassicalNavigationRoot(backStack: NavBackStack, navigateTo: (Route) -> Unit) { NavDisplay( backStack = backStack, onBack = { @@ -77,27 +90,35 @@ private fun ClassicNavigationRoot(backStack: NavBackStack, navigateTo: ( ), entryProvider = entryProvider { entry { - ClassicalMainScreen(navigateTo) + ClassicalMainScreen(navigateTo = navigateTo) } entry { - SettingsScreen() + ClassicalSettingsScreen() } entry { - AboutScreen() + ClassicalAboutScreen() } entry { - PasswordManagerScreen(navigateTo) + ClassicalPasswordManagerScreen(navigateTo = navigateTo) + } + + entry { + ClassicalLoadPasswordScreen(navigateTo = navigateTo) } entry { - SavePasswordScreen() + ClassicalSavePasswordScreen() + } + + entry { + ClassicalViewPasswordScreen(passwordId = it.id, navigateTo = navigateTo) } entry { - UpdatePasswordScreen(it.id) + ClassicalUpdatePasswordScreen(passwordId = it.id) } } ) @@ -110,10 +131,11 @@ fun NavigationRoot() { val appDataStore = LocalContext.current.appDatastore val appDatastoreState by appDataStore.data.collectAsState(AppSettings()) - fun navigateTo(route: Route): Unit { + fun navigateTo(route: Route) { backStack.add(route) } - if (appDatastoreState.isModernLayoutEnable) { ModernNavigationRoot(backStack, ::navigateTo) } - else { ClassicNavigationRoot(backStack, ::navigateTo) } + if (appDatastoreState.isModernLayoutEnable) { ModernNavigationRoot(backStack, ::navigateTo) } else { + ClassicalNavigationRoot(backStack, ::navigateTo) + } } diff --git a/app/src/main/kotlin/com/jeeldobariya/passcodes/oldui/LicenseActivity.kt b/app/src/main/kotlin/com/jeeldobariya/passcodes/oldui/LicenseActivity.kt index ef3cacbe..b35a10b9 100644 --- a/app/src/main/kotlin/com/jeeldobariya/passcodes/oldui/LicenseActivity.kt +++ b/app/src/main/kotlin/com/jeeldobariya/passcodes/oldui/LicenseActivity.kt @@ -3,7 +3,7 @@ package com.jeeldobariya.passcodes.oldui import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.google.android.gms.oss.licenses.OssLicensesMenuActivity +import com.google.android.gms.oss.licenses.v2.OssLicensesMenuActivity import com.jeeldobariya.passcodes.core.datastore.appDatastore import com.jeeldobariya.passcodes.databinding.ActivityLicenseBinding import kotlinx.coroutines.flow.first @@ -12,7 +12,6 @@ import java.io.BufferedReader import java.io.InputStreamReader class LicenseActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { runBlocking { setTheme(appDatastore.data.first().theme) @@ -31,7 +30,7 @@ class LicenseActivity : AppCompatActivity() { e.printStackTrace() } - binding.thirdPartyBtn.setOnClickListener { v -> + binding.thirdPartyBtn.setOnClickListener { _ -> val thirdPartyLicenseActivity = Intent(this, OssLicensesMenuActivity::class.java) startActivity(thirdPartyLicenseActivity) } diff --git a/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/AboutScreen.kt b/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/AboutScreen.kt index 7917da3c..e7ee42f1 100644 --- a/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/AboutScreen.kt +++ b/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/AboutScreen.kt @@ -1,34 +1,48 @@ package com.jeeldobariya.passcodes.ui +import android.content.Intent import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +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.material3.Button +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Card -import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp +import androidx.core.net.toUri +import com.jeeldobariya.passcodes.Constant import com.jeeldobariya.passcodes.R import com.jeeldobariya.passcodes.ui.ui.theme.PasscodesTheme @Composable -fun AboutScreen() { +fun ModernAboutScreen() { Scaffold { padding -> Column( modifier = Modifier @@ -72,11 +86,257 @@ fun AboutScreen() { } -@Preview(showBackground = true) +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ClassicalAboutScreen() { + Scaffold( + topBar = { + TopAppBar( + title = { Text("About Us") }, + navigationIcon = { + IconButton(onClick = {}) { + Icon(imageVector = Icons.AutoMirrored.Default.ArrowBack, contentDescription = "") + } + } + ) + } + ) { padding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(padding), + contentPadding = PaddingValues(12.dp) + ) { + item { + Text( + text = stringResource(R.string.textview_app_description), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + + item { + Text( + text = stringResource(R.string.textview_app_warning), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(bottom = 24.dp) + ) + } + + // Grid Section + item { + AboutGridSection() + } + + item { + Spacer(modifier = Modifier.height(24.dp)) + } + + item { + Text( + text = stringResource(R.string.label_contributor), + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + + item { + ContributorCard(stringResource(R.string.developer_name)) + } + + item { + ContributorCard(stringResource(R.string.code_maintainer)) + } + + item { + ContributorCard(stringResource(R.string.co_developer)) + } + } + } +} + +@Composable +private fun AboutGridSection() { + val context = LocalContext.current + + Column( + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + AboutCard( + modifier = Modifier.weight(1f), + icon = painterResource(R.drawable.ic_security), + text = stringResource(R.string.view_security_guidelines_button_text), + onClick = { + Intent(Intent.ACTION_VIEW, Constant.SECURITY_GUIDE_URL.toUri()).also { + context.startActivity(it) + } + } + ) + + AboutCard( + modifier = Modifier.weight(1f), + icon = painterResource(R.drawable.ic_history), + text = stringResource(R.string.view_release_notes_button_text), + onClick = { + Intent(Intent.ACTION_VIEW, Constant.RELEASE_NOTE_URL.toUri()).also { + context.startActivity(it) + } + } + ) + } + + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + AboutCard( + modifier = Modifier.weight(1f), + icon = painterResource(R.drawable.ic_article), + text = stringResource(R.string.view_license_button_text), + onClick = { + /* TODO: Add License Activity Link */ + + Intent( + Intent.ACTION_VIEW, + "https://passcodesapp.github.io/Passcodes-Docs/LICENSE/".toUri() + ).also { + context.startActivity(it) + } + } + ) + + AboutCard( + modifier = Modifier.weight(1f), + icon = painterResource(R.drawable.ic_bug_report), + text = stringResource(R.string.view_report_bug_text), + onClick = { + Intent(Intent.ACTION_VIEW, Constant.REPORT_BUG_URL.toUri()).also { + context.startActivity(it) + } + } + ) + } + + TelegramCard( + text = stringResource(R.string.view_telegram_community_text), + onClick = { + Intent(Intent.ACTION_VIEW, Constant.TELEGRAM_COMMUNITY_URL.toUri()).also { + context.startActivity(it) + } + } + ) + } +} + + +@Composable +private fun AboutCard( + modifier: Modifier = Modifier, + icon: Painter, + text: String, + onClick: () -> Unit +) { + Card( + onClick = onClick, + shape = RoundedCornerShape(16.dp), + modifier = modifier + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = icon, + contentDescription = text, + modifier = Modifier + .size(32.dp) + .padding(bottom = 8.dp) + ) + + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } + } +} + +@Composable +private fun TelegramCard( + text: String, + onClick: () -> Unit +) { + Card( + onClick = onClick, + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.secondaryContainer + ) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(R.drawable.ic_send), + contentDescription = text, + modifier = Modifier + .size(96.dp) + .padding(bottom = 8.dp) + ) + + Text( + text = text, + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center + ) + } + } +} + +@Composable +private fun ContributorCard(text: String) { + Card( + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.tertiaryContainer + ), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 8.dp) + ) { + Text( + text = text, + modifier = Modifier.padding(16.dp), + color = MaterialTheme.colorScheme.tertiary, + style = MaterialTheme.typography.bodyLarge + ) + } +} + + +@PreviewLightDark +@Composable +fun ModernAboutScreenPreview() { + PasscodesTheme { + ModernAboutScreen() + } +} + @PreviewLightDark @Composable -fun AboutScreenPreview() { +fun ClassicalAboutScreenPreview() { PasscodesTheme { - AboutScreen() + ClassicalAboutScreen() } } diff --git a/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/MainScreen.kt b/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/MainScreen.kt index 5472fb96..b8c5103a 100644 --- a/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/MainScreen.kt +++ b/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/MainScreen.kt @@ -28,15 +28,15 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.jeeldobariya.passcodes.R import com.jeeldobariya.passcodes.core.navigation.Route import com.jeeldobariya.passcodes.ui.ui.theme.PasscodesTheme + @Composable -fun MainScreen(navigateTo: (Route) -> Unit) { +fun ModernMainScreen(navigateTo: (Route) -> Unit) { Scaffold { padding -> Column( modifier = Modifier @@ -112,6 +112,7 @@ fun MainScreen(navigateTo: (Route) -> Unit) { } } + @Composable fun ClassicalMainScreen(navigateTo: (Route) -> Unit) { Scaffold { padding -> @@ -190,11 +191,19 @@ fun ClassicalMainScreen(navigateTo: (Route) -> Unit) { } -@Preview(showBackground = true) @PreviewLightDark @Composable -fun MainScreenPreview() { +fun ModernMainScreenPreview() { + PasscodesTheme { + ModernMainScreen(navigateTo = { }) + } +} + + +@PreviewLightDark +@Composable +fun ClassicalMainScreenPreview() { PasscodesTheme { - MainScreen(navigateTo = { }) + ClassicalMainScreen(navigateTo = { }) } } diff --git a/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/SettingsScreen.kt b/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/SettingsScreen.kt index c3000de6..f732e42b 100644 --- a/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/SettingsScreen.kt +++ b/app/src/main/kotlin/com/jeeldobariya/passcodes/ui/SettingsScreen.kt @@ -1,5 +1,6 @@ package com.jeeldobariya.passcodes.ui +import android.content.Intent import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -11,21 +12,34 @@ 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.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuAnchorType +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.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 import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -33,10 +47,10 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.jeeldobariya.passcodes.R +import com.jeeldobariya.passcodes.autofill.AutofillSettingsActivity import com.jeeldobariya.passcodes.core.datastore.AppSettings import com.jeeldobariya.passcodes.core.datastore.appDatastore import com.jeeldobariya.passcodes.core.feature_flags.FeatureFlagsSettings @@ -45,7 +59,8 @@ import com.jeeldobariya.passcodes.ui.ui.theme.PasscodesTheme import kotlinx.coroutines.launch @Composable -fun SettingsScreen() { +fun ModernSettingsScreen() { + val context = LocalContext.current val scope = rememberCoroutineScope() val flagDataStore = LocalContext.current.featureFlagsDatastore @@ -182,10 +197,11 @@ fun SettingsScreen() { .fillMaxWidth() .padding(8.dp) ) { - OutlinedButton( - modifier = Modifier.align(Alignment.TopStart), - onClick = { } - ) { + OutlinedButton(modifier = Modifier.align(Alignment.TopStart), onClick = { + Intent(context, AutofillSettingsActivity::class.java).also { + context.startActivity(it) + } + }) { Icon(imageVector = Icons.Default.Settings, contentDescription = "settings") Spacer(modifier = Modifier.padding(2.dp)) Text(text = stringResource(R.string.autofill_settings)) @@ -205,12 +221,269 @@ fun SettingsScreen() { } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ClassicalSettingsScreen() { + // TODO: Language & Theme need to be done. + + val selectedLanguage = "Under Development" + val languageOptions: List = listOf("English", "Korean") + val onLanguageSelected: (String) -> Unit = { /* TODO */ } + + val context = LocalContext.current + val scope = rememberCoroutineScope() + + val flagDataStore = LocalContext.current.featureFlagsDatastore + val flagDatastoreState by flagDataStore.data.collectAsState( + FeatureFlagsSettings( + version = 0, + isPreviewFeaturesEnabled = false, + isPreviewLayoutEnabled = false + ) + ) + val appDataStore = LocalContext.current.appDatastore + val appDatastoreState by appDataStore.data.collectAsState(initial = AppSettings()) + + var expanded by remember { mutableStateOf(false) } + + Scaffold { padding -> + + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(24.dp) + ) { + // Headline + item { + Text( + text = stringResource(R.string.textview_settings_headline), + style = MaterialTheme.typography.headlineMedium, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp) + ) + } + + // Language Card + item { + Card(shape = RoundedCornerShape(16.dp)) { + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Text( + modifier = Modifier.padding(4.dp), + text = stringResource(R.string.label_language_setting), + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.bodyMedium + ) + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded } + ) { + OutlinedTextField( + value = selectedLanguage, + onValueChange = {}, + readOnly = true, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded) + }, + modifier = Modifier.menuAnchor( + ExposedDropdownMenuAnchorType.PrimaryNotEditable, + enabled = true + ) + ) + + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + languageOptions.forEach { lang -> + DropdownMenuItem( + text = { Text(lang) }, + onClick = { + onLanguageSelected(lang) + expanded = false + } + ) + } + } + } + } + } + } + + // Theme Card + item { + Card(shape = RoundedCornerShape(16.dp)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Text( + text = stringResource(R.string.label_theme_setting), + color = MaterialTheme.colorScheme.primary + ) + + Button(onClick = { /* TODO */ }) { + Text( + text = stringResource(R.string.toggle_theme_button_text), + style = MaterialTheme.typography.labelMedium + ) + } + } + } + } + + // Latest Features Switch + item { + SwitchCard( + text = stringResource(R.string.latest_feature), + checked = flagDatastoreState.isPreviewFeaturesEnabled, + onCheckedChange = { + scope.launch { + flagDataStore.updateData { + it.copy(isPreviewFeaturesEnabled = !it.isPreviewFeaturesEnabled) + } + } + } + ) + } + + // Latest Layout Switch + item { + SwitchCard( + text = stringResource(R.string.preview_layout), + checked = flagDatastoreState.isPreviewLayoutEnabled, + onCheckedChange = { + scope.launch { + flagDataStore.updateData { + it.copy(isPreviewLayoutEnabled = !it.isPreviewLayoutEnabled) + } + } + } + ) + } + + // Modern Layout Switch + item { + SwitchCard( + text = "Modern Layout", + checked = appDatastoreState.isModernLayoutEnable, + onCheckedChange = { + scope.launch { + appDataStore.updateData { + it.copy(isModernLayoutEnable = !it.isModernLayoutEnable) + } + } + }, + enabled = flagDatastoreState.isPreviewLayoutEnabled + ) + } + + // Autofill Settings + item { + Card(shape = RoundedCornerShape(16.dp)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.Center + ) { + OutlinedButton(onClick = { + Intent(context, AutofillSettingsActivity::class.java).also { + context.startActivity(it) + } + }) { + Text( + text = stringResource(R.string.autofill_settings), + color = MaterialTheme.colorScheme.secondary + ) + } + } + } + } + + // Clear All Data + item { + Card(shape = RoundedCornerShape(16.dp)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.Center + ) { + OutlinedButton( + onClick = { /* TODO */ }, + colors = ButtonDefaults.outlinedButtonColors( + contentColor = MaterialTheme.colorScheme.error + ) + ) { + Text(stringResource(R.string.clear_all_data_button_text)) + } + } + } + } + } + } +} + +@Composable +fun SwitchCard( + text: String, + checked: Boolean, + onCheckedChange: (Boolean) -> Unit, + enabled: Boolean = true +) { + Card(shape = RoundedCornerShape(16.dp)) { + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Text( + text = text, + color = MaterialTheme.colorScheme.primary + ) + + Switch( + checked = checked, + onCheckedChange = onCheckedChange, + enabled = enabled + ) + } + } +} + +@PreviewLightDark +@Composable +fun ModernSettingsScreenPreview() { + PasscodesTheme { + ModernSettingsScreen() + } +} -@Preview(showBackground = true) @PreviewLightDark @Composable -fun SettingsScreenPreview() { +fun ClassicalSettingsScreenPreview() { PasscodesTheme { - SettingsScreen() + ClassicalSettingsScreen() } } diff --git a/core/src/main/kotlin/com/jeeldobariya/passcodes/core/datastore/AppSettingsSerializer.kt b/core/src/main/kotlin/com/jeeldobariya/passcodes/core/datastore/AppSettingsSerializer.kt index 03c78eb3..9c4f7c41 100644 --- a/core/src/main/kotlin/com/jeeldobariya/passcodes/core/datastore/AppSettingsSerializer.kt +++ b/core/src/main/kotlin/com/jeeldobariya/passcodes/core/datastore/AppSettingsSerializer.kt @@ -1,6 +1,8 @@ package com.jeeldobariya.passcodes.core.datastore import androidx.datastore.core.Serializer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import java.io.InputStream @@ -25,7 +27,7 @@ object AppSettingsSerializer : Serializer { override suspend fun writeTo( t: AppSettings, output: OutputStream - ) { + ) = withContext(Dispatchers.IO) { output.write( Json.encodeToString( serializer = AppSettings.serializer(), diff --git a/core/src/main/kotlin/com/jeeldobariya/passcodes/core/feature_flags/FeatureFlagsSettingsSerializer.kt b/core/src/main/kotlin/com/jeeldobariya/passcodes/core/feature_flags/FeatureFlagsSettingsSerializer.kt index 92d16f1f..c82b17a6 100644 --- a/core/src/main/kotlin/com/jeeldobariya/passcodes/core/feature_flags/FeatureFlagsSettingsSerializer.kt +++ b/core/src/main/kotlin/com/jeeldobariya/passcodes/core/feature_flags/FeatureFlagsSettingsSerializer.kt @@ -1,6 +1,8 @@ package com.jeeldobariya.passcodes.core.feature_flags import androidx.datastore.core.Serializer +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import java.io.InputStream @@ -25,7 +27,7 @@ object FeatureFlagsSettingsSerializer : Serializer { override suspend fun writeTo( t: FeatureFlagsSettings, output: OutputStream - ) { + ) = withContext(Dispatchers.IO) { output.write( Json.encodeToString( serializer = FeatureFlagsSettings.serializer(), diff --git a/core/src/main/kotlin/com/jeeldobariya/passcodes/core/navigation/Route.kt b/core/src/main/kotlin/com/jeeldobariya/passcodes/core/navigation/Route.kt index 165e0e21..d9fba69a 100644 --- a/core/src/main/kotlin/com/jeeldobariya/passcodes/core/navigation/Route.kt +++ b/core/src/main/kotlin/com/jeeldobariya/passcodes/core/navigation/Route.kt @@ -21,6 +21,13 @@ sealed interface Route: NavKey { @Serializable data object SavePassword: Route, NavKey + @Serializable + data object LoadPassword : Route, NavKey + + @Serializable + data class ViewPassword(val id: Int) : Route, NavKey + @Serializable data class UpdatePassword(val id: Int): Route, NavKey + } diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..500e9091 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/10fc3bf1ee0001078a473afe6e43cfdb/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/9c55677aff3966382f3d853c0959bfb2/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/248ffb1098f61659502d0c09aa348294/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/ac151d55def6b6a9a159dc4cb4642851/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6f5693cd..c8ef9f8a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ kotlinSerializationJson = "1.10.0" # Plugin versions agp = "9.0.1" -ksp = "2.3.5" +ksp = "2.3.6" kotlin = "2.3.10" oss-license-plugin = "0.10.10" # Also update in settings.gradle.kts diff --git a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/LoadPasswordScreen.kt b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/LoadPasswordScreen.kt new file mode 100644 index 00000000..24c6bc26 --- /dev/null +++ b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/LoadPasswordScreen.kt @@ -0,0 +1,77 @@ +package com.jeeldobariya.passcodes.password_manager.ui + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.jeeldobariya.passcodes.core.navigation.Route +import com.jeeldobariya.passcodes.password_manager.presentation.load_password.LoadPasswordAction +import com.jeeldobariya.passcodes.password_manager.presentation.load_password.LoadPasswordViewModel +import org.koin.compose.viewmodel.koinViewModel + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ClassicalLoadPasswordScreen( + navigateTo: (Route) -> Unit, + viewmodel: LoadPasswordViewModel = koinViewModel() +) { + Scaffold( + topBar = { + TopAppBar( + title = { + Text("Load Passwords") + } + ) + } + ) { paddingValue -> + + viewmodel.onAction(LoadPasswordAction.RefreshPassword) + val state = viewmodel.state.collectAsState() + + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddingValue), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + items( + items = state.value.passwordEntityList, + key = { it.id } + ) { passwordItem -> + Column( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = { navigateTo(Route.ViewPassword(passwordItem.id)) }) + .padding(12.dp) + ) { + Text( + text = passwordItem.domain, + style = MaterialTheme.typography.titleLarge + ) + Text( + text = passwordItem.username, + style = MaterialTheme.typography.bodySmall + ) + } + + HorizontalDivider(thickness = 2.dp) + } + } + } +} diff --git a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/PasswordManagerScreen.kt b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/PasswordManagerScreen.kt index 47f96978..26c722c5 100644 --- a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/PasswordManagerScreen.kt +++ b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/PasswordManagerScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -12,6 +13,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.AddCircle +import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.HorizontalDivider @@ -27,9 +29,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.PreviewLightDark +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.jeeldobariya.passcodes.core.navigation.Route import com.jeeldobariya.passcodes.password_manager.presentation.load_password.LoadPasswordAction import com.jeeldobariya.passcodes.password_manager.presentation.load_password.LoadPasswordViewModel @@ -38,7 +42,10 @@ import org.koin.compose.viewmodel.koinViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable -fun PasswordManagerScreen(navigateTo: (Route) -> Unit, viewmodel: LoadPasswordViewModel = koinViewModel()) { +fun ModernPasswordManagerScreen( + navigateTo: (Route) -> Unit, + viewmodel: LoadPasswordViewModel = koinViewModel() +) { val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -68,7 +75,9 @@ fun PasswordManagerScreen(navigateTo: (Route) -> Unit, viewmodel: LoadPasswordVi val state = viewmodel.state.collectAsState() LazyColumn( - modifier = Modifier.fillMaxSize().padding(paddingValue), + modifier = Modifier + .fillMaxSize() + .padding(paddingValue), contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), verticalArrangement = Arrangement.spacedBy(4.dp) ) { @@ -98,8 +107,35 @@ fun PasswordManagerScreen(navigateTo: (Route) -> Unit, viewmodel: LoadPasswordVi } } -@PreviewLightDark +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun PasswordManagerScreenPreview() { - PasswordManagerScreen({ }) +fun ClassicalPasswordManagerScreen(navigateTo: (Route) -> Unit) { + Scaffold( + modifier = Modifier.fillMaxSize() + ) { paddingValue -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValue) + .padding(12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text("Password Manager", fontSize = 36.sp, textAlign = TextAlign.Center) + + Spacer(modifier = Modifier.padding(8.dp)) + + Button(onClick = { + navigateTo(Route.SavePassword) + }) { + Text("Save Password") + } + + Button(onClick = { + navigateTo(Route.LoadPassword) + }) { + Text("Load Password") + } + } + } } diff --git a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/SavePasswordScreen.kt b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/SavePasswordScreen.kt index bee71f6b..dbd15cd7 100644 --- a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/SavePasswordScreen.kt +++ b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/SavePasswordScreen.kt @@ -19,7 +19,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.jeeldobariya.passcodes.password_manager.presentation.save_password.SavePasswordAction import com.jeeldobariya.passcodes.password_manager.presentation.save_password.SavePasswordViewModel @@ -27,7 +26,7 @@ import kotlinx.coroutines.launch import org.koin.compose.viewmodel.koinViewModel @Composable -fun SavePasswordScreen(viewmodel: SavePasswordViewModel = koinViewModel()) { +fun ModernSavePasswordScreen(viewmodel: SavePasswordViewModel = koinViewModel()) { val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -102,8 +101,88 @@ fun SavePasswordScreen(viewmodel: SavePasswordViewModel = koinViewModel()) { } } -@PreviewLightDark + @Composable -fun SavePasswordScreenPreview() { - SavePasswordScreen() +fun ClassicalSavePasswordScreen(viewmodel: SavePasswordViewModel = koinViewModel()) { + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + ) { paddingValues -> + val state by viewmodel.state.collectAsState() + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text("Save Password") + + Spacer(modifier = Modifier.padding(16.dp)) + + OutlinedTextField( + value = state.domain, + onValueChange = { + viewmodel.onAction(action = SavePasswordAction.OnChangeDomain(it)) + }, + label = { + Text("Domain:") + }, + placeholder = { + Text("e.g. Google, Instagram etc...") + } + ) + + OutlinedTextField( + value = state.username, + onValueChange = { + viewmodel.onAction(action = SavePasswordAction.OnChangeUsername(it)) + }, + label = { + Text("Username:") + }, + placeholder = { + Text("e.g. username / email / mobile.no.") + } + ) + + OutlinedTextField( + value = state.password, + onValueChange = { + viewmodel.onAction(action = SavePasswordAction.OnChangePassword(it)) + }, + label = { + Text("Password:") + }, + visualTransformation = PasswordVisualTransformation() + ) + + OutlinedTextField( + value = state.notes, + onValueChange = { + viewmodel.onAction(action = SavePasswordAction.OnChangeNotes(it)) + }, + label = { + Text("Notes (Optional):") + }, + placeholder = { + Text("e.g. Url or Platform Info. Account Info.") + } + ) + + Spacer(modifier = Modifier.padding(8.dp)) + + Button(onClick = { + viewmodel.onAction(action = SavePasswordAction.OnSavePasswordButtonClick) + scope.launch { + snackbarHostState.showSnackbar("Saved Successfully!!") + } + }) { + Text("Save Password") + } + } + } } diff --git a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/UpdatePasswordScreen.kt b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/UpdatePasswordScreen.kt index 3cb6fcc6..d0c65936 100644 --- a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/UpdatePasswordScreen.kt +++ b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/UpdatePasswordScreen.kt @@ -25,7 +25,10 @@ import kotlinx.coroutines.launch import org.koin.compose.viewmodel.koinViewModel @Composable -fun UpdatePasswordScreen(passwordId: Int, viewmodel: UpdatePasswordViewModel = koinViewModel()) { +fun ModernUpdatePasswordScreen( + passwordId: Int, + viewmodel: UpdatePasswordViewModel = koinViewModel() +) { val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -99,4 +102,85 @@ fun UpdatePasswordScreen(passwordId: Int, viewmodel: UpdatePasswordViewModel = k } } } -} \ No newline at end of file +} + + +@Composable +fun ClassicalUpdatePasswordScreen( + passwordId: Int, + viewmodel: UpdatePasswordViewModel = koinViewModel() +) { + val snackbarHostState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + + viewmodel.loadInitialData(passwordId) + + Scaffold( + snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, + ) { paddingValues -> + val state by viewmodel.state.collectAsState() + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text("Update Password") + + Spacer(modifier = Modifier.padding(16.dp)) + + OutlinedTextField( + value = state.domain, + onValueChange = { + viewmodel.onAction(action = UpdatePasswordAction.OnChangeDomain(it)) + }, + label = { + Text("Domain:") + } + ) + + OutlinedTextField( + value = state.username, + onValueChange = { + viewmodel.onAction(action = UpdatePasswordAction.OnChangeUsername(it)) + }, + label = { + Text("Username:") + } + ) + + OutlinedTextField( + value = state.password, + onValueChange = { + viewmodel.onAction(action = UpdatePasswordAction.OnChangePassword(it)) + }, + label = { + Text("Password:") + } + ) + + OutlinedTextField( + value = state.notes, + onValueChange = { + viewmodel.onAction(action = UpdatePasswordAction.OnChangeNotes(it)) + }, + label = { + Text("Notes:") + } + ) + + Spacer(modifier = Modifier.padding(8.dp)) + + Button(onClick = { + viewmodel.onAction(action = UpdatePasswordAction.OnUpdatePasswordButtonClick) + scope.launch { + snackbarHostState.showSnackbar("Update Successfully!!") + } + }) { + Text("Update Password") + } + } + } +} diff --git a/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/ViewPasswordScreen.kt b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/ViewPasswordScreen.kt new file mode 100644 index 00000000..eaa9cee8 --- /dev/null +++ b/password_manager/src/main/kotlin/com/jeeldobariya/passcodes/password_manager/ui/ViewPasswordScreen.kt @@ -0,0 +1,82 @@ +package com.jeeldobariya.passcodes.password_manager.ui + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Button +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.jeeldobariya.passcodes.core.navigation.Route +import com.jeeldobariya.passcodes.password_manager.presentation.view_password.ViewPasswordAction +import com.jeeldobariya.passcodes.password_manager.presentation.view_password.ViewPasswordViewModel +import org.koin.compose.viewmodel.koinViewModel + +@Composable +fun ClassicalViewPasswordScreen( + passwordId: Int, + navigateTo: (Route) -> Unit, + viewmodel: ViewPasswordViewModel = koinViewModel() +) { + viewmodel.onAction(ViewPasswordAction.LoadPassword(passwordId)) + + Scaffold { paddingValues -> + val state by viewmodel.state.collectAsState() + + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text("View Password") + + Spacer(modifier = Modifier.padding(16.dp)) + + Column( + modifier = Modifier.padding(4.dp), + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + Text("Domain: ${state.domain}", fontSize = 18.sp) + Text("Username: ${state.username}", fontSize = 18.sp) + Text("Password: ${state.password}", fontSize = 18.sp) + Text("Notes: ${state.notes}", fontSize = 18.sp) + Text("LastUpdateAt: ${state.lastUpdatedAt}", fontSize = 18.sp) + } + + Spacer(modifier = Modifier.padding(8.dp)) + + Button( + onClick = { + navigateTo(Route.UpdatePassword(passwordId)) + } + ) { + Text("Update Password") + } + + Button( + onClick = { + viewmodel.onAction(ViewPasswordAction.DeletePasswordAction) + /* TODO: Navigate Back */ + } + ) { + Text("Delete Password") + } + + Button(onClick = { + /* TODO: Navigate Back */ + }) { + Text(" Navigate Back ") + } + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index b948f801..0ac5b0cf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,6 +12,9 @@ pluginManagement { } } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} // Defines all modules in your project include(":app")