Redux improvements (#463)

* Redux improvements

* redux improvements

* Drop files from .gitignore

* removed unused files
This commit is contained in:
Yogesh Choudhary Paliyal
2023-04-23 15:06:11 +05:30
committed by GitHub
parent 6d4b0f4604
commit a7083d4faf
39 changed files with 270 additions and 1048 deletions

View File

@@ -1,83 +0,0 @@
<resources>
<string name="generate_password">Şifre oluştur</string>
<string name="home">Ev</string>
<string name="delete">Sil</string>
<string name="generate_shortcut_short_label">Şifre oluştur</string>
<string name="backup_restored">Yedekleme Geri Yüklendi</string>
<string name="invalid_keyphrase">Geçersiz Anahtar </string>
<string name="backup_completed">Yedekleme tamamlandı</string>
<!-- Sync Preferences -->
<string name="credentials_backups">Kimlik Yedeklemeleri</string>
<string name="credentials_backups_desc">Kimlik Bilgilerini harici depolama birimine yedekleyin</string>
<string name="restore_credentials">Kimlik Bilgilerini Geri Yükle</string>
<string name="restore_credentials_desc">Yedeklemenizi geri yükleyin</string>
<string name="send_feedback">Geribildirim yolla</string>
<string name="send_feedback_desc">Teknik sorunları bildirin veya yeni özellikler önerin</string>
<string name="share">Paylaş</string>
<string name="share_desc">Uygulamayı başkalarıyla paylaşın</string>
<string name="password_length">Şifre uzunluğu</string>
<string name="password">Şifre</string>
<string name="uppercase_alphabets">BÜYÜK HARFLER</string>
<string name="lowercase_alphabets">küçük harfler</string>
<string name="symbols">Semboller</string>
<string name="numbers">Sayılar</string>
<string name="copied_to_clipboard">Panoya kopyalandı</string>
<string name="login_to_enter_keypass">KeyPass\'e girmek için oturum açın</string>
<string name="authentication_failed">Kimlik doğrulama başarısız oldu</string>
<string name="last_backup_date">Son yedekleme : %s</string>
<string name="coming_soon">Yakında gelecek</string>
<string name="delete_account_title">Emin misin?</string>
<string name="delete_account_msg">Bu girişi gerçekten silmek istiyor musunuz, geri yüklenemez</string>
<string name="cancel">İptal et</string>
<string name="feedback_to_keypass">KeyPass\'a geri bildirim</string>
<string name="share_keypass">KeyPass\'i Paylaş</string>
<string name="message_no_accounts">Hesap eklenmedi, lütfen aşağıdaki düğmeden ekleyin</string>
<string name="turn_off_backup">Yedeklemeleri Kapat</string>
<string name="verify_keyphrase">Yedek parolayı doğrulayın</string>
<string name="verify_keyphrase_message">Yedek parolanızı test edin ve eşleştiğini doğrulayın</string>
<string name="backup_folder">Yedekleme Klasörü</string>
<string name="backup">Yedekle</string>
<string name="turn_on_backup">Yedeklemeleri Aç</string>
<string name="backup_desc">Yedeklemeler bir parola ile şifrelenir ve cihazınızda depolanır</string>
<string name="yes">Evet</string>
<string name="create_backup">Yedek Oluştur</string>
<string name="alert">Uyarı</string>
<string name="copy_keypharse_msg">Bu Anahtar Sözcüğü Kopyala bu, bu yedeği kurtarmak için kullanılacaktır, bu KeyPass tarafından tekrar sağlanmayacaktır, kopyaladınız mı veya yazdınız mı?</string>
<string name="help">Yardım</string>
<string name="security">Güvenlik</string>
<string name="search">Ara</string>
<string name="account_name">Hesap adı</string>
<string name="username_email_phone">Kullanıcı adı/E-posta/Telefon</string>
<string name="tags_comma_separated_optional">Etiketler virgülle ayrılmış (isteğe bağlı)</string>
<string name="website_url_optional">Web sitesi url\'si (isteğe bağlı)</string>
<string name="notes_optional">Notlar (isteğe bağlı)</string>
<string name="auto_backup">Otomatik yedekleme</string>
<string name="disabled">Devre dışı </string>
<string name="enabled">Etkinleştirilmiş</string>
<string name="override_auto_backup_file">Otomatik Yedekleme Dosyasını Geçersiz Kılma</string>
<string name="scanner">Tarayıcı</string>
<string name="alert_black_secret_key">Lütfen gizli anahtarı girin</string>
<string name="alert_black_account_name">Lütfen hesap adını girin</string>
<string name="add_totp">TOTP ekle</string>
<string name="scan_password">Tarama Parolası</string>
<string name="save">Kaydetmek</string>
<string name="custom_keyphrase">Özel Anahtar Kelime</string>
<string name="generate_keyphrase">Anahtar Sözcük Oluştur</string>
<string name="alert_blank_keyphrase">Lütfen anahtar sözcük girin</string>
<string name="alert_invalid_keyphrase">Lütfen geçerli bir anahtar sözcük girin</string>
<string name="set_keyphrase">Anahtar Sözcüğü Ayarla</string>
<string name="set_keyphrase_info">Lütfen yedekleme dosyanız için bir anahtar kelime oluşturun, bu, yedekleme dosyasını geri yüklemek için kullanılacaktır. anahtar sözcük 16 karakterden oluşmalıdır</string>
<string name="enter_keyphrase">Anahtar Sözcük Girin</string>
<string name="restore">Eski haline getir</string>
<string name="keyphrase_restore_info">Lütfen yedeklediğinizde aldığınız anahtar kelimeyi girin</string>
<string name="custom_generated_keyphrase_info">Yedeklemeler için kendi anahtar kelimenizi mi oluşturmak istiyorsunuz yoksa yeni bir anahtar mı oluşturmak istiyorsunuz?</string>
</resources>

2
.gitignore vendored
View File

@@ -1,5 +1,7 @@
*.iml
.gradle
.app
.idea/
/local.properties
/.idea
/.idea/libraries

3
.idea/.gitignore generated vendored
View File

@@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml

View File

@@ -1,124 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="ENABLE_SECOND_REFORMAT" value="true" />
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/compiler.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

21
.idea/gradle.xml generated
View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-11" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/common" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="maven2" />
<option name="name" value="maven2" />
<option name="url" value="https://maven.localazy.com/repository/release/" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

43
.idea/misc.xml generated
View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="../../../../layout/custom_preview.xml" value="0.20833333333333334" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/drawable/ic_twotone_content_copy_24.xml" value="0.10989583333333333" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/drawable/ic_undraw_empty_street_sfxm.xml" value="0.21614583333333334" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/drawable/nav_divider_top.xml" value="0.21614583333333334" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/activity_add_totpactivity.xml" value="0.16485507246376813" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/activity_dashboard.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/activity_scanner.xml" value="0.10416666666666667" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/backup_activity.xml" value="0.1" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/fragment_bottom_nav_drawer.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/fragment_detail.xml" value="0.1403985507246377" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/fragment_home.xml" value="0.16304347826086957" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/item_accounts.xml" value="0.388259526261586" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/item_totp.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/layout_backup_keypharse.xml" value="0.15353260869565216" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/layout_no_accounts.xml" value="0.17074275362318841" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/nav_divider_item_layout.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/nav_email_folder_item_layout.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/layout/nav_menu_item_layout.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/menu/bottom_app_bar_detail.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/menu/menu_delete.xml" value="0.2" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/main/res/xml/backup_preferences.xml" value="0.4287690179806362" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/production/res/mipmap-anydpi-v26/ic_launcher.xml" value="0.21614583333333334" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/app/src/staging/res/drawable/ic_twotone_content_copy_24.xml" value="0.21614583333333334" />
<entry key="..\:/Users/paliy/StudioProjects/KeyPass/common/src/main/res/layout/activity_main.xml" value="0.1" />
<entry key="app/src/main/res/layout/activity_dashboard.xml" value="0.19322916666666667" />
<entry key="app/src/main/res/layout/fragment_home.xml" value="0.19322916666666667" />
<entry key="app/src/main/res/layout/item_accounts.xml" value="0.19322916666666667" />
<entry key="app/src/main/res/layout/layout_no_accounts.xml" value="0.19322916666666667" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -153,6 +153,7 @@ dependencies {
implementation("androidx.hilt:hilt-work:1.0.0")
// When using Kotlin.
kapt("androidx.hilt:hilt-compiler:1.0.0")
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
// zxing library

View File

@@ -52,10 +52,6 @@
android:name=".ui.nav.DashboardComposeActivity"
android:exported="false"
android:windowSoftInputMode="adjustPan" />
<activity
android:name=".ui.detail.DetailActivity"
android:exported="false"
android:windowSoftInputMode="adjustResize" />
<!-- If you want to disable android.startup completely. -->
<provider

View File

@@ -1,14 +1,7 @@
package com.yogeshpaliyal.keypass.ui.detail
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -43,19 +36,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.common.utils.PasswordGenerator
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.style.KeyPassTheme
import dagger.hilt.android.AndroidEntryPoint
import com.yogeshpaliyal.keypass.ui.redux.GoBackAction
import org.reduxkotlin.compose.rememberDispatcher
/*
* @author Yogesh Paliyal
@@ -63,43 +55,14 @@ import dagger.hilt.android.AndroidEntryPoint
* https://techpaliyal.com
* created on 31-01-2021 10:38
*/
@AndroidEntryPoint
class DetailActivity : AppCompatActivity() {
companion object {
private const val ARG_ACCOUNT_ID = "ARG_ACCOUNT_ID"
@JvmStatic
fun start(context: Context?, accountId: Long? = null) {
val starter = Intent(context, DetailActivity::class.java)
.putExtra(ARG_ACCOUNT_ID, accountId)
context?.startActivity(starter)
}
}
private val mViewModel by viewModels<DetailViewModel>()
private val accountId by lazy {
intent?.extras?.getLong(ARG_ACCOUNT_ID) ?: -1
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Detail(accountId, mViewModel)
}
mViewModel.loadAccount(accountId)
}
}
@Preview()
@Composable
fun Detail(
fun AccountDetailPage(
id: Long?,
viewModel: DetailViewModel = androidx.lifecycle.viewmodel.compose.viewModel()
viewModel: DetailViewModel = hiltViewModel()
) {
val dispatchAction = rememberDispatcher()
// task value state
val (accountModel, setAccountModel) = remember {
mutableStateOf(
@@ -108,15 +71,14 @@ fun Detail(
}
// Set initial object
LaunchedEffect(key1 = viewModel.accountModel) {
viewModel.accountModel.value?.apply {
setAccountModel(this.copy())
LaunchedEffect(key1 = id) {
viewModel.loadAccount(id) {
setAccountModel(it.copy())
}
}
val activity = (LocalContext.current as? Activity)
val goBack: () -> Unit = {
activity?.onBackPressed()
dispatchAction(GoBackAction)
}
val launcher = rememberLauncherForActivityResult(QRScanner()) {
@@ -125,29 +87,27 @@ fun Detail(
}
}
KeyPassTheme {
Scaffold(
bottomBar = {
BottomBar(
accountModel,
backPressed = goBack,
onDeleteAccount = {
viewModel.deleteAccount(accountModel, goBack)
}
) {
viewModel.insertOrUpdate(accountModel, goBack)
Scaffold(
bottomBar = {
BottomBar(
accountModel,
backPressed = goBack,
onDeleteAccount = {
viewModel.deleteAccount(accountModel, goBack)
}
) {
viewModel.insertOrUpdate(accountModel, goBack)
}
) { paddingValues ->
Surface(modifier = Modifier.padding(paddingValues)) {
Fields(
accountModel = accountModel,
updateAccountModel = { newAccountModel ->
setAccountModel(newAccountModel)
}
) {
launcher.launch(null)
}
) { paddingValues ->
Surface(modifier = Modifier.padding(paddingValues)) {
Fields(
accountModel = accountModel,
updateAccountModel = { newAccountModel ->
setAccountModel(newAccountModel)
}
) {
launcher.launch(null)
}
}
}

View File

@@ -6,7 +6,6 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.common.worker.executeAutoBackup
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -21,18 +20,16 @@ import javax.inject.Inject
*/
@HiltViewModel
class DetailViewModel @Inject constructor(
val app: Application,
app: Application,
val appDb: com.yogeshpaliyal.common.AppDatabase
) : AndroidViewModel(app) {
private val _accountModel by lazy { MutableLiveData<AccountModel>() }
val accountModel: LiveData<AccountModel> = _accountModel
fun loadAccount(accountId: Long?) {
fun loadAccount(accountId: Long?, getAccount: (AccountModel) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
_accountModel.postValue(
appDb.getDao().getAccount(accountId) ?: AccountModel()
)
getAccount(appDb.getDao().getAccount(accountId) ?: AccountModel())
}
}
@@ -62,7 +59,7 @@ class DetailViewModel @Inject constructor(
private fun autoBackup() {
viewModelScope.launch {
app.executeAutoBackup()
// application.executeAutoBackup()
}
}
}

View File

@@ -45,8 +45,10 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.yogeshpaliyal.common.constants.AccountType
import com.yogeshpaliyal.common.data.AccountModel
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.AccountDetailState
import com.yogeshpaliyal.keypass.ui.redux.CopyToClipboard
import com.yogeshpaliyal.keypass.ui.redux.ScreeNavigationAction
import com.yogeshpaliyal.keypass.ui.redux.IntentNavigation
import com.yogeshpaliyal.keypass.ui.redux.NavigationAction
import com.yogeshpaliyal.keypass.ui.style.KeyPassTheme
import kotlinx.coroutines.delay
import org.reduxkotlin.compose.rememberDispatcher
@@ -112,9 +114,9 @@ fun AccountsList(accounts: List<AccountModel>? = null) {
account,
onClick = {
if (it.type == AccountType.TOTP) {
dispatch(ScreeNavigationAction.AddTOTP(it.uniqueId))
dispatch(IntentNavigation.AddTOTP(it.uniqueId))
} else {
dispatch(ScreeNavigationAction.AddAccount(it.id))
dispatch(NavigationAction(AccountDetailState(it.id)))
}
}
)

View File

@@ -2,8 +2,8 @@ package com.yogeshpaliyal.keypass.ui.nav
import android.os.Bundle
import android.view.WindowManager
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -46,24 +46,21 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.navigation.NavController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.yogeshpaliyal.keypass.databinding.LayoutMySettingsFragmentBinding
import com.yogeshpaliyal.keypass.ui.detail.DetailActivity
import com.yogeshpaliyal.keypass.ui.home.DashboardViewModel
import com.yogeshpaliyal.keypass.ui.detail.AccountDetailPage
import com.yogeshpaliyal.keypass.ui.home.Homepage
import com.yogeshpaliyal.keypass.ui.redux.Action
import com.yogeshpaliyal.keypass.ui.redux.AccountDetailState
import com.yogeshpaliyal.keypass.ui.redux.BottomSheetAction
import com.yogeshpaliyal.keypass.ui.redux.BottomSheetState
import com.yogeshpaliyal.keypass.ui.redux.GoBackAction
import com.yogeshpaliyal.keypass.ui.redux.HomeState
import com.yogeshpaliyal.keypass.ui.redux.KeyPassRedux
import com.yogeshpaliyal.keypass.ui.redux.KeyPassState
import com.yogeshpaliyal.keypass.ui.redux.ScreeNavigationAction
import com.yogeshpaliyal.keypass.ui.redux.ScreenRoutes
import com.yogeshpaliyal.keypass.ui.redux.NavigationAction
import com.yogeshpaliyal.keypass.ui.redux.ScreenState
import com.yogeshpaliyal.keypass.ui.redux.SettingsState
import com.yogeshpaliyal.keypass.ui.redux.TotpDetailState
import com.yogeshpaliyal.keypass.ui.redux.UpdateContextAction
import com.yogeshpaliyal.keypass.ui.redux.UpdateNavControllerAction
import com.yogeshpaliyal.keypass.ui.style.KeyPassTheme
import dagger.hilt.android.AndroidEntryPoint
import org.reduxkotlin.compose.StoreProvider
@@ -74,8 +71,6 @@ import java.util.Locale
@AndroidEntryPoint
class DashboardComposeActivity : AppCompatActivity() {
private val mViewModel by viewModels<DashboardViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.setFlags(
@@ -84,8 +79,8 @@ class DashboardComposeActivity : AppCompatActivity() {
)
setContent {
KeyPassTheme {
StoreProvider(store = KeyPassRedux.store) {
Dashboard(viewModel = mViewModel)
StoreProvider(store = KeyPassRedux.createStore()) {
Dashboard()
}
}
}
@@ -93,59 +88,59 @@ class DashboardComposeActivity : AppCompatActivity() {
}
@Composable
fun Dashboard(viewModel: DashboardViewModel = androidx.lifecycle.viewmodel.compose.viewModel()) {
val appState by selectState<KeyPassState, KeyPassState> { this }
fun Dashboard() {
val systemBackPress by selectState<KeyPassState, Boolean> { this.systemBackPress }
val navController = rememberNavController()
val context = LocalContext.current
val dispatch = rememberDispatcher()
DisposableEffect(KeyPassRedux, navController, context) {
BackHandler(!systemBackPress) {
dispatch(GoBackAction)
}
if (systemBackPress) {
(context as? AppCompatActivity)?.onBackPressed()
}
DisposableEffect(KeyPassRedux, context) {
dispatch(UpdateContextAction(context))
dispatch(UpdateNavControllerAction(navController))
onDispose {
dispatch(UpdateContextAction(null))
dispatch(UpdateNavControllerAction(null))
}
}
Scaffold(bottomBar = {
KeyPassBottomBar(navController) {
dispatch(it)
}
KeyPassBottomBar()
}) { paddingValues ->
Surface(modifier = Modifier.padding(paddingValues)) {
NavHost(
navController = navController,
startDestination = ScreenRoutes.HOME
) {
composable(
ScreenRoutes.HOME,
arguments = listOf(
navArgument("tag") {
type = NavType.StringType
nullable = true
defaultValue = ""
}
)
) { backStackEntry ->
val type = backStackEntry.arguments?.getString("tag")
Homepage(viewModel, type)
}
composable(ScreenRoutes.SETTINGS) {
MySettings()
}
}
CurrentPage()
if (appState.bottomSheet.isBottomSheetOpen) {
OptionBottomBar({
dispatch(it)
if (it !is BottomSheetAction) {
dispatch(BottomSheetAction.HomeNavigationMenu(false))
}
})
}
OptionBottomBar()
}
}
}
@Composable
fun CurrentPage() {
val currentScreen by selectState<KeyPassState, ScreenState> { this.currentScreen }
val dispatch = rememberDispatcher()
when (currentScreen) {
is HomeState -> {
Homepage(selectedTag = (currentScreen as HomeState).type)
}
is SettingsState -> {
MySettings()
}
is AccountDetailState -> {
AccountDetailPage(id = (currentScreen as AccountDetailState).accountId)
}
is TotpDetailState -> {
}
}
}
@@ -158,9 +153,16 @@ fun MySettings() {
@Composable
fun OptionBottomBar(
dispatchAction: (Action) -> Unit,
viewModel: BottomNavViewModel = androidx.lifecycle.viewmodel.compose.viewModel()
) {
val bottomSheetState by selectState<KeyPassState, BottomSheetState> { this.bottomSheet }
if (!bottomSheetState.isBottomSheetOpen) {
return
}
val dispatchAction = rememberDispatcher()
val navigationItems by viewModel.navigationList.observeAsState()
ModalBottomSheet(onDismissRequest = {
dispatchAction(BottomSheetAction.HomeNavigationMenu(false))
@@ -179,13 +181,15 @@ fun OptionBottomBar(
is NavigationModelItem.NavTagItem -> {
NavMenuFolder(folder = it) {
dispatchAction(ScreeNavigationAction.Home(it.tag))
dispatchAction(NavigationAction(HomeState(it.tag)))
dispatchAction(BottomSheetAction.HomeNavigationMenu(false))
}
}
is NavigationModelItem.NavMenuItem -> {
NavItem(it) {
dispatchAction(it.action)
dispatchAction(BottomSheetAction.HomeNavigationMenu(false))
}
}
}
@@ -251,12 +255,19 @@ fun NavItemSection(divider: NavigationModelItem.NavDivider) {
}
@Composable
fun KeyPassBottomBar(navController: NavController, onActionItemClick: (Action) -> Unit) {
fun KeyPassBottomBar() {
val showMainBottomAppBar by selectState<KeyPassState, Boolean> { this.currentScreen.showMainBottomAppBar }
val dispatchAction = rememberDispatcher()
if (!showMainBottomAppBar) {
return
}
val context = LocalContext.current
BottomAppBar(actions = {
IconButton(onClick = {
onActionItemClick(BottomSheetAction.HomeNavigationMenu(true))
dispatchAction(BottomSheetAction.HomeNavigationMenu(true))
}) {
Icon(
painter = rememberVectorPainter(image = Icons.Rounded.Menu),
@@ -266,7 +277,7 @@ fun KeyPassBottomBar(navController: NavController, onActionItemClick: (Action) -
}
IconButton(onClick = {
onActionItemClick(ScreeNavigationAction.Settings())
dispatchAction(NavigationAction(SettingsState))
}) {
Icon(
painter = rememberVectorPainter(image = Icons.Rounded.Settings),
@@ -276,7 +287,7 @@ fun KeyPassBottomBar(navController: NavController, onActionItemClick: (Action) -
}
}, floatingActionButton = {
FloatingActionButton(onClick = {
DetailActivity.start(context)
dispatchAction(NavigationAction(AccountDetailState()))
}) {
Icon(
painter = rememberVectorPainter(image = Icons.Rounded.Add),

View File

@@ -1,11 +1,10 @@
package com.yogeshpaliyal.keypass.ui.nav
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.redux.ScreeNavigationAction
import com.yogeshpaliyal.keypass.ui.redux.HomeState
import com.yogeshpaliyal.keypass.ui.redux.IntentNavigation
import com.yogeshpaliyal.keypass.ui.redux.NavigationAction
/**
* A class which maintains and generates a navigation list to be displayed by [NavigationAdapter].
*/
object NavigationModel {
const val HOME = 0
@@ -18,21 +17,21 @@ object NavigationModel {
icon = R.drawable.ic_twotone_home_24,
titleRes = R.string.home,
checked = false,
action = ScreeNavigationAction.Home()
action = NavigationAction(HomeState())
),
NavigationModelItem.NavMenuItem(
id = GENERATE_PASSWORD,
icon = R.drawable.ic_twotone_vpn_key_24,
titleRes = R.string.generate_password,
checked = false,
action = ScreeNavigationAction.GeneratePassword
action = IntentNavigation.GeneratePassword
),
NavigationModelItem.NavMenuItem(
id = ADD_TOPT,
icon = R.drawable.ic_twotone_totp,
titleRes = R.string.add_totp,
checked = false,
action = ScreeNavigationAction.AddTOTP()
action = IntentNavigation.AddTOTP()
)
)
}

View File

@@ -3,26 +3,19 @@ package com.yogeshpaliyal.keypass.ui.redux
import android.content.Context
import android.os.Bundle
import androidx.annotation.StringRes
import androidx.navigation.NavController
sealed interface Action
class UpdateContextAction(val context: Context?) : Action
class UpdateNavControllerAction(val navController: NavController?) : Action
sealed class ScreeNavigationAction(val route: String, val globalArgs: Bundle? = null) : Action {
data class Home(val tag: String? = null) : ScreeNavigationAction(ScreenRoutes.HOME.replace("{tag}", tag ?: ""))
data class Settings(val args: Bundle? = null) :
ScreeNavigationAction(ScreenRoutes.SETTINGS, args)
object GoBackAction : Action
data class AddAccount(val accountId: Long? = null) :
ScreeNavigationAction(ScreenRoutes.ADD_ACCOUNT)
data class NavigationAction(val state: ScreenState) : Action
data class StateUpdateAction(val state: ScreenState) : Action
data class AddTOTP(val accountId: String? = null) :
ScreeNavigationAction(ScreenRoutes.ADD_TOTP)
object GeneratePassword :
ScreeNavigationAction(ScreenRoutes.GENERATE_PASSWORD)
sealed interface IntentNavigation : Action {
object GeneratePassword : IntentNavigation
data class AddTOTP(val accountId: String? = null) : IntentNavigation
}
data class ToastAction(@StringRes val text: Int) : Action

View File

@@ -5,11 +5,8 @@ import android.content.ClipboardManager
import android.content.Intent
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.navigation.NavController
import androidx.navigation.NavOptionsBuilder
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.ui.addTOTP.AddTOTPActivity
import com.yogeshpaliyal.keypass.ui.detail.DetailActivity
import com.yogeshpaliyal.keypass.ui.generate.GeneratePasswordActivity
import org.reduxkotlin.Reducer
import org.reduxkotlin.applyMiddleware
@@ -18,23 +15,39 @@ import org.reduxkotlin.middleware
object KeyPassRedux {
private fun NavController.navigateIfNotInSameRoute(
route: String,
builder: (NavOptionsBuilder.() -> Unit)? = null
) {
if (this.currentDestination?.route != route) {
if (builder != null) {
this.navigate(route, builder)
} else {
this.navigate(route)
}
}
private val arrPages by lazy {
mutableListOf<ScreenState>()
}
private val reducer: Reducer<KeyPassState> = { state, action ->
when (action) {
is ScreeNavigationAction -> {
state.copy(currentScreen = ScreenState(action.route, action.globalArgs))
is NavigationAction -> {
// TODO handle backstack
arrPages.add(state.currentScreen)
state.copy(currentScreen = action.state)
}
is StateUpdateAction -> {
state.copy(currentScreen = action.state)
}
is CopyToClipboard -> {
state.context?.let {
val clipboard = ContextCompat.getSystemService(
it,
ClipboardManager::class.java
)
val clip = ClipData.newPlainText("KeyPass", action.password)
clipboard?.setPrimaryClip(clip)
Toast.makeText(
it,
R.string.copied_to_clipboard,
Toast.LENGTH_SHORT
)
.show()
}
state
}
is CopyToClipboard -> {
@@ -60,8 +73,13 @@ object KeyPassRedux {
state.copy(context = action.context)
}
is UpdateNavControllerAction -> {
state.copy(navController = action.navController)
is GoBackAction -> {
val lastItem = arrPages.removeLastOrNull()
if (lastItem != null) {
state.copy(currentScreen = lastItem)
} else {
state.copy(systemBackPress = true)
}
}
is BottomSheetAction -> {
@@ -78,35 +96,22 @@ object KeyPassRedux {
}
}
val navigationMiddleware = middleware<KeyPassState> { store, next, action ->
private val intentNavigationMiddleware = middleware<KeyPassState> { store, next, action ->
val state = store.state
when (action) {
is ScreeNavigationAction.GeneratePassword -> {
is IntentNavigation.GeneratePassword -> {
val intent = Intent(state.context, GeneratePasswordActivity::class.java)
state.context?.startActivity(intent)
}
is ScreeNavigationAction.AddTOTP -> {
is IntentNavigation.AddTOTP -> {
AddTOTPActivity.start(state.context, action.accountId)
}
is ScreeNavigationAction.AddAccount -> {
DetailActivity.start(state.context, action.accountId)
}
is ScreeNavigationAction.Home -> {
state.navController?.navigateIfNotInSameRoute(action.route) {
launchSingleTop = true
}
}
is ScreeNavigationAction.Settings -> {
state.navController?.navigateIfNotInSameRoute(action.route)
}
}
next(action)
}
val store = createStore(reducer, generateDefaultState(), applyMiddleware(navigationMiddleware))
fun createStore() =
createStore(reducer, generateDefaultState(), applyMiddleware(intentNavigationMiddleware))
}

View File

@@ -1,30 +1,13 @@
package com.yogeshpaliyal.keypass.ui.redux
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
import androidx.navigation.navArgument
object ScreenRoutes {
const val HOME = "home?tag={tag}"
const val SETTINGS = "/settings"
const val ADD_ACCOUNT = "/addAccount"
const val ADD_ACCOUNT = "addAccount?accountId={accountId}"
const val GENERATE_PASSWORD = "/generatePassword"
const val ADD_TOTP = "/addTOTP"
}
object Home {
fun getArguments(): List<NamedNavArgument> {
return listOf(
navArgument("tag") {
type = NavType.StringType
},
navArgument("query") {
type = NavType.StringType
}
)
}
}
object BottomSheetRoutes {
const val HOME_NAV_MENU = "/home/navMenu"
}

View File

@@ -2,19 +2,20 @@ package com.yogeshpaliyal.keypass.ui.redux
import android.content.Context
import android.os.Bundle
import androidx.navigation.NavController
data class KeyPassState(
val context: Context? = null,
val navController: NavController? = null,
val currentScreen: ScreenState,
val bottomSheet: BottomSheetState
val bottomSheet: BottomSheetState,
val systemBackPress: Boolean = false
)
data class ScreenState(
val path: String,
val args: Bundle? = null
)
sealed class ScreenState(val showMainBottomAppBar: Boolean = false)
data class HomeState(val type: String? = null) : ScreenState(true)
data class AccountDetailState(val accountId: Long? = null) : ScreenState()
data class TotpDetailState(val accountId: String? = null) : ScreenState()
object SettingsState : ScreenState(true)
data class BottomSheetState(
val path: String,
@@ -23,7 +24,7 @@ data class BottomSheetState(
)
fun generateDefaultState(): KeyPassState {
val screenState = ScreenState(ScreenRoutes.HOME)
val currentPage = HomeState()
val bottomSheet = BottomSheetState(BottomSheetRoutes.HOME_NAV_MENU, isBottomSheetOpen = false)
return KeyPassState(currentScreen = screenState, bottomSheet = bottomSheet)
return KeyPassState(currentScreen = currentPage, bottomSheet = bottomSheet)
}

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_checked="true"/>
<item android:color="?attr/colorOnSurface" />
</selector>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2019 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
-->
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/add"
android:drawable="@drawable/ic_round_add_24"
android:state_activated="false"/>
<item
android:id="@+id/save"
android:drawable="@drawable/ic_round_done_24"
android:state_activated="true"/>
<transition
android:fromId="@id/add"
android:toId="@id/save"
android:drawable="@drawable/avd_add_to_save"/>
<transition
android:fromId="@id/save"
android:toId="@id/add"
android:drawable="@drawable/avd_save_to_add"/>
</animated-selector>

View File

@@ -1,126 +0,0 @@
<!--
Copyright (c) 2019 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
-->
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group android:name="group">
<path
android:fillColor="#000"
android:name="path"
android:pathData="M18,13h-5v5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-5H6c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h5V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v5h5c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/>
</group>
<group android:name="group_1">
<path
android:name="path_1"
android:fillColor="#000"
android:pathData="M9,16.2l-3.5,-3.5c-0.39,-0.39 -1.01,-0.39 -1.4,0 -0.39,0.39 -0.39,1.01 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7c0.39,-0.39 0.39,-1.01 0,-1.4 -0.39,-0.39 -1.01,-0.39 -1.4,0L9,16.2z"/>
</group>
</vector>
</aapt:attr>
<target android:name="group">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="scaleX"
android:duration="100"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="scaleY"
android:duration="100"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="translateY"
android:duration="300"
android:valueFrom="-0.8"
android:valueTo="-0.8"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="rotation"
android:duration="100"
android:valueFrom="0"
android:valueTo="30"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotX"
android:duration="100"
android:valueFrom="22"
android:valueTo="22"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotY"
android:duration="100"
android:valueFrom="16"
android:valueTo="16"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</set>
</aapt:attr>
</target>
<target android:name="group_1">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="scaleX"
android:startOffset="100"
android:duration="200"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="scaleY"
android:startOffset="100"
android:duration="200"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotX"
android:startOffset="100"
android:duration="200"
android:valueFrom="12"
android:valueTo="12"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotY"
android:startOffset="100"
android:duration="200"
android:valueFrom="12"
android:valueTo="12"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</set>
</aapt:attr>
</target>
</animated-vector>

View File

@@ -1,127 +0,0 @@
<!--
Copyright (c) 2019 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
-->
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
<aapt:attr name="android:drawable">
<vector
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group android:name="group">
<path
android:name="path"
android:fillColor="#000"
android:pathData="M9,16.2l-3.5,-3.5c-0.39,-0.39 -1.01,-0.39 -1.4,0 -0.39,0.39 -0.39,1.01 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7c0.39,-0.39 0.39,-1.01 0,-1.4 -0.39,-0.39 -1.01,-0.39 -1.4,0L9,16.2z"/>
</group>
<group android:name="group_1">
<path
android:name="path_1"
android:fillColor="#000"
android:pathData="M18,13h-5v5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-5H6c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h5V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v5h5c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/>
</group>
</vector>
</aapt:attr>
<target android:name="group">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="scaleX"
android:duration="100"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="scaleY"
android:duration="100"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="translateY"
android:duration="300"
android:valueFrom="-0.8"
android:valueTo="-0.8"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="rotation"
android:duration="100"
android:valueFrom="0"
android:valueTo="30"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotX"
android:duration="100"
android:valueFrom="22"
android:valueTo="22"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotY"
android:duration="100"
android:valueFrom="16"
android:valueTo="16"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</set>
</aapt:attr>
</target>
<target android:name="group_1">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:propertyName="scaleX"
android:startOffset="100"
android:duration="200"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="scaleY"
android:startOffset="100"
android:duration="200"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotX"
android:startOffset="100"
android:duration="200"
android:valueFrom="12"
android:valueTo="12"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="pivotY"
android:startOffset="100"
android:duration="200"
android:valueFrom="12"
android:valueTo="12"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</set>
</aapt:attr>
</target>
</animated-vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M18,13h-5v5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-5H6c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1h5V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v5h5c0.55,0 1,0.45 1,1s-0.45,1 -1,1z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M4,18h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,16c-0.55,0 -1,0.45 -1,1s0.45,1 1,1zM4,13h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,11c-0.55,0 -1,0.45 -1,1s0.45,1 1,1zM3,7c0,0.55 0.45,1 1,1h16c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1L4,6c-0.55,0 -1,0.45 -1,1z"/>
</vector>

View File

@@ -1,15 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M8,7h11v14H8z"
android:strokeAlpha="0.3"
android:fillAlpha="0.3"/>
<path
android:fillColor="@android:color/white"
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
</vector>

View File

@@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M11.17,8l-0.58,-0.59L9.17,6H4v12h16V8h-8z"
android:fillAlpha="0.3"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,6h5.17l1.41,1.41 0.59,0.59L20,8v10z"/>
</vector>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/color_on_primary_surface_divider" />
<size android:height="1dp"
android:width="200dp"/>
</shape>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_settings"
android:title="Settings"/>
</menu>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_settings"
android:title="Settings"
app:showAsAction="always"
android:icon="@drawable/ic_baseline_settings_24"/>
</menu>

View File

@@ -16,11 +16,8 @@
<!--BottomAppBar-->
<dimen name="bottom_app_bar_height">56dp</dimen>
<dimen name="bottom_app_bar_fab_cradle_corner_radius">32dp</dimen>
<dimen name="bottom_app_bar_fab_cradle_margin">8dp</dimen>
<!--BottomNavigationDrawer-->
<dimen name="navigation_drawer_menu_item_height">56dp</dimen>
<!--Email previews-->

View File

@@ -14,6 +14,4 @@
-->
<resources>
<dimen name="plane_00">0dp</dimen>
<dimen name="plane_16">16dp</dimen>
</resources>

View File

@@ -1,17 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="com_google_android_gms_fonts_certs">
<item>@array/com_google_android_gms_fonts_certs_dev</item>
<item>@array/com_google_android_gms_fonts_certs_prod</item>
</array>
<string-array name="com_google_android_gms_fonts_certs_dev">
<item>
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
</item>
</string-array>
<string-array name="com_google_android_gms_fonts_certs_prod">
<item>
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
</item>
</string-array>
</resources>
<resources></resources>

View File

@@ -18,8 +18,6 @@
<!--KeyPass uses an 8dp grid system. Smaller components can align to a 2dp 'sub' grid.-->
<!--2dp sub grid-->
<dimen name="grid_0_25">2dp</dimen>
<dimen name="grid_0_5">4dp</dimen>
<!--8dp grid-->
<dimen name="grid_1">8dp</dimen>

View File

@@ -3,6 +3,4 @@
<!--Motion-->
<integer name="keypass_motion_duration_large">300</integer>
</resources>

184
gradlew.bat vendored
View File

@@ -1,92 +1,92 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega