backup improvements

This commit is contained in:
Yogesh Paliyal
2021-03-23 22:37:16 +05:30
parent 0f0e0e83bd
commit c6405ae5df
4 changed files with 217 additions and 42 deletions

View File

@@ -1,15 +1,26 @@
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.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.yogeshpaliyal.keypass.AppDatabase
import com.yogeshpaliyal.keypass.R
import com.yogeshpaliyal.keypass.utils.canUserAccessBackupDirectory
import com.yogeshpaliyal.keypass.utils.getBackupDirectory
import com.yogeshpaliyal.keypass.databinding.LayoutBackupKeypharseBinding
import com.yogeshpaliyal.keypass.db_helper.createBackup
import com.yogeshpaliyal.keypass.utils.*
import kotlinx.coroutines.launch
class BackupActivity : AppCompatActivity() {
@@ -34,24 +45,144 @@ class BackupActivity : AppCompatActivity() {
}
class SettingsFragment : PreferenceFragmentCompat() {
private val CHOOSE_BACKUPS_LOCATION_REQUEST_CODE = 26212
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.backup_preferences, rootKey)
val selectedDirectory = Uri.parse(getBackupDirectory())
if (context?.canUserAccessBackupDirectory() == true) {
// already backup on
findPreference<Preference>("create_backup")?.isVisible = true
findPreference<Preference>("backup_folder")?.isVisible = true
findPreference<Preference>("settings_verify_key_phrase")?.isVisible = true
findPreference<Preference>("start_backup")?.isVisible = false
} else {
// backup is off
findPreference<Preference>("create_backup")?.isVisible = false
findPreference<Preference>("backup_folder")?.isVisible = false
findPreference<Preference>("settings_verify_key_phrase")?.isVisible = false
findPreference<Preference>("start_backup")?.isVisible = true
}
updateItems()
}
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
"start_backup" -> {
startBackup()
}
"create_backup" -> {
context?.let {
if (it.canUserAccessBackupDirectory()) {
val selectedDirectory = Uri.parse(getBackupDirectory())
backup(selectedDirectory)
}
}
}
"backup_folder" -> {
changeBackupFolder()
}
getString(R.string.settings_verify_key_phrase) -> {
verifyKeyPhrase()
}
"stop_backup" -> {
stopBackup()
}
}
return super.onPreferenceTreeClick(preference)
}
private fun updateItems() {
val isBackupEnabled = context?.canUserAccessBackupDirectory() ?: false
findPreference<Preference>("start_backup")?.isVisible = isBackupEnabled.not()
findPreference<Preference>("create_backup")?.isVisible = isBackupEnabled
findPreference<Preference>("create_backup")?.setSummary("Last backup : ${getBackupTime().formatCalendar("dd MMM yyyy hh:mm aa")}")
findPreference<Preference>("backup_folder")?.isVisible = isBackupEnabled
findPreference<Preference>("settings_verify_key_phrase")?.isVisible = isBackupEnabled
findPreference<Preference>("stop_backup")?.isVisible = isBackupEnabled
}
private fun startBackup(){
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
try {
startActivityForResult(intent, CHOOSE_BACKUPS_LOCATION_REQUEST_CODE)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CHOOSE_BACKUPS_LOCATION_REQUEST_CODE && resultCode == Activity.RESULT_OK){
val contentResolver = context?.contentResolver
val selectedDirectory = data?.data
if (contentResolver != null && selectedDirectory != null) {
contentResolver.takePersistableUriPermission(
selectedDirectory,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
setBackupDirectory(selectedDirectory.toString())
backup(selectedDirectory)
}
}
}
private fun backup(selectedDirectory: Uri){
val keyPair = getOrCreateBackupKey()
val tempFile = DocumentFile.fromTreeUri(requireContext(), selectedDirectory)?.createFile(
"*/*",
"key_pass_backup_${System.currentTimeMillis()}.keypass"
)
lifecycleScope.launch {
context?.contentResolver?.let {
AppDatabase.getInstance().createBackup(keyPair.second,
it,
tempFile?.uri
)
setBackupTime(System.currentTimeMillis())
if (keyPair.first) {
val binding = LayoutBackupKeypharseBinding.inflate(layoutInflater)
binding.txtCode.text = getOrCreateBackupKey().second
binding.txtCode.setOnClickListener {
val clipboard =
ContextCompat.getSystemService(
requireContext(),
ClipboardManager::class.java
)
val clip = ClipData.newPlainText("KeyPass", binding.txtCode.text)
clipboard?.setPrimaryClip(clip)
Toast.makeText(context, "Copied to clipboard", Toast.LENGTH_SHORT).show()
}
MaterialAlertDialogBuilder(requireContext()).setView(binding.root)
.setPositiveButton(
"Yes"
) { dialog, which -> dialog?.dismiss()
}.show()
}else{
Toast.makeText(context, getString(R.string.backup_completed), Toast.LENGTH_SHORT).show()
}
}
updateItems()
}
}
private fun changeBackupFolder(){
startBackup()
}
private fun verifyKeyPhrase(){
Toast.makeText(context, "Under Development", Toast.LENGTH_SHORT).show()
}
private fun stopBackup(){
clearBackupKey()
setBackupDirectory("")
setBackupTime(-1)
updateItems()
}
}
}

View File

@@ -0,0 +1,21 @@
package com.yogeshpaliyal.keypass.utils
import java.text.SimpleDateFormat
import java.util.*
/*
* @author Yogesh Paliyal
* techpaliyal@gmail.com
* https://techpaliyal.com
* created on 23-03-2021 22:30
*/
fun Long.formatCalendar(dateTimeFormat: String?): String? {
val calendar: Calendar = Calendar.getInstance()
calendar.timeInMillis = this
val simpleDateFormat = SimpleDateFormat(dateTimeFormat, Locale.US)
return simpleDateFormat.format(calendar.getTime())
}

View File

@@ -7,7 +7,6 @@ import androidx.core.content.edit
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
import com.yogeshpaliyal.keypass.MyApplication
import java.util.*
/*
@@ -17,27 +16,28 @@ import java.util.*
* created on 21-02-2021 11:18
*/
fun getSharedPreferences() : SharedPreferences{
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
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)
}
// 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,
return EncryptedSharedPreferences.create(
MyApplication.instance,
"secret_shared_prefs",
masterKeyAlias,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
@@ -51,32 +51,51 @@ fun getSharedPreferences() : SharedPreferences{
* 1st => true if key is created now & false if key is created previously
*
*/
fun getOrCreateBackupKey(reset: Boolean = false): Pair<Boolean,String>{
fun getOrCreateBackupKey(reset: Boolean = false): Pair<Boolean, String> {
val sp = getSharedPreferences()
return if (sp.contains(BACKUP_KEY) && reset.not()){
Pair(false,sp.getString(BACKUP_KEY,"") ?: "")
}else{
return if (sp.contains(BACKUP_KEY) && reset.not()) {
Pair(false, sp.getString(BACKUP_KEY, "") ?: "")
} else {
val randomKey = getRandomString(16)
sp.edit {
putString(BACKUP_KEY, randomKey)
}
Pair(true,randomKey)
Pair(true, randomKey)
}
}
fun clearBackupKey() {
val sp = getSharedPreferences()
sp.edit {
putString(BACKUP_KEY, "")
}
fun setBackupDirectory(string: String){
}
fun setBackupDirectory(string: String) {
getSharedPreferences().edit {
putString(BACKUP_DIRECTORY, string)
}
}
fun getBackupDirectory(): String{
fun setBackupTime(time: Long) {
getSharedPreferences().edit {
putLong(BACKUP_DATE_TIME, time)
}
}
fun getBackupDirectory(): String {
val sp = getSharedPreferences()
return sp.getString(BACKUP_DIRECTORY,"") ?: ""
return sp.getString(BACKUP_DIRECTORY, "") ?: ""
}
fun getBackupTime(): Long {
val sp = getSharedPreferences()
return sp.getLong(BACKUP_DATE_TIME, -1) ?: -1L
}
private const val BACKUP_KEY = "backup_key"
private const val BACKUP_DIRECTORY = "backup_directory"
private const val BACKUP_DIRECTORY = "backup_directory"
private const val BACKUP_DATE_TIME = "backup_date_time"

View File

@@ -27,6 +27,10 @@
app:summary="Test your backup passphrase and verify that it matches"/>
<Preference
app:key="stop_backup"
app:isPreferenceVisible="false"
app:title="Turn Off Backups"/>
</PreferenceCategory>
</PreferenceScreen>