mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2026-01-01 09:20:31 -06:00
added Dipendency Injection (hilt) and custom password generate length
This commit is contained in:
16
.idea/codeStyles/Project.xml
generated
16
.idea/codeStyles/Project.xml
generated
@@ -1,22 +1,6 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="java.util" alias="false" withSubpackages="false" />
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
<package name="io.ktor" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||
<value>
|
||||
<package name="" alias="false" withSubpackages="true" />
|
||||
<package name="java" alias="false" withSubpackages="true" />
|
||||
<package name="javax" alias="false" withSubpackages="true" />
|
||||
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||
<package name="" alias="true" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
|
||||
2
.idea/compiler.xml
generated
2
.idea/compiler.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="1.8" />
|
||||
<bytecodeTargetLevel target="11" />
|
||||
</component>
|
||||
</project>
|
||||
5
.idea/gradle.xml
generated
5
.idea/gradle.xml
generated
@@ -4,10 +4,10 @@
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="PLATFORM" />
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="1.8 (2)" />
|
||||
<option name="gradleJvm" value="JDK" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
@@ -15,7 +15,6 @@
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
<option name="useQualifiedModuleNames" value="true" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
@@ -3,6 +3,7 @@ plugins {
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
id 'androidx.navigation.safeargs.kotlin'
|
||||
id("dagger.hilt.android.plugin")
|
||||
|
||||
}
|
||||
|
||||
@@ -74,17 +75,17 @@ android {
|
||||
dependencies {
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
implementation 'androidx.core:core-ktx:1.5.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
testImplementation 'junit:junit:4.+'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
|
||||
|
||||
implementation ('androidx.appcompat:appcompat:1.2.0')
|
||||
implementation ('androidx.appcompat:appcompat:1.3.0')
|
||||
implementation "androidx.documentfile:documentfile:1.0.1"
|
||||
|
||||
// ViewModel
|
||||
@@ -102,7 +103,7 @@ dependencies {
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
|
||||
implementation 'com.github.yogeshpaliyal:Android-Universal-Recycler-View-Adapter:1.0.2'
|
||||
implementation 'com.github.yogeshpaliyal:Android-Universal-Recycler-View-Adapter:2.0.0'
|
||||
|
||||
|
||||
implementation "androidx.biometric:biometric:1.1.0"
|
||||
@@ -116,4 +117,9 @@ dependencies {
|
||||
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
|
||||
|
||||
// dependency injection
|
||||
implementation("com.google.dagger:hilt-android:$hilt_version")
|
||||
kapt("com.google.dagger:hilt-android-compiler:$hilt_version")
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import com.yogeshpaliyal.keypass.utils.getRandomString
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* yogeshpaliyal.foss@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 30-01-2021 20:37
|
||||
*/
|
||||
@@ -25,38 +25,4 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
// define DAO start
|
||||
abstract fun getDao(): DbDao
|
||||
// define DAO end
|
||||
|
||||
companion object {
|
||||
private lateinit var _instance: AppDatabase
|
||||
|
||||
fun getInstance(): AppDatabase {
|
||||
if (!this::_instance.isInitialized)
|
||||
|
||||
|
||||
synchronized(this) {
|
||||
_instance = Room.databaseBuilder(
|
||||
MyApplication.instance,
|
||||
AppDatabase::class.java,
|
||||
MyApplication.instance.getString(R.string.app_name)
|
||||
).addMigrations(object : Migration(3, 4) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `account` ADD COLUMN `unique_id` TEXT")
|
||||
database.query("select id,unique_id from `account` where unique_id IS NULL")
|
||||
?.use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getInt(0)
|
||||
database.execSQL("update `account` set `unique_id` = '${getRandomString()}' where `id` = '$id'")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
return _instance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
package com.yogeshpaliyal.keypass
|
||||
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* yogeshpaliyal.foss@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 22-01-2021 22:41
|
||||
*/
|
||||
@HiltAndroidApp
|
||||
class MyApplication : Application() {
|
||||
|
||||
companion object{
|
||||
|
||||
@@ -2,23 +2,14 @@ package com.yogeshpaliyal.keypass.db_helper
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import androidx.room.withTransaction
|
||||
import com.google.gson.Gson
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import com.yogeshpaliyal.keypass.data.BackupData
|
||||
import com.yogeshpaliyal.keypass.utils.getOrCreateBackupKey
|
||||
import com.yogeshpaliyal.keypass.utils.getRandomString
|
||||
import com.yogeshpaliyal.keypass.utils.logD
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import javax.crypto.KeyGenerator
|
||||
import javax.crypto.SecretKey
|
||||
|
||||
|
||||
/*
|
||||
@@ -28,29 +19,35 @@ import javax.crypto.SecretKey
|
||||
* created on 20-02-2021 19:31
|
||||
*/
|
||||
|
||||
suspend fun AppDatabase.createBackup(key: String,contentResolver : ContentResolver,fileUri: Uri?) = withContext(Dispatchers.IO){
|
||||
fileUri ?: return@withContext false
|
||||
val data = getDao().getAllAccounts().first()
|
||||
suspend fun AppDatabase.createBackup(key: String, contentResolver: ContentResolver, fileUri: Uri?) =
|
||||
withContext(Dispatchers.IO) {
|
||||
fileUri ?: return@withContext false
|
||||
val data = getDao().getAllAccounts().first()
|
||||
|
||||
val json = Gson().toJson(BackupData(this@createBackup.openHelper.readableDatabase.version, data))
|
||||
val fileStream = contentResolver.openOutputStream(fileUri)
|
||||
EncryptionHelper.doCryptoEncrypt(key,json, fileStream)
|
||||
val json =
|
||||
Gson().toJson(BackupData(this@createBackup.openHelper.readableDatabase.version, data))
|
||||
val fileStream = contentResolver.openOutputStream(fileUri)
|
||||
EncryptionHelper.doCryptoEncrypt(key, json, fileStream)
|
||||
|
||||
return@withContext true
|
||||
}
|
||||
return@withContext true
|
||||
}
|
||||
|
||||
|
||||
suspend fun AppDatabase.restoreBackup(key: String,contentResolver : ContentResolver,fileUri: Uri?) = withContext(Dispatchers.IO){
|
||||
suspend fun AppDatabase.restoreBackup(
|
||||
key: String,
|
||||
contentResolver: ContentResolver,
|
||||
fileUri: Uri?
|
||||
) = withContext(Dispatchers.IO) {
|
||||
fileUri ?: return@withContext false
|
||||
|
||||
val restoredFile = try {
|
||||
EncryptionHelper.doCryptoDecrypt(key, contentResolver.openInputStream(fileUri))
|
||||
}catch (e:Exception){
|
||||
val restoredFile = try {
|
||||
EncryptionHelper.doCryptoDecrypt(key, contentResolver.openInputStream(fileUri))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return@withContext false
|
||||
}
|
||||
val data = Gson().fromJson(restoredFile, BackupData::class.java)
|
||||
if (data.version == 3){
|
||||
if (data.version == 3) {
|
||||
for (datum in data.data) {
|
||||
datum.uniqueId = getRandomString()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.yogeshpaliyal.keypass.di.module
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.room.Room
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.MyApplication
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.utils.MySharedPreferences
|
||||
import com.yogeshpaliyal.keypass.utils.getRandomString
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object AppModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun getDb(@ApplicationContext context: Context): AppDatabase{
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
AppDatabase::class.java,
|
||||
MyApplication.instance.getString(R.string.app_name)
|
||||
).addMigrations(object : Migration(3, 4) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE `account` ADD COLUMN `unique_id` TEXT")
|
||||
database.query("select id,unique_id from `account` where unique_id IS NULL")
|
||||
?.use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getInt(0)
|
||||
database.execSQL("update `account` set `unique_id` = '${getRandomString()}' where `id` = '$id'")
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun getSharedPre(@ApplicationContext context: Context): SharedPreferences{
|
||||
return MySharedPreferences(context).sharedPref
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,9 +9,11 @@ import androidx.core.content.ContextCompat
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.databinding.ActivityAuthenticationBinding
|
||||
import com.yogeshpaliyal.keypass.ui.nav.DashboardActivity
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AuthenticationActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var binding : ActivityAuthenticationBinding
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package com.yogeshpaliyal.keypass.ui.backup
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
@@ -21,9 +18,12 @@ import com.yogeshpaliyal.keypass.databinding.BackupActivityBinding
|
||||
import com.yogeshpaliyal.keypass.databinding.LayoutBackupKeypharseBinding
|
||||
import com.yogeshpaliyal.keypass.db_helper.createBackup
|
||||
import com.yogeshpaliyal.keypass.utils.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URLDecoder
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class BackupActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
@@ -54,8 +54,15 @@ class BackupActivity : AppCompatActivity() {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
@Inject
|
||||
lateinit var sp : SharedPreferences
|
||||
|
||||
@Inject
|
||||
lateinit var appDb : AppDatabase
|
||||
|
||||
private val CHOOSE_BACKUPS_LOCATION_REQUEST_CODE = 26212
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
@@ -70,8 +77,8 @@ class BackupActivity : AppCompatActivity() {
|
||||
}
|
||||
"create_backup" -> {
|
||||
context?.let {
|
||||
if (it.canUserAccessBackupDirectory()) {
|
||||
val selectedDirectory = Uri.parse(getBackupDirectory())
|
||||
if (it.canUserAccessBackupDirectory(sp)) {
|
||||
val selectedDirectory = Uri.parse(getBackupDirectory(sp))
|
||||
backup(selectedDirectory)
|
||||
}
|
||||
}
|
||||
@@ -90,14 +97,14 @@ class BackupActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun updateItems() {
|
||||
val isBackupEnabled = context?.canUserAccessBackupDirectory() ?: false
|
||||
val isBackupEnabled = context?.canUserAccessBackupDirectory(sp) ?: false
|
||||
|
||||
findPreference<Preference>("start_backup")?.isVisible = isBackupEnabled.not()
|
||||
|
||||
findPreference<Preference>("create_backup")?.isVisible = isBackupEnabled
|
||||
findPreference<Preference>("create_backup")?.summary = "Last backup : ${getBackupTime().formatCalendar("dd MMM yyyy hh:mm aa")}"
|
||||
findPreference<Preference>("create_backup")?.summary = "Last backup : ${getBackupTime(sp).formatCalendar("dd MMM yyyy hh:mm aa")}"
|
||||
findPreference<Preference>("backup_folder")?.isVisible = isBackupEnabled
|
||||
val directory = URLDecoder.decode(getBackupDirectory(),"utf-8").split("/")
|
||||
val directory = URLDecoder.decode(getBackupDirectory(sp),"utf-8").split("/")
|
||||
val folderName = directory.get(directory.lastIndex)
|
||||
findPreference<Preference>("backup_folder")?.summary = folderName
|
||||
findPreference<Preference>("settings_verify_key_phrase")?.isVisible = false
|
||||
@@ -131,7 +138,7 @@ class BackupActivity : AppCompatActivity() {
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
)
|
||||
|
||||
setBackupDirectory(selectedDirectory.toString())
|
||||
setBackupDirectory(sp,selectedDirectory.toString())
|
||||
backup(selectedDirectory)
|
||||
}
|
||||
}
|
||||
@@ -139,7 +146,7 @@ class BackupActivity : AppCompatActivity() {
|
||||
|
||||
private fun backup(selectedDirectory: Uri){
|
||||
|
||||
val keyPair = getOrCreateBackupKey()
|
||||
val keyPair = getOrCreateBackupKey(sp)
|
||||
|
||||
val tempFile = DocumentFile.fromTreeUri(requireContext(), selectedDirectory)?.createFile(
|
||||
"*/*",
|
||||
@@ -148,14 +155,14 @@ class BackupActivity : AppCompatActivity() {
|
||||
|
||||
lifecycleScope.launch {
|
||||
context?.contentResolver?.let {
|
||||
AppDatabase.getInstance().createBackup(keyPair.second,
|
||||
appDb.createBackup(keyPair.second,
|
||||
it,
|
||||
tempFile?.uri
|
||||
)
|
||||
setBackupTime(System.currentTimeMillis())
|
||||
setBackupTime(sp,System.currentTimeMillis())
|
||||
if (keyPair.first) {
|
||||
val binding = LayoutBackupKeypharseBinding.inflate(layoutInflater)
|
||||
binding.txtCode.text = getOrCreateBackupKey().second
|
||||
binding.txtCode.text = getOrCreateBackupKey(sp).second
|
||||
binding.txtCode.setOnClickListener {
|
||||
val clipboard =
|
||||
ContextCompat.getSystemService(
|
||||
@@ -189,9 +196,9 @@ class BackupActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun stopBackup(){
|
||||
clearBackupKey()
|
||||
setBackupDirectory("")
|
||||
setBackupTime(-1)
|
||||
clearBackupKey(sp)
|
||||
setBackupDirectory(sp,"")
|
||||
setBackupTime(sp,-1)
|
||||
updateItems()
|
||||
}
|
||||
|
||||
|
||||
@@ -5,46 +5,50 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.findNavController
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.databinding.FragmentDetailBinding
|
||||
import com.yogeshpaliyal.keypass.utils.PasswordGenerator
|
||||
import com.yogeshpaliyal.keypass.utils.initViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* yogeshpaliyal.foss@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 31-01-2021 10:38
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class DetailActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var binding : FragmentDetailBinding
|
||||
|
||||
lateinit var binding: FragmentDetailBinding
|
||||
|
||||
|
||||
companion object{
|
||||
@Inject
|
||||
lateinit var appDb: AppDatabase
|
||||
|
||||
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)
|
||||
.putExtra(ARG_ACCOUNT_ID, accountId)
|
||||
context?.startActivity(starter)
|
||||
}
|
||||
}
|
||||
|
||||
private val mViewModel by lazy {
|
||||
initViewModel(DetailViewModel::class.java)
|
||||
}
|
||||
private val mViewModel by viewModels<DetailViewModel>()
|
||||
|
||||
|
||||
private val accountId by lazy {
|
||||
@@ -66,7 +70,7 @@ class DetailActivity : AppCompatActivity() {
|
||||
binding.bottomAppBar.replaceMenu(R.menu.bottom_app_bar_detail)
|
||||
|
||||
binding.tilPassword.startIconDrawable = null
|
||||
}else{
|
||||
} else {
|
||||
binding.tilPassword.setStartIconDrawable(R.drawable.ic_round_refresh_24)
|
||||
|
||||
binding.tilPassword.setStartIconOnClickListener {
|
||||
@@ -80,7 +84,7 @@ class DetailActivity : AppCompatActivity() {
|
||||
onBackPressed()
|
||||
}
|
||||
binding.bottomAppBar.setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == R.id.action_delete){
|
||||
if (item.itemId == R.id.action_delete) {
|
||||
deleteAccount()
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
@@ -93,7 +97,7 @@ class DetailActivity : AppCompatActivity() {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val model = mViewModel.accountModel.value
|
||||
if (model != null) {
|
||||
AppDatabase.getInstance().getDao().insertOrUpdateAccount(model)
|
||||
appDb.getDao().insertOrUpdateAccount(model)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
onBackPressed()
|
||||
@@ -106,19 +110,20 @@ class DetailActivity : AppCompatActivity() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Are you sure?")
|
||||
.setMessage("Do you really want to delete this entry, it can't be restored")
|
||||
.setPositiveButton("Delete"
|
||||
.setPositiveButton(
|
||||
"Delete"
|
||||
) { dialog, which ->
|
||||
dialog?.dismiss()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (accountId > 0L) {
|
||||
AppDatabase.getInstance().getDao().deleteAccount(accountId)
|
||||
appDb.getDao().deleteAccount(accountId)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton("Cancel"){dialog, which ->
|
||||
.setNegativeButton("Cancel") { dialog, which ->
|
||||
dialog.dismiss()
|
||||
}.show()
|
||||
|
||||
@@ -129,8 +134,4 @@ class DetailActivity : AppCompatActivity() {
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,10 @@ import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/*
|
||||
@@ -16,7 +18,8 @@ import kotlinx.coroutines.launch
|
||||
* https://techpaliyal.com
|
||||
* created on 31-01-2021 11:52
|
||||
*/
|
||||
class DetailViewModel(application: Application) : AndroidViewModel(application) {
|
||||
@HiltViewModel
|
||||
class DetailViewModel @Inject constructor(application: Application, val appDb: AppDatabase) : AndroidViewModel(application) {
|
||||
|
||||
val accountModel by lazy { MutableLiveData<AccountModel>() }
|
||||
|
||||
@@ -24,7 +27,7 @@ class DetailViewModel(application: Application) : AndroidViewModel(application)
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
accountModel.postValue(
|
||||
AppDatabase.getInstance().getDao().getAccount(accountId) ?: AccountModel()
|
||||
appDb.getDao().getAccount(accountId) ?: AccountModel()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,13 @@ import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.getSystemService
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.databinding.ActivityGeneratePasswordBinding
|
||||
import com.yogeshpaliyal.keypass.utils.PasswordGenerator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class GeneratePasswordActivity : AppCompatActivity() {
|
||||
private lateinit var binding : ActivityGeneratePasswordBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -28,14 +31,14 @@ class GeneratePasswordActivity : AppCompatActivity() {
|
||||
val clipboard= getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText("random_password", binding.etPassword.text)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(this, "Copied to clipboard", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(this, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun generatePassword(){
|
||||
val password = PasswordGenerator(
|
||||
10, binding.cbCapAlphabets.isChecked,
|
||||
binding.sliderPasswordLength.value.toInt(), binding.cbCapAlphabets.isChecked,
|
||||
binding.cbLowerAlphabets.isChecked,
|
||||
binding.cbSymbols.isChecked,
|
||||
binding.cbNumbers.isChecked
|
||||
|
||||
@@ -5,21 +5,18 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import com.yogeshpaliyal.keypass.databinding.FragmentHomeBinding
|
||||
import com.yogeshpaliyal.keypass.db_helper.EncryptionHelper
|
||||
import com.yogeshpaliyal.keypass.listener.UniversalClickListener
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailActivity
|
||||
import com.yogeshpaliyal.keypass.utils.initViewModel
|
||||
import com.yogeshpaliyal.keypass.utils.logD
|
||||
import com.yogeshpaliyal.universal_adapter.adapter.UniversalAdapterViewType
|
||||
import com.yogeshpaliyal.universal_adapter.adapter.UniversalRecyclerAdapter
|
||||
import com.yogeshpaliyal.universal_adapter.utils.Resource
|
||||
import com.yogeshpaliyal.universal_adapter.utils.UniversalAdapterOptions
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -32,25 +29,30 @@ import kotlinx.coroutines.withContext
|
||||
* https://techpaliyal.com
|
||||
* created on 31-01-2021 09:25
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class HomeFragment : Fragment() {
|
||||
private lateinit var binding : FragmentHomeBinding
|
||||
private lateinit var binding: FragmentHomeBinding
|
||||
|
||||
private val mViewModel by viewModels<HomeViewModel>()
|
||||
|
||||
private val mViewModel by lazy {
|
||||
initViewModel(HomeViewModel::class.java)
|
||||
}
|
||||
|
||||
private val args: HomeFragmentArgs by navArgs()
|
||||
|
||||
|
||||
private val mAdapter by lazy {
|
||||
val adapterOptions = UniversalAdapterOptions<AccountModel>(this,
|
||||
content = UniversalAdapterViewType.Content(R.layout.item_accounts,mListener = mListener),
|
||||
noData = UniversalAdapterViewType.NoData(R.layout.layout_no_accounts))
|
||||
val adapterOptions = UniversalRecyclerAdapter.Builder<AccountModel>(
|
||||
this,
|
||||
content = UniversalAdapterViewType.Content(
|
||||
R.layout.item_accounts,
|
||||
listener = mListener
|
||||
),
|
||||
noData = UniversalAdapterViewType.NoData(R.layout.layout_no_accounts)
|
||||
)
|
||||
|
||||
UniversalRecyclerAdapter<AccountModel>(adapterOptions)
|
||||
}
|
||||
|
||||
val mListener = object : UniversalClickListener<AccountModel>{
|
||||
val mListener = object : UniversalClickListener<AccountModel> {
|
||||
override fun onItemClick(view: View, model: AccountModel) {
|
||||
DetailActivity.start(context, model.id)
|
||||
}
|
||||
@@ -61,15 +63,15 @@ class HomeFragment : Fragment() {
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = FragmentHomeBinding.inflate(layoutInflater,container,false)
|
||||
binding = FragmentHomeBinding.inflate(layoutInflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
binding.recyclerView.adapter = mAdapter
|
||||
lifecycleScope.launch(Dispatchers.IO){
|
||||
binding.recyclerView.adapter = mAdapter.getAdapter()
|
||||
lifecycleScope.launch() {
|
||||
mViewModel.loadData(args.tag).collect {
|
||||
withContext(Dispatchers.Main) {
|
||||
mAdapter.updateData(Resource.success(ArrayList(it)))
|
||||
@@ -80,5 +82,4 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -7,8 +7,10 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import com.yogeshpaliyal.universal_adapter.utils.Resource
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/*
|
||||
@@ -17,14 +19,9 @@ import kotlinx.coroutines.launch
|
||||
* https://techpaliyal.com
|
||||
* created on 30-01-2021 23:02
|
||||
*/
|
||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
@HiltViewModel
|
||||
class HomeViewModel @Inject constructor(application: Application, val appDb : AppDatabase) : AndroidViewModel(application) {
|
||||
|
||||
|
||||
private val dao by lazy {
|
||||
AppDatabase.getInstance().getDao()
|
||||
}
|
||||
|
||||
|
||||
suspend fun loadData(tag : String?)= if (tag.isNullOrBlank())dao.getAllAccounts() else dao.getAllAccounts(tag)
|
||||
suspend fun loadData(tag : String?)= if (tag.isNullOrBlank())appDb.getDao().getAllAccounts() else appDb.getDao().getAllAccounts(tag)
|
||||
|
||||
}
|
||||
@@ -8,7 +8,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.viewModels
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.observe
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
|
||||
@@ -19,14 +21,17 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior.from
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.databinding.FragmentBottomNavDrawerBinding
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailViewModel
|
||||
import com.yogeshpaliyal.keypass.utils.initViewModel
|
||||
import com.yogeshpaliyal.keypass.utils.themeColor
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
import kotlin.LazyThreadSafetyMode.NONE
|
||||
|
||||
/**
|
||||
* A [Fragment] which acts as a bottom navigation drawer.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class BottomNavDrawerFragment :
|
||||
Fragment(),
|
||||
NavigationAdapter.NavigationAdapterListener {
|
||||
@@ -39,9 +44,8 @@ class BottomNavDrawerFragment :
|
||||
from(binding.foregroundContainer)
|
||||
}
|
||||
|
||||
private val mViewModel by lazy {
|
||||
initViewModel(BottomNavViewModel::class.java)
|
||||
}
|
||||
private val mViewModel by viewModels<BottomNavViewModel>()
|
||||
|
||||
|
||||
private val bottomSheetCallback = BottomNavigationDrawerCallback()
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/*
|
||||
@@ -16,9 +18,10 @@ import kotlinx.coroutines.launch
|
||||
* https://techpaliyal.com
|
||||
* created on 31-01-2021 14:11
|
||||
*/
|
||||
class BottomNavViewModel(application: Application) : AndroidViewModel(application) {
|
||||
@HiltViewModel
|
||||
class BottomNavViewModel @Inject constructor (application: Application,val appDb: AppDatabase) : AndroidViewModel(application) {
|
||||
private val _navigationList: MutableLiveData<List<NavigationModelItem>> = MutableLiveData()
|
||||
private val tagsDb = AppDatabase.getInstance().getDao().getTags()
|
||||
private val tagsDb = appDb.getDao().getTags()
|
||||
|
||||
private var tagsList : List<String> ?= null
|
||||
|
||||
|
||||
@@ -24,10 +24,12 @@ import com.yogeshpaliyal.keypass.ui.detail.DetailActivity
|
||||
import com.yogeshpaliyal.keypass.ui.generate.GeneratePasswordActivity
|
||||
import com.yogeshpaliyal.keypass.ui.home.HomeFragmentDirections
|
||||
import com.yogeshpaliyal.keypass.ui.settings.MySettingsFragmentDirections
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DashboardActivity : AppCompatActivity(),
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
NavController.OnDestinationChangedListener,
|
||||
|
||||
@@ -2,10 +2,7 @@ package com.yogeshpaliyal.keypass.ui.settings
|
||||
|
||||
import android.R.attr.label
|
||||
import android.app.Activity
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
@@ -24,13 +21,22 @@ import com.yogeshpaliyal.keypass.db_helper.createBackup
|
||||
import com.yogeshpaliyal.keypass.db_helper.restoreBackup
|
||||
import com.yogeshpaliyal.keypass.ui.backup.BackupActivity
|
||||
import com.yogeshpaliyal.keypass.utils.*
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MySettingsFragment : PreferenceFragmentCompat() {
|
||||
private val CHOOSE_BACKUPS_LOCATION_REQUEST_CODE = 26212
|
||||
private val CHOOSE_RESTORE_FILE_REQUEST_CODE = 26213
|
||||
|
||||
@Inject
|
||||
lateinit var appDb : AppDatabase
|
||||
|
||||
@Inject
|
||||
lateinit var sp : SharedPreferences
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
}
|
||||
@@ -68,12 +74,13 @@ class MySettingsFragment : PreferenceFragmentCompat() {
|
||||
}
|
||||
|
||||
private fun selectBackupDirectory(){
|
||||
val selectedDirectory = Uri.parse(getBackupDirectory())
|
||||
val selectedDirectory = Uri.parse(getBackupDirectory(sp))
|
||||
|
||||
context?.let {
|
||||
if(it.canUserAccessBackupDirectory()){
|
||||
if(it.canUserAccessBackupDirectory(sp)){
|
||||
backup(selectedDirectory)
|
||||
}else{
|
||||
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
|
||||
intent.addFlags(
|
||||
@@ -121,7 +128,7 @@ class MySettingsFragment : PreferenceFragmentCompat() {
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
)
|
||||
|
||||
setBackupDirectory(selectedDirectory.toString())
|
||||
setBackupDirectory(sp,selectedDirectory.toString())
|
||||
backup(selectedDirectory)
|
||||
}
|
||||
} else if (requestCode == CHOOSE_RESTORE_FILE_REQUEST_CODE && resultCode == Activity.RESULT_OK){
|
||||
@@ -141,7 +148,7 @@ class MySettingsFragment : PreferenceFragmentCompat() {
|
||||
"Restore"
|
||||
) { dialog, which ->
|
||||
lifecycleScope.launch {
|
||||
val result = AppDatabase.getInstance().restoreBackup(binding.etKeyPhrase.text.toString(),contentResolver, selectedFile)
|
||||
val result = appDb.restoreBackup(binding.etKeyPhrase.text.toString(),contentResolver, selectedFile)
|
||||
if (result) {
|
||||
dialog?.dismiss()
|
||||
Toast.makeText(
|
||||
@@ -166,7 +173,7 @@ class MySettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
fun backup(selectedDirectory: Uri){
|
||||
|
||||
val keyPair = getOrCreateBackupKey()
|
||||
val keyPair = getOrCreateBackupKey(sp)
|
||||
|
||||
val tempFile = DocumentFile.fromTreeUri(requireContext(), selectedDirectory)?.createFile(
|
||||
"*/*",
|
||||
@@ -175,19 +182,19 @@ class MySettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
lifecycleScope.launch {
|
||||
context?.contentResolver?.let {
|
||||
AppDatabase.getInstance().createBackup(keyPair.second,
|
||||
appDb.createBackup(keyPair.second,
|
||||
it,
|
||||
tempFile?.uri
|
||||
)
|
||||
if (keyPair.first) {
|
||||
val binding = LayoutBackupKeypharseBinding.inflate(layoutInflater)
|
||||
binding.txtCode.text = getOrCreateBackupKey().second
|
||||
binding.txtCode.text = getOrCreateBackupKey(sp).second
|
||||
binding.txtCode.setOnClickListener {
|
||||
val clipboard =
|
||||
getSystemService(requireContext(), ClipboardManager::class.java)
|
||||
val clip = ClipData.newPlainText("KeyPass", binding.txtCode.text)
|
||||
val clip = ClipData.newPlainText(getString(R.string.app_name), binding.txtCode.text)
|
||||
clipboard?.setPrimaryClip(clip)
|
||||
Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, getString(R.string.copied_to_clipboard), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
MaterialAlertDialogBuilder(requireContext()).setView(binding.root)
|
||||
.setPositiveButton(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.yogeshpaliyal.keypass.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
@@ -28,8 +29,8 @@ fun getRandomString(sizeOfRandomString: Int): String {
|
||||
}
|
||||
|
||||
|
||||
fun Context.canUserAccessBackupDirectory(): Boolean {
|
||||
val backupDirectoryUri = getUri(getBackupDirectory()) ?: return false
|
||||
fun Context.canUserAccessBackupDirectory(sp : SharedPreferences): Boolean {
|
||||
val backupDirectoryUri = getUri(getBackupDirectory(sp)) ?: return false
|
||||
val backupDirectory = DocumentFile.fromTreeUri(this, backupDirectoryUri)
|
||||
return backupDirectory != null && backupDirectory.exists() && backupDirectory.canRead() && backupDirectory.canWrite()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.yogeshpaliyal.keypass.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.security.keystore.KeyGenParameterSpec
|
||||
import android.security.keystore.KeyProperties
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import com.yogeshpaliyal.keypass.MyApplication
|
||||
|
||||
class MySharedPreferences(context : Context) {
|
||||
private val masterKeyAlias = MasterKey.Builder(MyApplication.instance).also {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
// this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC
|
||||
|
||||
// this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC
|
||||
val spec = KeyGenParameterSpec.Builder(
|
||||
MasterKey.DEFAULT_MASTER_KEY_ALIAS,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
|
||||
)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||
.setKeySize(256)
|
||||
.build()
|
||||
it.setKeyGenParameterSpec(spec)
|
||||
}
|
||||
//it.setUserAuthenticationRequired(true)
|
||||
}
|
||||
.build()
|
||||
|
||||
|
||||
val sharedPref = EncryptedSharedPreferences.create(
|
||||
context,
|
||||
"secret_shared_prefs",
|
||||
masterKeyAlias,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
}
|
||||
@@ -16,43 +16,13 @@ import com.yogeshpaliyal.keypass.MyApplication
|
||||
* created on 21-02-2021 11:18
|
||||
*/
|
||||
|
||||
fun getSharedPreferences(): SharedPreferences {
|
||||
val masterKeyAlias = MasterKey.Builder(MyApplication.instance).also {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
// this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC
|
||||
|
||||
// this is equivalent to using deprecated MasterKeys.AES256_GCM_SPEC
|
||||
val spec = KeyGenParameterSpec.Builder(
|
||||
MasterKey.DEFAULT_MASTER_KEY_ALIAS,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
|
||||
)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
|
||||
.setKeySize(256)
|
||||
.build()
|
||||
it.setKeyGenParameterSpec(spec)
|
||||
}
|
||||
//it.setUserAuthenticationRequired(true)
|
||||
}
|
||||
.build()
|
||||
|
||||
return EncryptedSharedPreferences.create(
|
||||
MyApplication.instance,
|
||||
"secret_shared_prefs",
|
||||
masterKeyAlias,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pair
|
||||
* 1st => true if key is created now & false if key is created previously
|
||||
*
|
||||
*/
|
||||
fun getOrCreateBackupKey(reset: Boolean = false): Pair<Boolean, String> {
|
||||
val sp = getSharedPreferences()
|
||||
fun getOrCreateBackupKey(sp: SharedPreferences,reset: Boolean = false): Pair<Boolean, String> {
|
||||
|
||||
return if (sp.contains(BACKUP_KEY) && reset.not()) {
|
||||
Pair(false, sp.getString(BACKUP_KEY, "") ?: "")
|
||||
@@ -65,8 +35,8 @@ fun getOrCreateBackupKey(reset: Boolean = false): Pair<Boolean, String> {
|
||||
}
|
||||
}
|
||||
|
||||
fun clearBackupKey() {
|
||||
val sp = getSharedPreferences()
|
||||
fun clearBackupKey(sp: SharedPreferences) {
|
||||
|
||||
sp.edit {
|
||||
remove(BACKUP_KEY)
|
||||
}
|
||||
@@ -74,25 +44,23 @@ fun clearBackupKey() {
|
||||
}
|
||||
|
||||
|
||||
fun setBackupDirectory(string: String) {
|
||||
getSharedPreferences().edit {
|
||||
fun setBackupDirectory(sp: SharedPreferences,string: String) {
|
||||
sp.edit {
|
||||
putString(BACKUP_DIRECTORY, string)
|
||||
}
|
||||
}
|
||||
|
||||
fun setBackupTime(time: Long) {
|
||||
getSharedPreferences().edit {
|
||||
fun setBackupTime(sp: SharedPreferences,time: Long) {
|
||||
sp.edit {
|
||||
putLong(BACKUP_DATE_TIME, time)
|
||||
}
|
||||
}
|
||||
|
||||
fun getBackupDirectory(): String {
|
||||
val sp = getSharedPreferences()
|
||||
fun getBackupDirectory(sp: SharedPreferences,): String {
|
||||
return sp.getString(BACKUP_DIRECTORY, "") ?: ""
|
||||
}
|
||||
|
||||
fun getBackupTime(): Long {
|
||||
val sp = getSharedPreferences()
|
||||
fun getBackupTime(sp: SharedPreferences,): Long {
|
||||
return sp.getLong(BACKUP_DATE_TIME, -1) ?: -1L
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
android:id="@+id/tilPassword"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Password"
|
||||
android:hint="@string/password"
|
||||
app:endIconDrawable="@drawable/ic_twotone_content_copy_24"
|
||||
app:endIconMode="custom"
|
||||
app:layout_constraintEnd_toEndOf="@id/endGuideline"
|
||||
@@ -44,34 +44,49 @@
|
||||
android:singleLine="true" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginTop="@dimen/grid_2"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/password_length"/>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:valueFrom="7"
|
||||
android:valueTo="50"
|
||||
android:stepSize="1"
|
||||
android:id="@+id/sliderPasswordLength"/>
|
||||
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbCapAlphabets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
|
||||
android:text="UPPERCASE ALPHABETS" />
|
||||
android:text="@string/uppercase_alphabets" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbLowerAlphabets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checked="true"
|
||||
android:text="lowercase alphabets" />
|
||||
android:text="@string/lowercase_alphabets" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbNumbers"
|
||||
android:layout_width="match_parent"
|
||||
android:checked="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Numbers" />
|
||||
android:text="@string/numbers" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/cbSymbols"
|
||||
android:layout_width="match_parent"
|
||||
android:checked="true"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Symbols" />
|
||||
android:text="@string/symbols" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.yogeshpaliyal.keypass.custom_views.MaskedCardView>
|
||||
|
||||
@@ -37,4 +37,11 @@
|
||||
<string name="send_feedback_desc">Report technical issues or suggest new features</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="share_desc">Share app with others</string>
|
||||
<string name="password_length">Password length</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="uppercase_alphabets">UPPERCASE ALPHABETS</string>
|
||||
<string name="lowercase_alphabets">lowercase alphabets</string>
|
||||
<string name="symbols">Symbols</string>
|
||||
<string name="numbers">Numbers</string>
|
||||
<string name="copied_to_clipboard">Copied to clipboard</string>
|
||||
</resources>
|
||||
15
build.gradle
15
build.gradle
@@ -3,20 +3,25 @@ buildscript {
|
||||
|
||||
ext{
|
||||
kotlin_version = "1.4.21"
|
||||
lifecycle_version = "2.2.0"
|
||||
room_version = "2.2.6"
|
||||
navigation_version = '2.3.2'
|
||||
lifecycle_version = "2.3.1"
|
||||
room_version = "2.3.0"
|
||||
navigation_version = '2.3.5'
|
||||
ext.hilt_version = '2.35'
|
||||
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:4.1.2"
|
||||
classpath "com.android.tools.build:gradle:4.1.3"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.gms:google-services:4.3.4'
|
||||
classpath 'com.google.gms:google-services:4.3.8'
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigation_version"
|
||||
|
||||
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
||||
9
gradle/wrapper/gradle-wrapper.properties
vendored
9
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +1,6 @@
|
||||
#Fri Jan 22 22:30:39 IST 2021
|
||||
#Sat May 22 13:20:40 IST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||
distributionSha256Sum=c9910513d0eed63cd8f5c7fec4cb4a05731144770104a0871234a4edc3ba3cef
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
Reference in New Issue
Block a user