diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/about/AboutScreen.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/about/AboutScreen.kt new file mode 100644 index 00000000..a4a99471 --- /dev/null +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/about/AboutScreen.kt @@ -0,0 +1,153 @@ +package com.yogeshpaliyal.keypass.ui.about + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +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.text.style.TextDecoration +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.yogeshpaliyal.common.utils.openLink +import com.yogeshpaliyal.keypass.BuildConfig +import com.yogeshpaliyal.keypass.R +import com.yogeshpaliyal.keypass.ui.commonComponents.DefaultBottomAppBar +import com.yogeshpaliyal.keypass.ui.commonComponents.PreferenceItem + +@Composable +fun AboutScreen() { + Scaffold(bottomBar = { DefaultBottomAppBar() }) { contentPadding -> + Surface( + modifier = Modifier + .padding(contentPadding) + .fillMaxWidth() + ) { + MainContent() + } + } +} + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) +@Composable +fun MainContentDarkPreview() { + MainContent() +} + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) +@Composable +fun MainContentLightPreview() { + MainContent() +} + +@Composable +private fun MainContent() { + val context = LocalContext.current + + Column( + modifier = Modifier + .fillMaxSize(1f) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier.border( + 1.dp, + MaterialTheme.colorScheme.onBackground, + shape = androidx.compose.foundation.shape.AbsoluteRoundedCornerShape(8.dp) + ) + ) { + Image( + painter = painterResource(id = R.mipmap.ic_launcher_foreground), + contentDescription = "App Icon" + ) + } + + Spacer(modifier = Modifier.size(16.dp)) + + Text( + stringResource(id = R.string.app_name), + style = MaterialTheme.typography.headlineSmall + ) + + Spacer(modifier = Modifier.size(8.dp)) + + Text(BuildConfig.VERSION_NAME) + + HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp)) + + Text( + stringResource(id = R.string.app_desc), + style = MaterialTheme.typography.bodyMedium + ) + + Text( + text = stringResource(R.string.source_code), + modifier = Modifier + .fillMaxWidth() + .clickable { + context.openLink("https://github.com/yogeshpaliyal/KeyPass") + }, + fontStyle = androidx.compose.ui.text.font.FontStyle.Italic, + style = MaterialTheme.typography.labelMedium, + textDecoration = TextDecoration.Underline, + textAlign = TextAlign.Center + ) + + HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp)) + } + + Text( + stringResource(id = R.string.app_developer), + style = MaterialTheme.typography.headlineSmall + ) + + Spacer(modifier = Modifier.size(16.dp)) + + Column(modifier = Modifier.fillMaxWidth()) { + PreferenceItem( + painter = painterResource(id = R.drawable.ic_twitter), + title = R.string.app_developer_x + ) { + context.openLink("https://twitter.com/yogeshpaliyal") + } + } + + PreferenceItem( + painter = painterResource(id = R.drawable.ic_github), + title = R.string.app_developer_github + ) { + context.openLink("https://github.com/yogeshpaliyal") + } + + PreferenceItem( + painter = painterResource(id = R.drawable.ic_linkedin), + title = R.string.app_developer_linkedin + ) { + context.openLink("https://linkedin.com/in/yogeshpaliyal") + } + } +} diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt index 43f898e8..24acbaec 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/nav/DashboardComposeActivity.kt @@ -24,6 +24,7 @@ import com.yogeshpaliyal.common.utils.getUserSettingsFlow import com.yogeshpaliyal.common.utils.migrateOldDataToNewerDataStore import com.yogeshpaliyal.common.utils.setUserSettings import com.yogeshpaliyal.keypass.BuildConfig +import com.yogeshpaliyal.keypass.ui.about.AboutScreen import com.yogeshpaliyal.keypass.ui.auth.AuthScreen import com.yogeshpaliyal.keypass.ui.backup.BackupScreen import com.yogeshpaliyal.keypass.ui.backupsImport.BackupImporter @@ -36,6 +37,7 @@ import com.yogeshpaliyal.keypass.ui.nav.components.KeyPassBottomBar import com.yogeshpaliyal.keypass.ui.redux.KeyPassRedux import com.yogeshpaliyal.keypass.ui.redux.actions.GoBackAction import com.yogeshpaliyal.keypass.ui.redux.actions.UpdateContextAction +import com.yogeshpaliyal.keypass.ui.redux.states.AboutState import com.yogeshpaliyal.keypass.ui.redux.states.AccountDetailState import com.yogeshpaliyal.keypass.ui.redux.states.AuthState import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState @@ -166,6 +168,7 @@ fun CurrentPage() { } is BackupImporterState -> BackupImporter(state = it) + is AboutState -> AboutScreen() } } } diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/AboutState.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/AboutState.kt new file mode 100644 index 00000000..ea06af43 --- /dev/null +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/redux/states/AboutState.kt @@ -0,0 +1,3 @@ +package com.yogeshpaliyal.keypass.ui.redux.states + +class AboutState : ScreenState() diff --git a/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt b/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt index 739ef348..aefe76ea 100644 --- a/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt +++ b/app/src/main/java/com/yogeshpaliyal/keypass/ui/settings/MySettingsFragment.kt @@ -6,15 +6,24 @@ import android.provider.Settings import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +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.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.rounded.Feedback import androidx.compose.material.icons.rounded.Fingerprint import androidx.compose.material.icons.rounded.Password import androidx.compose.material.icons.rounded.Share import androidx.compose.material3.Divider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -22,13 +31,14 @@ 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.platform.LocalContext import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.yogeshpaliyal.common.utils.email import com.yogeshpaliyal.common.utils.setBiometricEnable +import com.yogeshpaliyal.keypass.BuildConfig import com.yogeshpaliyal.keypass.R import com.yogeshpaliyal.keypass.ui.commonComponents.PreferenceItem import com.yogeshpaliyal.keypass.ui.generate.ui.components.DEFAULT_PASSWORD_LENGTH @@ -37,6 +47,7 @@ import com.yogeshpaliyal.keypass.ui.redux.actions.Action import com.yogeshpaliyal.keypass.ui.redux.actions.IntentNavigation import com.yogeshpaliyal.keypass.ui.redux.actions.NavigationAction import com.yogeshpaliyal.keypass.ui.redux.actions.ToastAction +import com.yogeshpaliyal.keypass.ui.redux.states.AboutState import com.yogeshpaliyal.keypass.ui.redux.states.BackupImporterState import com.yogeshpaliyal.keypass.ui.redux.states.BackupScreenState import com.yogeshpaliyal.keypass.ui.redux.states.ChangeAppPasswordState @@ -44,7 +55,6 @@ import com.yogeshpaliyal.keypass.ui.redux.states.ChangeDefaultPasswordLengthStat import kotlinx.coroutines.launch import org.reduxkotlin.compose.rememberTypedDispatcher -@Preview(showSystemUi = true) @Composable fun MySettingCompose() { val dispatchAction = rememberTypedDispatcher() @@ -57,7 +67,7 @@ fun MySettingCompose() { userSettings.defaultPasswordLength.let { value -> savedPasswordLength = value } } - Column { + Column(modifier = Modifier.fillMaxSize(1f).verticalScroll(rememberScrollState())) { PreferenceItem(title = R.string.security, isCategory = true) PreferenceItem( painter = painterResource(id = R.drawable.credentials_backup), @@ -113,6 +123,27 @@ fun MySettingCompose() { ) { dispatchAction(IntentNavigation.ShareApp) } + + PreferenceItem( + title = R.string.about_us, + summary = R.string.about_us, + icon = Icons.Outlined.Info + ) { + dispatchAction(NavigationAction(AboutState())) + } + + Row( + modifier = Modifier + .fillMaxWidth(1f) + .padding(16.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "App Version ${BuildConfig.VERSION_NAME}", + style = MaterialTheme.typography.labelSmall + ) + } } } diff --git a/app/src/main/res/drawable/ic_code.xml b/app/src/main/res/drawable/ic_code.xml new file mode 100644 index 00000000..f180c000 --- /dev/null +++ b/app/src/main/res/drawable/ic_code.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_github.xml b/app/src/main/res/drawable/ic_github.xml new file mode 100644 index 00000000..2fb73604 --- /dev/null +++ b/app/src/main/res/drawable/ic_github.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_linkedin.xml b/app/src/main/res/drawable/ic_linkedin.xml new file mode 100644 index 00000000..b7314ed4 --- /dev/null +++ b/app/src/main/res/drawable/ic_linkedin.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_twitter.xml b/app/src/main/res/drawable/ic_twitter.xml new file mode 100644 index 00000000..4dd43989 --- /dev/null +++ b/app/src/main/res/drawable/ic_twitter.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 300f6fc1..db1a9555 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ Generate Password + Open-source & offline password manager. Store, manage, take control securely. Home Delete @@ -21,7 +22,14 @@ Send feedback Report technical issues or suggest new features Share + About Us Share app with others + About us Desc + Developer + X (previously Twitter) + LinkedIn + Source Code + Github Password Copied to clipboard Login to enter KeyPass diff --git a/app/src/pro/res/values/strings.xml b/app/src/pro/res/values/strings.xml new file mode 100644 index 00000000..9878c151 --- /dev/null +++ b/app/src/pro/res/values/strings.xml @@ -0,0 +1,4 @@ + + + KeyPass Pro + \ No newline at end of file diff --git a/common/src/main/java/com/yogeshpaliyal/common/utils/IntentHelper.kt b/common/src/main/java/com/yogeshpaliyal/common/utils/IntentHelper.kt index 0fd1831a..52082a48 100644 --- a/common/src/main/java/com/yogeshpaliyal/common/utils/IntentHelper.kt +++ b/common/src/main/java/com/yogeshpaliyal/common/utils/IntentHelper.kt @@ -61,6 +61,17 @@ fun Context.sendSMS(chooserTitle: String, number: String, text: String = ""): Bo } } +fun Context.openLink(link: String): Boolean { + try { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse("$link?utm_source=KeyPass&utm_medium=app&utm_campaign=KeyPass")) + startActivity(intent) + return true + } catch (e: Exception) { + e.printStackTrace() + return false + } +} + fun Context.share(chooserTitle: String, text: String): Boolean { try { val intent = Intent(Intent.ACTION_SEND)