mirror of
https://github.com/yogeshpaliyal/KeyPass.git
synced 2026-01-08 00:49:47 -06:00
Working on Encryption
This commit is contained in:
@@ -6,6 +6,8 @@ plugins {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.2"
|
||||
@@ -38,12 +40,20 @@ android {
|
||||
viewBinding = true
|
||||
dataBinding = true
|
||||
}
|
||||
flavorDimensions "default"
|
||||
productFlavors {
|
||||
production {
|
||||
}
|
||||
|
||||
staging {
|
||||
applicationIdSuffix ".staging"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.3.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
@@ -69,10 +79,15 @@ dependencies {
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
|
||||
implementation 'com.github.yogeshpaliyal:Android-Universal-Recycler-View-Adapter:1.0.1'
|
||||
implementation 'com.github.yogeshpaliyal:Android-Universal-Recycler-View-Adapter:1.0.2'
|
||||
|
||||
|
||||
implementation "androidx.biometric:biometric:1.1.0"
|
||||
|
||||
implementation "androidx.preference:preference:1.1.1"
|
||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||
|
||||
|
||||
|
||||
//Androidx Security
|
||||
implementation "androidx.security:security-crypto:1.1.0-alpha03"
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
<activity
|
||||
android:name=".ui.nav.DashboardActivity"
|
||||
android:windowSoftInputMode="adjustPan"/>
|
||||
<activity android:name=".ui.detail.DetailActivity"
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
|
||||
<meta-data
|
||||
android:name="preloaded_fonts"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.yogeshpaliyal.keypass.db_helper
|
||||
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 07-02-2021 18:52
|
||||
*/
|
||||
|
||||
class CryptoException : Exception {
|
||||
constructor() {}
|
||||
constructor(message: String?, throwable: Throwable?) : super(message, throwable) {}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.yogeshpaliyal.keypass.db_helper
|
||||
|
||||
import com.yogeshpaliyal.universal_adapter.utils.LogHelper
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.security.InvalidKeyException
|
||||
import java.security.Key
|
||||
import java.security.NoSuchAlgorithmException
|
||||
import javax.crypto.*
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 07-02-2021 18:50
|
||||
*/
|
||||
object EncryptionHelper {
|
||||
private const val ALGORITHM = "AES"
|
||||
private const val TRANSFORMATION = "AES"
|
||||
// private const val TRANSFORMATION = "DES/CBC/PKCS5Padding"
|
||||
|
||||
@Throws(CryptoException::class)
|
||||
fun encrypt(key: String?, inputFile: File?, outputFile: File?) {
|
||||
LogHelper.logD("FindingBug", "Key => ${key} \n inputFile => ${inputFile?.path} \n Output File => ${outputFile?.path}")
|
||||
|
||||
doCryptoEncrypt(Cipher.ENCRYPT_MODE, key!!, inputFile!!, outputFile!!)
|
||||
}
|
||||
|
||||
@Throws(CryptoException::class)
|
||||
fun decrypt(
|
||||
key: String?,
|
||||
inputFile: File?,
|
||||
outputFile: File?
|
||||
) {
|
||||
doCryptoDecrypt(Cipher.DECRYPT_MODE, key!!, inputFile!!, outputFile!!)
|
||||
}
|
||||
|
||||
|
||||
/*@Throws(CryptoException::class)
|
||||
private fun doCrypto(
|
||||
cipherMode: Int, key: String, inputFile: File,
|
||||
outputFile: File
|
||||
) {
|
||||
try {
|
||||
val secretKey: Key =
|
||||
SecretKeySpec(key.toByteArray(), ALGORITHM)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(cipherMode, secretKey)
|
||||
val inputStream = FileInputStream(inputFile)
|
||||
|
||||
val inputBytes = inputFile.readBytes()
|
||||
|
||||
val outputBytes = cipher.doFinal(inputBytes)
|
||||
val outputStream = FileOutputStream(outputFile)
|
||||
outputStream.write(outputBytes)
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
} catch (ex: NoSuchPaddingException) {
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: NoSuchAlgorithmException) {
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: InvalidKeyException) {
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: BadPaddingException) {
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IllegalBlockSizeException) {
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IOException) {
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
}
|
||||
}*/
|
||||
|
||||
@Throws(CryptoException::class)
|
||||
private fun doCrypto(
|
||||
cipherMode: Int, key: String, inputFile: File,
|
||||
outputFile: File
|
||||
) {
|
||||
try {
|
||||
val secretKey: Key =
|
||||
SecretKeySpec(key.toByteArray(), ALGORITHM)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(cipherMode, secretKey)
|
||||
val inputStream = FileInputStream(inputFile)
|
||||
val outputStream = FileOutputStream(outputFile)
|
||||
|
||||
val cis = CipherInputStream(inputStream, cipher)
|
||||
var numberOfBytedRead: Int = 0
|
||||
val buffer = ByteArray(4096)
|
||||
|
||||
while (numberOfBytedRead >= 0) {
|
||||
outputStream.write(buffer, 0, numberOfBytedRead);
|
||||
numberOfBytedRead = cis.read(buffer)
|
||||
}
|
||||
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
} catch (ex: NoSuchPaddingException) {
|
||||
//Log.d("TestingEnc","NoSuchPaddingException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: NoSuchAlgorithmException) {
|
||||
//Log.d("TestingEnc","NoSuchAlgorithmException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: InvalidKeyException) {
|
||||
// Log.d("TestingEnc","InvalidKeyException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: BadPaddingException) {
|
||||
// Log.d("TestingEnc","BadPaddingException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IllegalBlockSizeException) {
|
||||
// Log.d("TestingEnc","IllegalBlockSizeException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IOException) {
|
||||
// Log.d("TestingEnc","IOException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(CryptoException::class)
|
||||
private fun doCryptoEncrypt(
|
||||
cipherMode: Int, key: String, inputFile: File,
|
||||
outputFile: File
|
||||
) {
|
||||
try {
|
||||
val secretKey: Key =
|
||||
SecretKeySpec(key.toByteArray(), ALGORITHM)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(cipherMode, secretKey)
|
||||
// val inputStream = FileInputStream(inputFile)
|
||||
// val outputStream = FileOutputStream(outputFile)
|
||||
|
||||
FileInputStream(inputFile).use {
|
||||
val inputStream = it
|
||||
FileOutputStream(outputFile).use {
|
||||
val outputStream = it
|
||||
CipherOutputStream(outputStream, cipher).use {
|
||||
inputStream.copyTo(it, 4096)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* val cis = CipherOutputStream(outputStream, cipher)
|
||||
var numberOfBytedRead: Int = 0
|
||||
val buffer = ByteArray(4096)
|
||||
|
||||
while (numberOfBytedRead >= 0) {
|
||||
cis.write(buffer, 0, numberOfBytedRead);
|
||||
numberOfBytedRead = inputStream.read(buffer)
|
||||
}
|
||||
cis.flush()
|
||||
cis.close()
|
||||
inputStream.close()
|
||||
outputStream.close()*/
|
||||
} catch (ex: NoSuchPaddingException) {
|
||||
// Log.d("TestingEnc","NoSuchPaddingException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: NoSuchAlgorithmException) {
|
||||
// Log.d("TestingEnc","NoSuchAlgorithmException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: InvalidKeyException) {
|
||||
//Log.d("TestingEnc","InvalidKeyException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: BadPaddingException) {
|
||||
// Log.d("TestingEnc","BadPaddingException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IllegalBlockSizeException) {
|
||||
// Log.d("TestingEnc","IllegalBlockSizeException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IOException) {
|
||||
// Log.d("TestingEnc","IOException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(CryptoException::class)
|
||||
private fun doCryptoDecrypt(
|
||||
cipherMode: Int, key: String, inputFile: File,
|
||||
outputFile: File
|
||||
) {
|
||||
try {
|
||||
val secretKey: Key =
|
||||
SecretKeySpec(key.toByteArray(), ALGORITHM)
|
||||
val cipher = Cipher.getInstance(TRANSFORMATION)
|
||||
cipher.init(cipherMode, secretKey)
|
||||
|
||||
|
||||
FileInputStream(inputFile).use {
|
||||
val inputStream = it
|
||||
FileOutputStream(outputFile).use {
|
||||
val outputStream = it
|
||||
CipherInputStream(inputStream, cipher).use {
|
||||
it.copyTo(outputStream, 4096)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*var numberOfBytedRead: Int = 0
|
||||
val buffer = ByteArray(4096)*/
|
||||
|
||||
|
||||
/*while (numberOfBytedRead >= 0) {
|
||||
outputStream.write(buffer, 0, numberOfBytedRead);
|
||||
numberOfBytedRead = cin.read(buffer)
|
||||
}*/
|
||||
|
||||
} catch (ex: NoSuchPaddingException) {
|
||||
//Log.d("TestingEnc","NoSuchPaddingException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: NoSuchAlgorithmException) {
|
||||
//Log.d("TestingEnc","NoSuchAlgorithmException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: InvalidKeyException) {
|
||||
//Log.d("TestingEnc","InvalidKeyException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: BadPaddingException) {
|
||||
//Log.d("TestingEnc","BadPaddingException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IllegalBlockSizeException) {
|
||||
//Log.d("TestingEnc","IllegalBlockSizeException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
} catch (ex: IOException) {
|
||||
// Log.d("TestingEnc","IOException")
|
||||
throw CryptoException("Error encrypting/decrypting file", ex)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.yogeshpaliyal.keypass.ui.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
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.initViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 31-01-2021 10:38
|
||||
*/
|
||||
class DetailActivity : AppCompatActivity() {
|
||||
|
||||
lateinit var binding : FragmentDetailBinding
|
||||
|
||||
|
||||
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 lazy {
|
||||
initViewModel(DetailViewModel::class.java)
|
||||
}
|
||||
|
||||
|
||||
private val accountId by lazy {
|
||||
intent?.extras?.getLong(ARG_ACCOUNT_ID) ?: -1
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = FragmentDetailBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
mViewModel.loadAccount(accountId)
|
||||
mViewModel.accountModel.observe(this, Observer {
|
||||
binding.accountData = it
|
||||
})
|
||||
|
||||
if (accountId > 0) {
|
||||
binding.bottomAppBar.replaceMenu(R.menu.bottom_app_bar_detail)
|
||||
}
|
||||
binding.bottomAppBar.setNavigationOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
binding.bottomAppBar.setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == R.id.action_delete){
|
||||
deleteAccount()
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
|
||||
return@setOnMenuItemClickListener false
|
||||
}
|
||||
|
||||
binding.btnSave.setOnClickListener {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val model = mViewModel.accountModel.value
|
||||
if (model != null) {
|
||||
AppDatabase.getInstance().getDao().insertOrUpdateAccount(model)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteAccount() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Are you sure?")
|
||||
.setMessage("Do you really want to delete this entry, it can't be restored")
|
||||
.setPositiveButton("Delete"
|
||||
) { dialog, which ->
|
||||
dialog?.dismiss()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (accountId > 0L) {
|
||||
AppDatabase.getInstance().getDao().deleteAccount(accountId)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton("Cancel"){dialog, which ->
|
||||
dialog.dismiss()
|
||||
}.show()
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.bottom_app_bar_detail, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.yogeshpaliyal.keypass.ui.detail
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import com.yogeshpaliyal.keypass.databinding.FragmentDetailBinding
|
||||
import com.yogeshpaliyal.keypass.utils.initViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
||||
/*
|
||||
* @author Yogesh Paliyal
|
||||
* techpaliyal@gmail.com
|
||||
* https://techpaliyal.com
|
||||
* created on 31-01-2021 10:38
|
||||
*/
|
||||
class DetailFragment : Fragment() {
|
||||
|
||||
lateinit var binding : FragmentDetailBinding
|
||||
private val args: DetailFragmentArgs by navArgs()
|
||||
|
||||
private val mViewModel by lazy {
|
||||
initViewModel(DetailViewModel::class.java)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = FragmentDetailBinding.inflate(layoutInflater)
|
||||
binding.lifecycleOwner = this
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mViewModel.loadAccount(args.accountId)
|
||||
mViewModel.accountModel.observe(viewLifecycleOwner, Observer {
|
||||
binding.accountData = it
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun fabClicked(){
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val model = mViewModel.accountModel.value
|
||||
if (model != null) {
|
||||
AppDatabase.getInstance().getDao().insertOrUpdateAccount(model)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,13 @@ class DetailViewModel(application: Application) : AndroidViewModel(application)
|
||||
|
||||
val accountModel by lazy { MutableLiveData<AccountModel>() }
|
||||
|
||||
fun loadAccount(accountId: Long?){
|
||||
fun loadAccount(accountId: Long?) {
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
accountModel.postValue(AppDatabase.getInstance().getDao().getAccount(accountId) ?: AccountModel())
|
||||
}
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
accountModel.postValue(
|
||||
AppDatabase.getInstance().getDao().getAccount(accountId) ?: AccountModel()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.data.AccountModel
|
||||
import com.yogeshpaliyal.keypass.databinding.FragmentHomeBinding
|
||||
import com.yogeshpaliyal.keypass.listener.UniversalClickListener
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailFragmentArgs
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailFragmentDirections
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailActivity
|
||||
import com.yogeshpaliyal.keypass.utils.initViewModel
|
||||
import com.yogeshpaliyal.universal_adapter.adapter.UniversalAdapterViewType
|
||||
import com.yogeshpaliyal.universal_adapter.adapter.UniversalRecyclerAdapter
|
||||
@@ -51,8 +50,7 @@ class HomeFragment : Fragment() {
|
||||
|
||||
val mListener = object : UniversalClickListener<AccountModel>{
|
||||
override fun onItemClick(view: View, model: AccountModel) {
|
||||
val destination = DetailFragmentDirections.actionGlobalCreateFragment(model.id ?: -1)
|
||||
findNavController().navigate(destination)
|
||||
DetailActivity.start(context, model.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,7 @@ import com.google.android.material.transition.MaterialElevationScale
|
||||
import com.yogeshpaliyal.keypass.AppDatabase
|
||||
import com.yogeshpaliyal.keypass.R
|
||||
import com.yogeshpaliyal.keypass.databinding.ActivityDashboardBinding
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailFragment
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailFragmentArgs
|
||||
import com.yogeshpaliyal.keypass.ui.detail.DetailFragmentDirections
|
||||
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
|
||||
@@ -41,8 +39,6 @@ class DashboardActivity : AppCompatActivity(),
|
||||
supportFragmentManager.findFragmentById(R.id.bottom_nav_drawer) as BottomNavDrawerFragment
|
||||
}
|
||||
|
||||
private var currentAccountId = -1L
|
||||
|
||||
val currentNavigationFragment: Fragment?
|
||||
get() = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
|
||||
?.childFragmentManager
|
||||
@@ -78,11 +74,6 @@ class DashboardActivity : AppCompatActivity(),
|
||||
|
||||
|
||||
binding.btnAdd.setOnClickListener {
|
||||
if (it.isActivated && currentNavigationFragment is DetailFragment) {
|
||||
(currentNavigationFragment as DetailFragment).fabClicked()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
currentNavigationFragment?.apply {
|
||||
exitTransition = MaterialElevationScale(false).apply {
|
||||
duration = resources.getInteger(R.integer.keypass_motion_duration_large).toLong()
|
||||
@@ -92,8 +83,7 @@ class DashboardActivity : AppCompatActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
val directions = DetailFragmentDirections.actionGlobalCreateFragment()
|
||||
findNavController(R.id.nav_host_fragment).navigate(directions)
|
||||
DetailActivity.start(this)
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +120,6 @@ class DashboardActivity : AppCompatActivity(),
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
when (item?.itemId) {
|
||||
R.id.action_delete -> deleteAccount()
|
||||
R.id.action_settings -> {
|
||||
val settingDestination = MySettingsFragmentDirections.actionGlobalSettings()
|
||||
findNavController(R.id.nav_host_fragment).navigate(settingDestination)
|
||||
@@ -140,46 +129,18 @@ class DashboardActivity : AppCompatActivity(),
|
||||
return true
|
||||
}
|
||||
|
||||
private fun deleteAccount() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle("Are you sure?")
|
||||
.setMessage("Do you really want to delete this entry, it can't be restored")
|
||||
.setPositiveButton("Delete"
|
||||
) { dialog, which ->
|
||||
dialog?.dismiss()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (currentAccountId != -1L) {
|
||||
AppDatabase.getInstance().getDao().deleteAccount(currentAccountId)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
findNavController(R.id.nav_host_fragment).popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton("Cancel"){dialog, which ->
|
||||
dialog.dismiss()
|
||||
}.show()
|
||||
|
||||
}
|
||||
|
||||
override fun onDestinationChanged(
|
||||
controller: NavController,
|
||||
destination: NavDestination,
|
||||
arguments: Bundle?
|
||||
) {
|
||||
|
||||
currentAccountId = -1
|
||||
when (destination.id) {
|
||||
R.id.homeFragment -> {
|
||||
binding.btnAdd.isActivated = false
|
||||
setBottomAppBarForHome(getBottomAppBarMenuForDestination(destination))
|
||||
}
|
||||
R.id.detailFragment -> {
|
||||
binding.btnAdd.isActivated = true
|
||||
currentAccountId =
|
||||
if (arguments == null) -1 else DetailFragmentArgs.fromBundle(arguments).accountId
|
||||
setBottomAppBarForHome(getBottomAppBarMenuForDestination(destination))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +202,6 @@ class DashboardActivity : AppCompatActivity(),
|
||||
val dest = destination ?: findNavController(R.id.nav_host_fragment).currentDestination
|
||||
return when (dest?.id) {
|
||||
R.id.homeFragment -> R.menu.bottom_app_bar_settings_menu
|
||||
R.id.detailFragment -> if (currentAccountId > 0) R.menu.bottom_app_bar_detail else R.menu.bottom_app_bar_settings_menu
|
||||
//R.id.emailFragment -> R.menu.bottom_app_bar_email_menu
|
||||
else -> R.menu.bottom_app_bar_settings_menu
|
||||
}
|
||||
|
||||
11
app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml
Normal file
11
app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<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/colorOnPrimary"
|
||||
android:autoMirrored="true">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11.67,3.87L9.9,2.1 0,12l9.9,9.9 1.77,-1.77L3.54,12z"/>
|
||||
</vector>
|
||||
@@ -5,13 +5,20 @@
|
||||
name="accountData"
|
||||
type="com.yogeshpaliyal.keypass.data.AccountModel" />
|
||||
</data>
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingHorizontal="@dimen/grid_0_5"
|
||||
android:paddingVertical="@dimen/grid_0_5"
|
||||
android:id="@+id/nestedScrollView">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
android:fillViewport="true"
|
||||
android:id="@+id/nestedScrollView"
|
||||
android:paddingHorizontal="@dimen/grid_0_5"
|
||||
android:paddingVertical="@dimen/grid_0_5">
|
||||
|
||||
<com.yogeshpaliyal.keypass.custom_views.MaskedCardView
|
||||
android:layout_width="match_parent"
|
||||
@@ -174,8 +181,29 @@
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
|
||||
</com.yogeshpaliyal.keypass.custom_views.MaskedCardView>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
<com.google.android.material.bottomappbar.BottomAppBar
|
||||
android:id="@+id/bottomAppBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:fabAlignmentMode="end"
|
||||
app:hideOnScroll="true"
|
||||
app:fabCradleRoundedCornerRadius="@dimen/bottom_app_bar_fab_cradle_corner_radius"
|
||||
app:fabCradleMargin="@dimen/bottom_app_bar_fab_cradle_margin"
|
||||
app:navigationIcon="@drawable/ic_baseline_arrow_back_ios_24"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:srcCompat="@drawable/ic_round_done_24"
|
||||
android:layout_marginEnd="@dimen/grid_2"
|
||||
app:layout_anchor="@id/bottomAppBar"
|
||||
android:id="@+id/btnSave"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
@@ -17,7 +17,7 @@
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/detailFragment"
|
||||
android:name="com.yogeshpaliyal.keypass.ui.detail.DetailFragment"
|
||||
android:name="com.yogeshpaliyal.keypass.ui.detail.DetailActivity"
|
||||
android:label="DetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument android:name="accountId"
|
||||
|
||||
Reference in New Issue
Block a user