Address book android auto backup (#1641)

* Address book android auto backup

* Documentation update

* Address book container folder added

* Address book general error handling

* Address book memory storage hotfix

* Documentation update

---------

Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
Milan 2024-11-15 15:10:17 +01:00 committed by GitHub
parent 95285c5133
commit 32b132950c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 75 additions and 41 deletions

View File

@ -8,6 +8,7 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
### Added ### Added
- Address book encryption - Address book encryption
- Android auto backup support for address book encryption
- The device authentication feature on the Zashi app launch has been added - The device authentication feature on the Zashi app launch has been added
- Zashi app now supports Spanish language. It can be changed in the System settings options. - Zashi app now supports Spanish language. It can be changed in the System settings options.
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI - The Flexa SDK has been adopted to enable payments using the embedded Flexa UI

View File

@ -4,7 +4,10 @@
<application <application
android:name="co.electriccoin.zcash.app.ZcashApplication" android:name="co.electriccoin.zcash.app.ZcashApplication"
android:allowBackup="false" android:allowBackup="true"
android:fullBackupContent="@xml/auto_backup_config"
android:dataExtractionRules="@xml/auto_backup_config_android_12"
android:backupInForeground="true"
android:icon="@mipmap/ic_launcher_square" android:icon="@mipmap/ic_launcher_square"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:localeConfig="@xml/locales_config" android:localeConfig="@xml/locales_config"

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<include
domain="file"
path="address_book/." />
</full-backup-content>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup disableIfNoEncryptionCapabilities="false">
<include
domain="file"
path="address_book/." />
</cloud-backup>
</data-extraction-rules>

View File

@ -4,7 +4,6 @@
<application <application
android:name="co.electriccoin.zcash.app.ZcashApplication" android:name="co.electriccoin.zcash.app.ZcashApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_square" android:icon="@mipmap/ic_launcher_square"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"> android:label="@string/app_name">

View File

@ -4,7 +4,6 @@
<application <application
android:name="co.electriccoin.zcash.app.ZcashApplication" android:name="co.electriccoin.zcash.app.ZcashApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_square" android:icon="@mipmap/ic_launcher_square"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"> android:label="@string/app_name">

View File

@ -4,7 +4,6 @@
<application <application
android:name="co.electriccoin.zcash.app.ZcashApplication" android:name="co.electriccoin.zcash.app.ZcashApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher_square" android:icon="@mipmap/ic_launcher_square"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"> android:label="@string/app_name">

View File

@ -1,4 +1,9 @@
# Documentation # Documentation
Here you'll find documentation # Android auto backup testing
TBD
To force the android system back up the app use the following commands:
```
adb shell bmgr enable true
adb shell bmgr backupnow co.electriccoin.zcash.debug
```

View File

@ -11,6 +11,7 @@ directly impact users rather than highlighting other key architectural updates.*
### Added ### Added
- Address book encryption - Address book encryption
- Android auto backup support for address book encryption
- The device authentication feature on the Zashi app launch has been added - The device authentication feature on the Zashi app launch has been added
- Zashi app now supports Spanish language. It can be changed in the System settings options. - Zashi app now supports Spanish language. It can be changed in the System settings options.
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI - The Flexa SDK has been adopted to enable payments using the embedded Flexa UI

View File

@ -11,6 +11,7 @@ directly impact users rather than highlighting other key architectural updates.*
### Added ### Added
- Address book encryption - Address book encryption
- Android auto backup support for address book encryption
- The device authentication feature on the Zashi app launch has been added - The device authentication feature on the Zashi app launch has been added
- Zashi app now supports Spanish language. It can be changed in the System settings options. - Zashi app now supports Spanish language. It can be changed in the System settings options.
- The Flexa SDK has been adopted to enable payments using the embedded Flexa UI - The Flexa SDK has been adopted to enable payments using the embedded Flexa UI

View File

@ -11,8 +11,6 @@ import co.electriccoin.zcash.ui.common.serialization.addressbook.AddressBookKey
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import java.io.IOException
import java.security.GeneralSecurityException
interface LocalAddressBookDataSource { interface LocalAddressBookDataSource {
suspend fun getContacts(addressBookKey: AddressBookKey): AddressBook suspend fun getContacts(addressBookKey: AddressBookKey): AddressBook
@ -61,7 +59,9 @@ class LocalAddressBookDataSourceImpl(
lastUpdated = Clock.System.now(), lastUpdated = Clock.System.now(),
version = ADDRESS_BOOK_SERIALIZATION_V1, version = ADDRESS_BOOK_SERIALIZATION_V1,
contacts = emptyList(), contacts = emptyList(),
) ).also {
this@LocalAddressBookDataSourceImpl.addressBook = it
}
writeAddressBookToLocalStorage(newAddressBook, addressBookKey) writeAddressBookToLocalStorage(newAddressBook, addressBookKey)
} }
newAddressBook newAddressBook
@ -88,7 +88,9 @@ class LocalAddressBookDataSourceImpl(
address = address, address = address,
lastUpdated = lastUpdated, lastUpdated = lastUpdated,
), ),
) ).also {
addressBook = it
}
writeAddressBookToLocalStorage(newAddressBook, addressBookKey) writeAddressBookToLocalStorage(newAddressBook, addressBookKey)
newAddressBook newAddressBook
} }
@ -118,7 +120,9 @@ class LocalAddressBookDataSourceImpl(
) )
} }
.toList(), .toList(),
) ).also {
addressBook = it
}
writeAddressBookToLocalStorage(newAddressBook, addressBookKey) writeAddressBookToLocalStorage(newAddressBook, addressBookKey)
newAddressBook newAddressBook
} }
@ -139,7 +143,9 @@ class LocalAddressBookDataSourceImpl(
remove(addressBookContact) remove(addressBookContact)
} }
.toList(), .toList(),
) ).also {
addressBook = it
}
writeAddressBookToLocalStorage(newAddressBook, addressBookKey) writeAddressBookToLocalStorage(newAddressBook, addressBookKey)
newAddressBook newAddressBook
} }
@ -158,22 +164,15 @@ class LocalAddressBookDataSourceImpl(
@Suppress("ReturnCount") @Suppress("ReturnCount")
private suspend fun readLocalFileToAddressBook(addressBookKey: AddressBookKey): AddressBook? { private suspend fun readLocalFileToAddressBook(addressBookKey: AddressBookKey): AddressBook? {
val encryptedFile = addressBookStorageProvider.getStorageFile(addressBookKey) val encryptedFile = runCatching { addressBookStorageProvider.getStorageFile(addressBookKey) }.getOrNull()
val unencryptedFile = addressBookStorageProvider.getLegacyUnencryptedStorageFile() val unencryptedFile = runCatching { addressBookStorageProvider.getLegacyUnencryptedStorageFile() }.getOrNull()
if (encryptedFile != null) { if (encryptedFile != null) {
return try { return runCatching {
addressBookProvider.readAddressBookFromFile(encryptedFile, addressBookKey) addressBookProvider
.also { .readAddressBookFromFile(encryptedFile, addressBookKey)
unencryptedFile?.deleteSuspend() .also { unencryptedFile?.deleteSuspend() }
} }.onFailure { e -> Twig.warn(e) { "Failed to decrypt address book" } }.getOrNull()
} catch (e: GeneralSecurityException) {
Twig.warn(e) { "Failed to decrypt address book" }
null
} catch (e: IOException) {
Twig.warn(e) { "Failed to decrypt address book" }
null
}
} }
return if (unencryptedFile != null) { return if (unencryptedFile != null) {
@ -191,7 +190,9 @@ class LocalAddressBookDataSourceImpl(
addressBook: AddressBook, addressBook: AddressBook,
addressBookKey: AddressBookKey addressBookKey: AddressBookKey
) { ) {
val file = addressBookStorageProvider.getOrCreateStorageFile(addressBookKey) runCatching {
addressBookProvider.writeAddressBookToFile(file, addressBook, addressBookKey) val file = addressBookStorageProvider.getOrCreateStorageFile(addressBookKey)
addressBookProvider.writeAddressBookToFile(file, addressBook, addressBookKey)
}.onFailure { e -> Twig.warn(e) { "Failed to write address book" } }
} }
} }

View File

@ -11,17 +11,17 @@ interface AddressBookStorageProvider {
fun getOrCreateStorageFile(addressBookKey: AddressBookKey): File fun getOrCreateStorageFile(addressBookKey: AddressBookKey): File
/** // /**
* Create a temporary file into which data from remote is written. This file is removed after usage. // * Create a temporary file into which data from remote is written. This file is removed after usage.
*/ // */
fun getOrCreateTempStorageFile(): File // fun getOrCreateTempStorageFile(): File
} }
class AddressBookStorageProviderImpl( class AddressBookStorageProviderImpl(
private val context: Context private val context: Context
) : AddressBookStorageProvider { ) : AddressBookStorageProvider {
override fun getStorageFile(addressBookKey: AddressBookKey): File? { override fun getStorageFile(addressBookKey: AddressBookKey): File? {
return File(context.noBackupFilesDir, addressBookKey.fileIdentifier()) return File(getOrCreateAddressBookDir(), addressBookKey.fileIdentifier())
.takeIf { it.exists() && it.isFile } .takeIf { it.exists() && it.isFile }
} }
@ -31,19 +31,30 @@ class AddressBookStorageProviderImpl(
} }
override fun getOrCreateStorageFile(addressBookKey: AddressBookKey): File { override fun getOrCreateStorageFile(addressBookKey: AddressBookKey): File {
return getOrCreateFile(addressBookKey.fileIdentifier()) val file = File(getOrCreateAddressBookDir(), addressBookKey.fileIdentifier())
}
override fun getOrCreateTempStorageFile(): File = getOrCreateFile(REMOTE_ADDRESS_BOOK_FILE_NAME_LOCAL_COPY)
private fun getOrCreateFile(name: String): File {
val file = File(context.noBackupFilesDir, name)
if (!file.exists()) { if (!file.exists()) {
file.createNewFile() file.createNewFile()
} }
return file return file
} }
// override fun getOrCreateTempStorageFile(): File {
// val file = File(context.noBackupFilesDir, REMOTE_ADDRESS_BOOK_FILE_NAME_LOCAL_COPY)
// if (!file.exists()) {
// file.createNewFile()
// }
// return file
// }
private fun getOrCreateAddressBookDir(): File {
val filesDir = context.filesDir
val addressBookDir = File(filesDir, "address_book")
if (!addressBookDir.exists()) {
addressBookDir.mkdir()
}
return addressBookDir
}
} }
private const val LEGACY_UNENCRYPTED_ADDRESS_BOOK_FILE_NAME = "address_book" private const val LEGACY_UNENCRYPTED_ADDRESS_BOOK_FILE_NAME = "address_book"
private const val REMOTE_ADDRESS_BOOK_FILE_NAME_LOCAL_COPY = "address_book_temp" // private const val REMOTE_ADDRESS_BOOK_FILE_NAME_LOCAL_COPY = "address_book_temp"