Ktlint 1.5.0
This commit is contained in:
parent
31ac7b8c6d
commit
37ed3841e4
|
@ -1,6 +1,8 @@
|
|||
package model
|
||||
|
||||
enum class BuildType(val value: String) {
|
||||
enum class BuildType(
|
||||
val value: String
|
||||
) {
|
||||
DEBUG("debug"),
|
||||
RELEASE("release"),
|
||||
BENCHMARK("benchmark")
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package model
|
||||
|
||||
enum class NetworkDimension(val value: String) {
|
||||
enum class NetworkDimension(
|
||||
val value: String
|
||||
) {
|
||||
MAINNET("zcashmainnet"),
|
||||
TESTNET("zcashtestnet");
|
||||
|
||||
|
@ -9,7 +11,9 @@ enum class NetworkDimension(val value: String) {
|
|||
}
|
||||
}
|
||||
|
||||
enum class DistributionDimension(val value: String) {
|
||||
enum class DistributionDimension(
|
||||
val value: String
|
||||
) {
|
||||
STORE("store"),
|
||||
FOSS("foss");
|
||||
|
||||
|
|
|
@ -47,7 +47,8 @@ data class ChangelogEntry(
|
|||
GsonBuilder()
|
||||
.serializeNulls()
|
||||
.create()
|
||||
.toJson(this).replace("\"", "\\\"")
|
||||
.toJson(this)
|
||||
.replace("\"", "\\\"")
|
||||
}
|
||||
|
||||
data class ChangelogEntrySection(
|
||||
|
|
|
@ -65,7 +65,8 @@ object ChangelogParser {
|
|||
log("Parser: index from: $fromIndex")
|
||||
|
||||
val toIndex =
|
||||
nodes.subList(fromIndex + 1, nodes.size)
|
||||
nodes
|
||||
.subList(fromIndex + 1, nodes.size)
|
||||
.indexOfFirst { findNodeByPrefix(it) }
|
||||
.let {
|
||||
// Applies to the last or the only one entry
|
||||
|
@ -117,24 +118,22 @@ object ChangelogParser {
|
|||
subNode.startsWith("### ${titleByLanguage(TitleType.FIXED, languageTag)}") ||
|
||||
subNode.startsWith("### ${titleByLanguage(TitleType.REMOVED, languageTag)}")
|
||||
|
||||
private fun List<String>.getVersionPart(versionNameFallback: String): String {
|
||||
return if (this.contains("## [Unreleased]")) {
|
||||
private fun List<String>.getVersionPart(versionNameFallback: String): String =
|
||||
if (this.contains("## [Unreleased]")) {
|
||||
versionNameFallback
|
||||
} else {
|
||||
// Parse just version name omitting version code as we currently don't need it in the UI
|
||||
this[0].split("[")[1].split(" ")[0].trim()
|
||||
}
|
||||
}
|
||||
|
||||
private val dateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH)
|
||||
|
||||
private fun List<String>.getDatePart(): String {
|
||||
return if (this.contains("## [Unreleased]")) {
|
||||
private fun List<String>.getDatePart(): String =
|
||||
if (this.contains("## [Unreleased]")) {
|
||||
dateFormatter.format(Date())
|
||||
} else {
|
||||
this[0].split("- ")[1].trim()
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<String>.getNodePart(title: String): ChangelogEntrySection? {
|
||||
val fromContent = "### $title"
|
||||
|
@ -160,7 +159,8 @@ object ChangelogParser {
|
|||
// To remove hard line wrap from AS
|
||||
.map { it.replace("\n ", "") }
|
||||
.joinToString(prefix = "\n", separator = "\n")
|
||||
.takeIf { it.isNotBlank() }?.let {
|
||||
.takeIf { it.isNotBlank() }
|
||||
?.let {
|
||||
ChangelogEntrySection(title = title, content = it)
|
||||
}
|
||||
}
|
||||
|
@ -168,8 +168,8 @@ object ChangelogParser {
|
|||
private fun titleByLanguage(
|
||||
type: TitleType,
|
||||
languageTag: LanguageTag
|
||||
): String {
|
||||
return when (type) {
|
||||
): String =
|
||||
when (type) {
|
||||
TitleType.ADDED ->
|
||||
when (languageTag) {
|
||||
is LanguageTag.English -> ADDED_PART_EN
|
||||
|
@ -191,13 +191,18 @@ object ChangelogParser {
|
|||
is LanguageTag.Spanish -> REMOVED_PART_ES
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class LanguageTag(open val tag: String) {
|
||||
data class English(override val tag: String = ENGLISH_TAG) : LanguageTag(tag)
|
||||
sealed class LanguageTag(
|
||||
open val tag: String
|
||||
) {
|
||||
data class English(
|
||||
override val tag: String = ENGLISH_TAG
|
||||
) : LanguageTag(tag)
|
||||
|
||||
data class Spanish(override val tag: String = SPANISH_TAG) : LanguageTag(tag)
|
||||
data class Spanish(
|
||||
override val tag: String = SPANISH_TAG
|
||||
) : LanguageTag(tag)
|
||||
}
|
||||
|
||||
private enum class TitleType {
|
||||
|
|
|
@ -23,4 +23,7 @@ object Git {
|
|||
}
|
||||
}
|
||||
|
||||
data class GitInfo(val sha: String, val commitCount: Int)
|
||||
data class GitInfo(
|
||||
val sha: String,
|
||||
val commitCount: Int
|
||||
)
|
||||
|
|
|
@ -13,32 +13,35 @@ import kotlinx.datetime.Instant
|
|||
class MergingConfigurationProvider(
|
||||
private val configurationProviders: PersistentList<ConfigurationProvider>
|
||||
) : ConfigurationProvider {
|
||||
override fun peekConfiguration(): Configuration {
|
||||
return MergingConfiguration(configurationProviders.map { it.peekConfiguration() }.toPersistentList())
|
||||
}
|
||||
override fun peekConfiguration(): Configuration =
|
||||
MergingConfiguration(
|
||||
configurationProviders
|
||||
.map {
|
||||
it.peekConfiguration()
|
||||
}.toPersistentList()
|
||||
)
|
||||
|
||||
override fun getConfigurationFlow(): Flow<Configuration> {
|
||||
return if (configurationProviders.isEmpty()) {
|
||||
override fun getConfigurationFlow(): Flow<Configuration> =
|
||||
if (configurationProviders.isEmpty()) {
|
||||
flowOf(MergingConfiguration(persistentListOf<Configuration>()))
|
||||
} else {
|
||||
combine(configurationProviders.map { it.getConfigurationFlow() }) { configurations ->
|
||||
MergingConfiguration(configurations.toList().toPersistentList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hintToRefresh() {
|
||||
configurationProviders.forEach { it.hintToRefresh() }
|
||||
}
|
||||
}
|
||||
|
||||
private data class MergingConfiguration(private val configurations: PersistentList<Configuration>) : Configuration {
|
||||
private data class MergingConfiguration(
|
||||
private val configurations: PersistentList<Configuration>
|
||||
) : Configuration {
|
||||
override val updatedAt: Instant?
|
||||
get() = configurations.mapNotNull { it.updatedAt }.maxOrNull()
|
||||
|
||||
override fun hasKey(key: ConfigKey): Boolean {
|
||||
return null != configurations.firstWithKey(key)
|
||||
}
|
||||
override fun hasKey(key: ConfigKey): Boolean = null != configurations.firstWithKey(key)
|
||||
|
||||
// TODO [#1373]: Catch and log Configuration Key Coercion Failures
|
||||
// TODO [#1373]: https://github.com/Electric-Coin-Company/zashi-android/issues/1373
|
||||
|
|
|
@ -7,7 +7,9 @@ package co.electriccoin.zcash.configuration.model.entry
|
|||
* least common denominator with some reasonable limits on what the keys can contain.
|
||||
*/
|
||||
@JvmInline
|
||||
value class ConfigKey(val key: String) {
|
||||
value class ConfigKey(
|
||||
val key: String
|
||||
) {
|
||||
init {
|
||||
requireKeyConstraints(key)
|
||||
}
|
||||
|
|
|
@ -4,4 +4,7 @@ package co.electriccoin.zcash.configuration.model.exception
|
|||
* Exception that may occur when parsing a value from the remote configuration. This could mean that someone made an
|
||||
* error in the remote config console.
|
||||
*/
|
||||
class ConfigurationParseException(message: String, cause: Throwable?) : IllegalArgumentException(message, cause)
|
||||
class ConfigurationParseException(
|
||||
message: String,
|
||||
cause: Throwable?
|
||||
) : IllegalArgumentException(message, cause)
|
||||
|
|
|
@ -109,14 +109,12 @@ class MergingConfigurationProviderTest {
|
|||
}
|
||||
}
|
||||
|
||||
private class MockConfigurationProvider(private val configuration: Configuration) : ConfigurationProvider {
|
||||
override fun peekConfiguration(): Configuration {
|
||||
return configuration
|
||||
}
|
||||
private class MockConfigurationProvider(
|
||||
private val configuration: Configuration
|
||||
) : ConfigurationProvider {
|
||||
override fun peekConfiguration(): Configuration = configuration
|
||||
|
||||
override fun getConfigurationFlow(): Flow<Configuration> {
|
||||
return flowOf(configuration)
|
||||
}
|
||||
override fun getConfigurationFlow(): Flow<Configuration> = flowOf(configuration)
|
||||
|
||||
override fun hintToRefresh() {
|
||||
// no-op
|
||||
|
|
|
@ -11,7 +11,9 @@ import kotlinx.datetime.Instant
|
|||
* mutate the configuration by mutating the original map. The mapping is stored in a val field
|
||||
* though, making the initial mapping thread-safe.
|
||||
*/
|
||||
class MockConfiguration(private val configurationMapping: Map<String, String> = emptyMap()) : Configuration {
|
||||
class MockConfiguration(
|
||||
private val configurationMapping: Map<String, String> = emptyMap()
|
||||
) : Configuration {
|
||||
override val updatedAt: Instant? = null
|
||||
|
||||
@Throws(ConfigurationParseException::class)
|
||||
|
|
|
@ -34,8 +34,8 @@ class IntentConfigurationReceiver : BroadcastReceiver() {
|
|||
}
|
||||
|
||||
// https://issuetracker.google.com/issues/36927401
|
||||
private fun Intent.defuse(): Intent? {
|
||||
return try {
|
||||
private fun Intent.defuse(): Intent? =
|
||||
try {
|
||||
extras?.containsKey(null)
|
||||
this
|
||||
} catch (
|
||||
|
@ -43,4 +43,3 @@ private fun Intent.defuse(): Intent? {
|
|||
) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,8 @@ import android.content.Context
|
|||
import co.electriccoin.zcash.crash.android.internal.local.LocalCrashReporter
|
||||
|
||||
class ListCrashReportersImpl : ListCrashReporters {
|
||||
override fun provideReporters(context: Context): List<CrashReporter> {
|
||||
return listOfNotNull(
|
||||
override fun provideReporters(context: Context): List<CrashReporter> =
|
||||
listOfNotNull(
|
||||
LocalCrashReporter.getInstance(context),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ import java.io.File
|
|||
@Suppress("ReturnCount")
|
||||
suspend fun ExceptionPath.getExceptionDirectory(context: Context): File? {
|
||||
val exceptionDirectory =
|
||||
context.getExternalFilesDirSuspend(null)
|
||||
context
|
||||
.getExternalFilesDirSuspend(null)
|
||||
?.let { File(File(it, ExceptionPath.LOG_DIRECTORY_NAME), ExceptionPath.EXCEPTION_DIRECTORY_NAME) }
|
||||
|
||||
if (null == exceptionDirectory) {
|
||||
|
|
|
@ -64,5 +64,6 @@ object GlobalCrashReporter {
|
|||
}
|
||||
|
||||
private fun isCrashProcess(context: Context) =
|
||||
ProcessNameCompat.getProcessName(context)
|
||||
ProcessNameCompat
|
||||
.getProcessName(context)
|
||||
.endsWith(GlobalCrashReporter.CRASH_PROCESS_NAME_SUFFIX)
|
||||
|
|
|
@ -13,7 +13,9 @@ import kotlinx.coroutines.launch
|
|||
/**
|
||||
* Registers an exception handler to write exceptions to disk.
|
||||
*/
|
||||
internal class LocalCrashReporter(private val applicationContext: Context) : CrashReporter {
|
||||
internal class LocalCrashReporter(
|
||||
private val applicationContext: Context
|
||||
) : CrashReporter {
|
||||
private val crashReportingScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
|
||||
@AnyThread
|
||||
|
@ -41,8 +43,6 @@ internal class LocalCrashReporter(private val applicationContext: Context) : Cra
|
|||
LocalCrashReporter(it.applicationContext)
|
||||
}
|
||||
|
||||
fun getInstance(context: Context): CrashReporter {
|
||||
return lazyWithArgument.getInstance(context)
|
||||
}
|
||||
fun getInstance(context: Context): CrashReporter = lazyWithArgument.getInstance(context)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,4 +36,6 @@ private suspend fun getFirebaseAppContainer(context: Context): FirebaseAppContai
|
|||
FirebaseAppContainer(firebaseApp)
|
||||
}
|
||||
|
||||
private class FirebaseAppContainer(val firebaseApp: FirebaseApp?)
|
||||
private class FirebaseAppContainer(
|
||||
val firebaseApp: FirebaseApp?
|
||||
)
|
||||
|
|
|
@ -121,9 +121,7 @@ private class FirebaseCrashReporterImpl(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getInstance(context: Context): CrashReporter? {
|
||||
return lazyWithArgument.getInstance(context)
|
||||
}
|
||||
suspend fun getInstance(context: Context): CrashReporter? = lazyWithArgument.getInstance(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,4 +36,6 @@ private suspend fun getFirebaseAppContainer(context: Context): FirebaseAppContai
|
|||
FirebaseAppContainer(firebaseApp)
|
||||
}
|
||||
|
||||
private class FirebaseAppContainer(val firebaseApp: FirebaseApp?)
|
||||
private class FirebaseAppContainer(
|
||||
val firebaseApp: FirebaseApp?
|
||||
)
|
||||
|
|
|
@ -121,9 +121,7 @@ private class FirebaseCrashReporterImpl(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getInstance(context: Context): CrashReporter? {
|
||||
return lazyWithArgument.getInstance(context)
|
||||
}
|
||||
suspend fun getInstance(context: Context): CrashReporter? = lazyWithArgument.getInstance(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,4 +36,6 @@ private suspend fun getFirebaseAppContainer(context: Context): FirebaseAppContai
|
|||
FirebaseAppContainer(firebaseApp)
|
||||
}
|
||||
|
||||
private class FirebaseAppContainer(val firebaseApp: FirebaseApp?)
|
||||
private class FirebaseAppContainer(
|
||||
val firebaseApp: FirebaseApp?
|
||||
)
|
||||
|
|
|
@ -121,9 +121,7 @@ private class FirebaseCrashReporterImpl(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getInstance(context: Context): CrashReporter? {
|
||||
return lazyWithArgument.getInstance(context)
|
||||
}
|
||||
suspend fun getInstance(context: Context): CrashReporter? = lazyWithArgument.getInstance(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,4 +36,6 @@ private suspend fun getFirebaseAppContainer(context: Context): FirebaseAppContai
|
|||
FirebaseAppContainer(firebaseApp)
|
||||
}
|
||||
|
||||
private class FirebaseAppContainer(val firebaseApp: FirebaseApp?)
|
||||
private class FirebaseAppContainer(
|
||||
val firebaseApp: FirebaseApp?
|
||||
)
|
||||
|
|
|
@ -121,9 +121,7 @@ private class FirebaseCrashReporterImpl(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getInstance(context: Context): CrashReporter? {
|
||||
return lazyWithArgument.getInstance(context)
|
||||
}
|
||||
suspend fun getInstance(context: Context): CrashReporter? = lazyWithArgument.getInstance(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ FULLADLE_VERSION=0.17.5
|
|||
GOOGLE_PLAY_SERVICES_GRADLE_PLUGIN_VERSION=4.4.2
|
||||
GRADLE_VERSIONS_PLUGIN_VERSION=0.52.0
|
||||
JGIT_VERSION=6.4.0.202211300538-r
|
||||
KTLINT_VERSION=1.2.1
|
||||
KTLINT_VERSION=1.5.0
|
||||
KOIN_VERSION=4.0.2
|
||||
|
||||
ACCOMPANIST_PERMISSIONS_VERSION=0.37.2
|
||||
|
|
|
@ -45,7 +45,8 @@ interface PreferenceDefault<T> {
|
|||
* indicating what was stored in the preferences, in addition to subsequent updates.
|
||||
*/
|
||||
fun observe(preferenceProvider: PreferenceProvider): Flow<T> =
|
||||
preferenceProvider.observe(key)
|
||||
preferenceProvider
|
||||
.observe(key)
|
||||
.map { getValue(preferenceProvider) }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
|
|
@ -9,7 +9,9 @@ import kotlin.jvm.JvmInline
|
|||
* find a least common denominator with some reasonable limits on what the keys can contain.
|
||||
*/
|
||||
@JvmInline
|
||||
value class PreferenceKey(val key: String) {
|
||||
value class PreferenceKey(
|
||||
val key: String
|
||||
) {
|
||||
init {
|
||||
requireKeyConstraints(key)
|
||||
}
|
||||
|
|
|
@ -172,9 +172,11 @@ class AndroidPreferenceProvider private constructor(
|
|||
val sharedPreferences =
|
||||
withContext(singleThreadedDispatcher) {
|
||||
val mainKey =
|
||||
MasterKey.Builder(context).apply {
|
||||
setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
}.build()
|
||||
MasterKey
|
||||
.Builder(context)
|
||||
.apply {
|
||||
setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
}.build()
|
||||
|
||||
EncryptedSharedPreferences.create(
|
||||
context,
|
||||
|
|
|
@ -3,8 +3,9 @@ package co.electriccoin.zcash.preference
|
|||
import android.content.Context
|
||||
import co.electriccoin.zcash.preference.api.PreferenceProvider
|
||||
|
||||
class EncryptedPreferenceProvider(private val context: Context) : PreferenceHolder() {
|
||||
override suspend fun create(): PreferenceProvider {
|
||||
return AndroidPreferenceProvider.newEncrypted(context, "co.electriccoin.zcash.encrypted")
|
||||
}
|
||||
class EncryptedPreferenceProvider(
|
||||
private val context: Context
|
||||
) : PreferenceHolder() {
|
||||
override suspend fun create(): PreferenceProvider =
|
||||
AndroidPreferenceProvider.newEncrypted(context, "co.electriccoin.zcash.encrypted")
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@ package co.electriccoin.zcash.preference
|
|||
import android.content.Context
|
||||
import co.electriccoin.zcash.preference.api.PreferenceProvider
|
||||
|
||||
class StandardPreferenceProvider(private val context: Context) : PreferenceHolder() {
|
||||
override suspend fun create(): PreferenceProvider {
|
||||
return AndroidPreferenceProvider.newStandard(context, "co.electriccoin.zcash")
|
||||
}
|
||||
class StandardPreferenceProvider(
|
||||
private val context: Context
|
||||
) : PreferenceHolder() {
|
||||
override suspend fun create(): PreferenceProvider =
|
||||
AndroidPreferenceProvider.newStandard(context, "co.electriccoin.zcash")
|
||||
}
|
||||
|
|
|
@ -10,9 +10,8 @@ import java.text.DecimalFormat
|
|||
import java.util.Locale
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun PercentDecimal.toPercentageWithDecimal(decimalFormat: DecimalFormat = preparePercentDecimalFormat()): String {
|
||||
return decimalFormat.format(decimal * 100)
|
||||
}
|
||||
fun PercentDecimal.toPercentageWithDecimal(decimalFormat: DecimalFormat = preparePercentDecimalFormat()): String =
|
||||
decimalFormat.format(decimal * 100)
|
||||
|
||||
private fun preparePercentDecimalFormat(): DecimalFormat =
|
||||
DecimalFormat().apply {
|
||||
|
|
|
@ -15,7 +15,9 @@ sealed class SeedPhraseValidation {
|
|||
|
||||
object FailedChecksum : SeedPhraseValidation()
|
||||
|
||||
class Valid(val seedPhrase: SeedPhrase) : SeedPhraseValidation()
|
||||
class Valid(
|
||||
val seedPhrase: SeedPhrase
|
||||
) : SeedPhraseValidation()
|
||||
|
||||
companion object {
|
||||
suspend fun new(list: List<String>): SeedPhraseValidation {
|
||||
|
|
|
@ -3,7 +3,11 @@ package cash.z.ecc.sdk.model
|
|||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
|
||||
data class ZecRequest(val address: WalletAddress.Unified, val amount: Zatoshi, val message: ZecRequestMessage) {
|
||||
data class ZecRequest(
|
||||
val address: WalletAddress.Unified,
|
||||
val amount: Zatoshi,
|
||||
val message: ZecRequestMessage
|
||||
) {
|
||||
// TODO [#397]: Waiting for an implementation of Uri parser in SDK project
|
||||
// TODO [#397]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/397
|
||||
suspend fun toUri(): String {
|
||||
|
@ -21,7 +25,9 @@ data class ZecRequest(val address: WalletAddress.Unified, val amount: Zatoshi, v
|
|||
}
|
||||
|
||||
@JvmInline
|
||||
value class ZecRequestMessage(val value: String) {
|
||||
value class ZecRequestMessage(
|
||||
val value: String
|
||||
) {
|
||||
init {
|
||||
require(value.length <= MAX_MESSAGE_LENGTH)
|
||||
}
|
||||
|
|
|
@ -13,9 +13,7 @@ object AndroidApiVersion {
|
|||
@ChecksSdkIntAtLeast(parameter = 0)
|
||||
private fun isAtLeast(
|
||||
@IntRange(from = Build.VERSION_CODES.BASE.toLong()) sdk: Int
|
||||
): Boolean {
|
||||
return Build.VERSION.SDK_INT >= sdk
|
||||
}
|
||||
): Boolean = Build.VERSION.SDK_INT >= sdk
|
||||
|
||||
/**
|
||||
* @param sdk SDK version number to test against the current environment.
|
||||
|
@ -23,9 +21,7 @@ object AndroidApiVersion {
|
|||
*/
|
||||
private fun isExactly(
|
||||
@IntRange(from = Build.VERSION_CODES.BASE.toLong()) sdk: Int
|
||||
): Boolean {
|
||||
return Build.VERSION.SDK_INT == sdk
|
||||
}
|
||||
): Boolean = Build.VERSION.SDK_INT == sdk
|
||||
|
||||
val isExactlyO = isExactly(Build.VERSION_CODES.O_MR1)
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@ import kotlinx.coroutines.launch
|
|||
* @param broadcastReceiverScope Scope for performing asynchronous work in the broadcast receiver.
|
||||
* It is not recommended to cancel this scope.
|
||||
*/
|
||||
abstract class CoroutineBroadcastReceiver(private val broadcastReceiverScope: CoroutineScope) : BroadcastReceiver() {
|
||||
abstract class CoroutineBroadcastReceiver(
|
||||
private val broadcastReceiverScope: CoroutineScope
|
||||
) : BroadcastReceiver() {
|
||||
final override fun onReceive(
|
||||
context: Context,
|
||||
intent: Intent
|
||||
|
|
|
@ -13,40 +13,44 @@ object StrictModeCompat {
|
|||
StrictMode.enableDefaults()
|
||||
|
||||
StrictMode.setThreadPolicy(
|
||||
StrictMode.ThreadPolicy.Builder().apply {
|
||||
detectAll()
|
||||
if (isCrashOnViolation) {
|
||||
penaltyDeath()
|
||||
} else {
|
||||
penaltyLog()
|
||||
}
|
||||
}.build()
|
||||
StrictMode.ThreadPolicy
|
||||
.Builder()
|
||||
.apply {
|
||||
detectAll()
|
||||
if (isCrashOnViolation) {
|
||||
penaltyDeath()
|
||||
} else {
|
||||
penaltyLog()
|
||||
}
|
||||
}.build()
|
||||
)
|
||||
|
||||
// Don't enable missing network tags, because those are noisy.
|
||||
StrictMode.setVmPolicy(
|
||||
StrictMode.VmPolicy.Builder().apply {
|
||||
if (AndroidApiVersion.isAtLeastS) {
|
||||
detectUnsafeIntentLaunch()
|
||||
}
|
||||
detectActivityLeaks()
|
||||
detectCleartextNetwork()
|
||||
detectContentUriWithoutPermission()
|
||||
detectFileUriExposure()
|
||||
detectLeakedClosableObjects()
|
||||
detectLeakedRegistrationObjects()
|
||||
detectLeakedSqlLiteObjects()
|
||||
if (AndroidApiVersion.isAtLeastP) {
|
||||
// Disable because this is mostly flagging Android X and Play Services
|
||||
// builder.detectNonSdkApiUsage();
|
||||
}
|
||||
StrictMode.VmPolicy
|
||||
.Builder()
|
||||
.apply {
|
||||
if (AndroidApiVersion.isAtLeastS) {
|
||||
detectUnsafeIntentLaunch()
|
||||
}
|
||||
detectActivityLeaks()
|
||||
detectCleartextNetwork()
|
||||
detectContentUriWithoutPermission()
|
||||
detectFileUriExposure()
|
||||
detectLeakedClosableObjects()
|
||||
detectLeakedRegistrationObjects()
|
||||
detectLeakedSqlLiteObjects()
|
||||
if (AndroidApiVersion.isAtLeastP) {
|
||||
// Disable because this is mostly flagging Android X and Play Services
|
||||
// builder.detectNonSdkApiUsage();
|
||||
}
|
||||
|
||||
if (isCrashOnViolation) {
|
||||
penaltyDeath()
|
||||
} else {
|
||||
penaltyLog()
|
||||
}
|
||||
}.build()
|
||||
if (isCrashOnViolation) {
|
||||
penaltyDeath()
|
||||
} else {
|
||||
penaltyLog()
|
||||
}
|
||||
}.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,37 +50,27 @@ open class AbstractProcessNameContentProvider : ContentProvider() {
|
|||
selection: String?,
|
||||
selectionArgs: Array<out String>?,
|
||||
sortOrder: String?
|
||||
): Cursor? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
): Cursor? = throw UnsupportedOperationException()
|
||||
|
||||
override fun getType(uri: Uri): String? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
override fun getType(uri: Uri): String? = throw UnsupportedOperationException()
|
||||
|
||||
override fun insert(
|
||||
uri: Uri,
|
||||
values: ContentValues?
|
||||
): Uri? {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
): Uri? = throw UnsupportedOperationException()
|
||||
|
||||
override fun delete(
|
||||
uri: Uri,
|
||||
selection: String?,
|
||||
selectionArgs: Array<out String>?
|
||||
): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
): Int = throw UnsupportedOperationException()
|
||||
|
||||
override fun update(
|
||||
uri: Uri,
|
||||
values: ContentValues?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<out String>?
|
||||
): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
): Int = throw UnsupportedOperationException()
|
||||
|
||||
companion object {
|
||||
internal fun getProcessNameLegacy(
|
||||
|
|
|
@ -54,15 +54,14 @@ object ProcessNameCompat {
|
|||
* @return Name of the current process. May return null if a failure occurs, which is possible
|
||||
* due to some race conditions in Android.
|
||||
*/
|
||||
private fun searchForProcessName(context: Context): String? {
|
||||
return if (AndroidApiVersion.isAtLeastTiramisu) {
|
||||
private fun searchForProcessName(context: Context): String? =
|
||||
if (AndroidApiVersion.isAtLeastTiramisu) {
|
||||
getProcessNameTPlus()
|
||||
} else if (AndroidApiVersion.isAtLeastP) {
|
||||
getProcessNamePPlus()
|
||||
} else {
|
||||
searchForProcessNameLegacy(context)
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
|
||||
private fun getProcessNameTPlus() = Process.myProcessName()
|
||||
|
|
|
@ -5,7 +5,9 @@ package co.electriccoin.zcash.spackle
|
|||
*
|
||||
* This class is thread-safe.
|
||||
*/
|
||||
class LazyWithArgument<in Input, out Output>(private val deferredCreator: ((Input) -> Output)) {
|
||||
class LazyWithArgument<in Input, out Output>(
|
||||
private val deferredCreator: ((Input) -> Output)
|
||||
) {
|
||||
@Volatile
|
||||
private var singletonInstance: Output? = null
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ import kotlinx.coroutines.sync.withLock
|
|||
*
|
||||
* This class is thread-safe.
|
||||
*/
|
||||
class SuspendingLazy<in Input, out Output>(private val deferredCreator: suspend ((Input) -> Output)) {
|
||||
class SuspendingLazy<in Input, out Output>(
|
||||
private val deferredCreator: suspend ((Input) -> Output)
|
||||
) {
|
||||
private var singletonInstance: Output? = null
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
|
|
@ -6,7 +6,9 @@ package co.electriccoin.zcash.spackle.model
|
|||
* @param value A 0-based index. Must be >= 0
|
||||
*/
|
||||
@JvmInline
|
||||
value class Index(val value: Int) {
|
||||
value class Index(
|
||||
val value: Int
|
||||
) {
|
||||
init {
|
||||
require(value >= 0) { "Index must be >= 0 but actually is $value" }
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package co.electriccoin.zcash.spackle.model
|
||||
|
||||
data class Progress(val current: Index, val last: Index) {
|
||||
data class Progress(
|
||||
val current: Index,
|
||||
val last: Index
|
||||
) {
|
||||
init {
|
||||
require(last.value > 0) { "last must be > 0 but was $last" }
|
||||
require(last.value >= current.value) { "last ($last) must be >= current ($current)" }
|
||||
|
|
|
@ -28,7 +28,8 @@ open class UiTestPrerequisites {
|
|||
|
||||
private fun isScreenOn(): Boolean {
|
||||
val powerService =
|
||||
ApplicationProvider.getApplicationContext<Context>()
|
||||
ApplicationProvider
|
||||
.getApplicationContext<Context>()
|
||||
.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
return powerService.isInteractive
|
||||
}
|
||||
|
@ -41,7 +42,8 @@ open class UiTestPrerequisites {
|
|||
|
||||
private fun isKeyguardLocked(): Boolean {
|
||||
val keyguardService = (
|
||||
ApplicationProvider.getApplicationContext<Context>()
|
||||
ApplicationProvider
|
||||
.getApplicationContext<Context>()
|
||||
.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
)
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ open class ZcashUiTestRunner : AndroidJUnitRunner() {
|
|||
super.onCreate(arguments)
|
||||
|
||||
val powerManager =
|
||||
ApplicationProvider.getApplicationContext<Context>()
|
||||
ApplicationProvider
|
||||
.getApplicationContext<Context>()
|
||||
.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||
|
||||
// There is no alternative to this deprecated API. The suggestion of a view to keep the screen
|
||||
|
|
|
@ -4,4 +4,5 @@ root = true
|
|||
ktlint_standard_trailing-comma-on-call-site = disabled
|
||||
ktlint_standard_trailing-comma-on-declaration-site = disabled
|
||||
# When using Compose, suppress the `function-naming` rule in favor of PascalCase naming convention
|
||||
ktlint_function_naming_ignore_when_annotated_with=Composable
|
||||
ktlint_function_naming_ignore_when_annotated_with=Composable
|
||||
ktlint_standard_function-signature=disabled
|
|
@ -119,17 +119,18 @@ private fun splitBalance(balanceStringParts: ZecAmountTriple): Pair<String, Stri
|
|||
Twig.debug { "Balance parts before calculation: $balanceStringParts" }
|
||||
|
||||
val cutPosition =
|
||||
balanceStringParts.main.indexOf(
|
||||
startIndex = 0,
|
||||
char = MonetarySeparators.current(Locale.getDefault()).decimal,
|
||||
ignoreCase = true
|
||||
).let { separatorPosition ->
|
||||
if (separatorPosition + CUT_POSITION_OFFSET < balanceStringParts.main.length) {
|
||||
separatorPosition + CUT_POSITION_OFFSET
|
||||
} else {
|
||||
balanceStringParts.main.length
|
||||
balanceStringParts.main
|
||||
.indexOf(
|
||||
startIndex = 0,
|
||||
char = MonetarySeparators.current(Locale.getDefault()).decimal,
|
||||
ignoreCase = true
|
||||
).let { separatorPosition ->
|
||||
if (separatorPosition + CUT_POSITION_OFFSET < balanceStringParts.main.length) {
|
||||
separatorPosition + CUT_POSITION_OFFSET
|
||||
} else {
|
||||
balanceStringParts.main.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val firstPart =
|
||||
buildString {
|
||||
|
@ -160,7 +161,10 @@ data class ZecAmountTriple(
|
|||
)
|
||||
|
||||
@Immutable
|
||||
data class BalanceTextStyle(val mostSignificantPart: TextStyle, val leastSignificantPart: TextStyle)
|
||||
data class BalanceTextStyle(
|
||||
val mostSignificantPart: TextStyle,
|
||||
val leastSignificantPart: TextStyle
|
||||
)
|
||||
|
||||
object StyledBalanceDefaults {
|
||||
@Stable
|
||||
|
|
|
@ -103,8 +103,7 @@ fun LabeledCheckBox(
|
|||
top = ZcashTheme.dimens.spacingTiny,
|
||||
bottom = ZcashTheme.dimens.spacingTiny,
|
||||
end = ZcashTheme.dimens.spacingTiny
|
||||
)
|
||||
.then(
|
||||
).then(
|
||||
if (checkBoxTestTag != null) {
|
||||
Modifier.testTag(checkBoxTestTag)
|
||||
} else {
|
||||
|
|
|
@ -120,8 +120,7 @@ fun ChipOnSurface(
|
|||
color = ZcashTheme.colors.layoutStrokeSecondary
|
||||
),
|
||||
shape = RoundedCornerShape(size = ZcashTheme.dimens.regularRippleEffectCorner),
|
||||
)
|
||||
.clickable { onClick() },
|
||||
).clickable { onClick() },
|
||||
color = ZcashTheme.colors.primaryColor,
|
||||
shadowElevation = ZcashTheme.dimens.chipShadowElevation,
|
||||
) {
|
||||
|
@ -134,8 +133,7 @@ fun ChipOnSurface(
|
|||
.padding(
|
||||
vertical = ZcashTheme.dimens.spacingMid,
|
||||
horizontal = ZcashTheme.dimens.spacingDefault
|
||||
)
|
||||
.testTag(CommonTag.CHIP)
|
||||
).testTag(CommonTag.CHIP)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,10 @@ fun Override(
|
|||
}
|
||||
}
|
||||
|
||||
data class ConfigurationOverride(val uiMode: UiMode?, val locale: LocaleList?) {
|
||||
data class ConfigurationOverride(
|
||||
val uiMode: UiMode?,
|
||||
val locale: LocaleList?
|
||||
) {
|
||||
fun newConfiguration(fromConfiguration: Configuration) =
|
||||
Configuration(fromConfiguration).apply {
|
||||
this@ConfigurationOverride.uiMode?.let {
|
||||
|
|
|
@ -104,8 +104,7 @@ private fun PagerTab(
|
|||
.fillMaxSize()
|
||||
.background(
|
||||
if (selected) Color.Transparent else ZcashTheme.colors.layoutStroke
|
||||
)
|
||||
.padding(vertical = ZcashTheme.dimens.spacingMid, horizontal = ZcashTheme.dimens.spacingXtiny),
|
||||
).padding(vertical = ZcashTheme.dimens.spacingMid, horizontal = ZcashTheme.dimens.spacingXtiny),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
|
|
|
@ -8,12 +8,11 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||
import kotlinx.coroutines.flow.update
|
||||
|
||||
sealed class ScreenBrightnessState {
|
||||
fun getChange(): ScreenBrightnessState {
|
||||
return when (this) {
|
||||
fun getChange(): ScreenBrightnessState =
|
||||
when (this) {
|
||||
NORMAL -> FULL
|
||||
FULL -> NORMAL
|
||||
}
|
||||
}
|
||||
|
||||
data object FULL : ScreenBrightnessState()
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@ fun SwitchWithLabel(
|
|||
indication = null,
|
||||
role = Role.Switch,
|
||||
onClick = { onStateChange(!state) }
|
||||
)
|
||||
.fillMaxWidth()
|
||||
).fillMaxWidth()
|
||||
) {
|
||||
val (text, spacer, switchButton) = createRefs()
|
||||
Body(
|
||||
|
|
|
@ -90,8 +90,7 @@ fun FormTextField(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.then(
|
||||
}.then(
|
||||
if (withBorder) {
|
||||
Modifier.border(
|
||||
width = 1.dp,
|
||||
|
@ -105,8 +104,7 @@ fun FormTextField(
|
|||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
.then(
|
||||
).then(
|
||||
if (testTag.isNullOrEmpty()) {
|
||||
Modifier
|
||||
} else {
|
||||
|
|
|
@ -32,6 +32,5 @@ internal val TextFieldColors.selectionColors: TextSelectionColors
|
|||
@Composable get() = textSelectionColors
|
||||
|
||||
@Composable
|
||||
internal fun TextFieldColors.cursorColor(isError: Boolean): State<Color> {
|
||||
return rememberUpdatedState(if (isError) errorCursorColor else cursorColor)
|
||||
}
|
||||
internal fun TextFieldColors.cursorColor(isError: Boolean): State<Color> =
|
||||
rememberUpdatedState(if (isError) errorCursorColor else cursorColor)
|
||||
|
|
|
@ -83,8 +83,7 @@ private fun AccountSwitch(state: AccountSwitchState) {
|
|||
onClick =
|
||||
state
|
||||
.onAccountTypeClick
|
||||
)
|
||||
.padding(start = 4.dp),
|
||||
).padding(start = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
|
|
|
@ -163,8 +163,7 @@ private fun FullscreenDialogContent(
|
|||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = null,
|
||||
onClick = onBack
|
||||
)
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 64.dp)
|
||||
).padding(start = 16.dp, end = 16.dp, bottom = 64.dp)
|
||||
) {
|
||||
ZashiQrInternal(
|
||||
modifier =
|
||||
|
|
|
@ -60,8 +60,7 @@ fun RadioButton(
|
|||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = state.onClick,
|
||||
role = Role.Button,
|
||||
)
|
||||
.padding(horizontal = 20.dp)
|
||||
).padding(horizontal = 20.dp)
|
||||
.then(
|
||||
if (testTag != null) {
|
||||
Modifier.testTag(testTag)
|
||||
|
|
|
@ -260,17 +260,13 @@ object ZashiListItemDefaults {
|
|||
fun primaryColors(
|
||||
borderColor: Color = Color.Unspecified,
|
||||
backgroundColor: Color = Color.Transparent
|
||||
): ZashiListItemColors {
|
||||
return ZashiListItemColors(borderColor = borderColor, backgroundColor = backgroundColor)
|
||||
}
|
||||
): ZashiListItemColors = ZashiListItemColors(borderColor = borderColor, backgroundColor = backgroundColor)
|
||||
|
||||
@Composable
|
||||
fun secondaryColors(
|
||||
borderColor: Color = ZashiColors.Surfaces.strokeSecondary,
|
||||
backgroundColor: Color = Color.Transparent
|
||||
): ZashiListItemColors {
|
||||
return ZashiListItemColors(borderColor = borderColor, backgroundColor = backgroundColor)
|
||||
}
|
||||
): ZashiListItemColors = ZashiListItemColors(borderColor = borderColor, backgroundColor = backgroundColor)
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
|
|
|
@ -14,7 +14,8 @@ object AndroidQrCodeImageGenerator : QrCodeImageGenerator {
|
|||
): ImageBitmap {
|
||||
val colorArray = bitArray.toThemeColorArray(colors)
|
||||
|
||||
return Bitmap.createBitmap(colorArray, sizePixels, sizePixels, Bitmap.Config.ARGB_8888)
|
||||
return Bitmap
|
||||
.createBitmap(colorArray, sizePixels, sizePixels, Bitmap.Config.ARGB_8888)
|
||||
.asImageBitmap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ sealed interface ImageResource {
|
|||
|
||||
@JvmInline
|
||||
@Immutable
|
||||
value class DisplayString(val value: String) : ImageResource
|
||||
value class DisplayString(
|
||||
val value: String
|
||||
) : ImageResource
|
||||
}
|
||||
|
||||
@Stable
|
||||
|
|
|
@ -62,15 +62,13 @@ data class ScreenHeight(
|
|||
val systemStatusBarHeight: Dp,
|
||||
val systemNavigationBarHeight: Dp
|
||||
) {
|
||||
fun overallScreenHeight(): Dp {
|
||||
return (contentHeight + systemBarsHeight()).also {
|
||||
fun overallScreenHeight(): Dp =
|
||||
(contentHeight + systemBarsHeight()).also {
|
||||
Twig.debug { "Screen height: Overall height: $it" }
|
||||
}
|
||||
}
|
||||
|
||||
fun systemBarsHeight(): Dp {
|
||||
return (systemStatusBarHeight + systemNavigationBarHeight).also {
|
||||
fun systemBarsHeight(): Dp =
|
||||
(systemStatusBarHeight + systemNavigationBarHeight).also {
|
||||
Twig.debug { "Screen height: System bars height: $it" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,22 +26,37 @@ sealed interface StringResource {
|
|||
|
||||
@JvmInline
|
||||
@Immutable
|
||||
value class ByString(val value: String) : StringResource
|
||||
value class ByString(
|
||||
val value: String
|
||||
) : StringResource
|
||||
|
||||
@Immutable
|
||||
data class ByZatoshi(val zatoshi: Zatoshi) : StringResource
|
||||
data class ByZatoshi(
|
||||
val zatoshi: Zatoshi
|
||||
) : StringResource
|
||||
|
||||
@Immutable
|
||||
data class ByDateTime(val zonedDateTime: ZonedDateTime, val useFullFormat: Boolean) : StringResource
|
||||
data class ByDateTime(
|
||||
val zonedDateTime: ZonedDateTime,
|
||||
val useFullFormat: Boolean
|
||||
) : StringResource
|
||||
|
||||
@Immutable
|
||||
data class ByYearMonth(val yearMonth: YearMonth) : StringResource
|
||||
data class ByYearMonth(
|
||||
val yearMonth: YearMonth
|
||||
) : StringResource
|
||||
|
||||
@Immutable
|
||||
data class ByTransactionId(val transactionId: String, val abbreviated: Boolean) : StringResource
|
||||
data class ByTransactionId(
|
||||
val transactionId: String,
|
||||
val abbreviated: Boolean
|
||||
) : StringResource
|
||||
|
||||
@Immutable
|
||||
data class ByAddress(val address: String, val abbreviated: Boolean) : StringResource
|
||||
data class ByAddress(
|
||||
val address: String,
|
||||
val abbreviated: Boolean
|
||||
) : StringResource
|
||||
}
|
||||
|
||||
@Stable
|
||||
|
@ -142,15 +157,28 @@ object StringResourceDefaults {
|
|||
.getDateTimeInstance(
|
||||
DateFormat.MEDIUM,
|
||||
DateFormat.SHORT,
|
||||
).format(
|
||||
Date.from(
|
||||
res.zonedDateTime
|
||||
.toInstant()
|
||||
.toKotlinInstant()
|
||||
.toJavaInstant()
|
||||
)
|
||||
)
|
||||
.format(Date.from(res.zonedDateTime.toInstant().toKotlinInstant().toJavaInstant()))
|
||||
} else {
|
||||
val pattern = DateTimeFormatter.ofPattern("MMM dd")
|
||||
val start = res.zonedDateTime.format(pattern).orEmpty()
|
||||
val end =
|
||||
DateFormat
|
||||
.getTimeInstance(DateFormat.SHORT)
|
||||
.format(Date.from(res.zonedDateTime.toInstant().toKotlinInstant().toJavaInstant()))
|
||||
.format(
|
||||
Date.from(
|
||||
res.zonedDateTime
|
||||
.toInstant()
|
||||
.toKotlinInstant()
|
||||
.toJavaInstant()
|
||||
)
|
||||
)
|
||||
|
||||
return "$start $end"
|
||||
}
|
||||
|
@ -161,23 +189,21 @@ object StringResourceDefaults {
|
|||
return yearMonth.format(pattern).orEmpty()
|
||||
}
|
||||
|
||||
fun convertAddress(res: StringResource.ByAddress): String {
|
||||
return if (res.abbreviated && res.address.isNotBlank()) {
|
||||
fun convertAddress(res: StringResource.ByAddress): String =
|
||||
if (res.abbreviated && res.address.isNotBlank()) {
|
||||
"${res.address.take(ADDRESS_MAX_LENGTH_ABBREVIATED)}..."
|
||||
} else {
|
||||
res.address
|
||||
}
|
||||
}
|
||||
|
||||
fun convertTransactionId(res: StringResource.ByTransactionId): String {
|
||||
return if (res.abbreviated) {
|
||||
fun convertTransactionId(res: StringResource.ByTransactionId): String =
|
||||
if (res.abbreviated) {
|
||||
"${res.transactionId.take(TRANSACTION_MAX_PREFIX_SUFFIX_LENGHT)}...${res.transactionId.takeLast(
|
||||
TRANSACTION_MAX_PREFIX_SUFFIX_LENGHT
|
||||
)}"
|
||||
} else {
|
||||
res.transactionId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val TRANSACTION_MAX_PREFIX_SUFFIX_LENGHT = 5
|
||||
|
|
|
@ -101,14 +101,15 @@ class ScanViewTest : UiTestPrerequisites() {
|
|||
it.assertDoesNotExist()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
getStringResourceWithArgs(
|
||||
resId = R.string.scan_state_permission,
|
||||
getStringResource(R.string.app_name)
|
||||
)
|
||||
).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
getStringResourceWithArgs(
|
||||
resId = R.string.scan_state_permission,
|
||||
getStringResource(R.string.app_name)
|
||||
)
|
||||
).also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.scan_settings_button), ignoreCase = true).also {
|
||||
it.assertIsDisplayed()
|
||||
|
|
|
@ -37,7 +37,9 @@ class ScreenBrightnessTest : UiTestPrerequisites() {
|
|||
assertEquals(ScreenBrightnessState.NORMAL, testSetup.getSecureBrightnessCount())
|
||||
}
|
||||
|
||||
private class TestSetup(composeTestRule: ComposeContentTestRule) {
|
||||
private class TestSetup(
|
||||
composeTestRule: ComposeContentTestRule
|
||||
) {
|
||||
val mutableScreenBrightnessFlag = MutableStateFlow(true)
|
||||
|
||||
private val screenBrightness = ScreenBrightness
|
||||
|
|
|
@ -58,7 +58,9 @@ class ScreenSecurityTest : UiTestPrerequisites() {
|
|||
assertEquals(0, testSetup.getSecureScreenCount())
|
||||
}
|
||||
|
||||
private class TestSetup(composeTestRule: ComposeContentTestRule) {
|
||||
private class TestSetup(
|
||||
composeTestRule: ComposeContentTestRule
|
||||
) {
|
||||
val mutableSecureScreenFlag = MutableStateFlow(true)
|
||||
|
||||
private val screenSecurity = ScreenSecurity()
|
||||
|
|
|
@ -38,7 +38,9 @@ class ScreenTimeoutTest : UiTestPrerequisites() {
|
|||
assertEquals(0, testSetup.getScreenTimeoutCount())
|
||||
}
|
||||
|
||||
private class TestSetup(composeTestRule: ComposeContentTestRule) {
|
||||
private class TestSetup(
|
||||
composeTestRule: ComposeContentTestRule
|
||||
) {
|
||||
val mutableScreenTimeoutFlag = MutableStateFlow(true)
|
||||
|
||||
private val screenTimeout = ScreenTimeout()
|
||||
|
|
|
@ -13,7 +13,8 @@ class ConfigurationEntriesTest {
|
|||
fun keys_unique() {
|
||||
val fieldValueSet = mutableSetOf<String>()
|
||||
|
||||
ConfigurationEntries::class.memberProperties
|
||||
ConfigurationEntries::class
|
||||
.memberProperties
|
||||
.map { it.getter.call(ConfigurationEntries) }
|
||||
.map { it as DefaultEntry<*> }
|
||||
.map { it.key }
|
||||
|
|
|
@ -13,7 +13,8 @@ class StandardPreferenceKeysTest {
|
|||
fun unique_keys() {
|
||||
val fieldValueSet = mutableSetOf<String>()
|
||||
|
||||
StandardPreferenceKeys::class.memberProperties
|
||||
StandardPreferenceKeys::class
|
||||
.memberProperties
|
||||
.map { it.getter.call(StandardPreferenceKeys) }
|
||||
.map { it as PreferenceDefault<*> }
|
||||
.map { it.key }
|
||||
|
|
|
@ -30,8 +30,7 @@ class AboutViewTest {
|
|||
.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description),
|
||||
ignoreCase = true
|
||||
)
|
||||
.also {
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
|
@ -60,11 +59,12 @@ class AboutViewTest {
|
|||
|
||||
assertEquals(0, testSetup.getOnBackCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnBackCount())
|
||||
}
|
||||
|
|
|
@ -28,7 +28,8 @@ class AccountViewTest : UiTestPrerequisites() {
|
|||
fun check_all_elementary_ui_elements_displayed() {
|
||||
newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithTag(CommonTag.TOP_APP_BAR)
|
||||
composeTestRule
|
||||
.onNodeWithTag(CommonTag.TOP_APP_BAR)
|
||||
.also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ class BalancesViewTest : UiTestPrerequisites() {
|
|||
fun check_all_elementary_ui_elements_displayed() {
|
||||
newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithTag(CommonTag.TOP_APP_BAR)
|
||||
composeTestRule
|
||||
.onNodeWithTag(CommonTag.TOP_APP_BAR)
|
||||
.also {
|
||||
it.assertIsDisplayed()
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
|||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class ExportPrivateDataViewTestSetup(private val composeTestRule: ComposeContentTestRule) {
|
||||
class ExportPrivateDataViewTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule
|
||||
) {
|
||||
private val onBackCount = AtomicInteger(0)
|
||||
|
||||
private val onAgree = AtomicBoolean(false)
|
||||
|
|
|
@ -19,34 +19,35 @@ class OnboardingViewTest : UiTestPrerequisites() {
|
|||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private fun newTestSetup(): OnboardingTestSetup {
|
||||
return OnboardingTestSetup(composeTestRule).apply {
|
||||
private fun newTestSetup(): OnboardingTestSetup =
|
||||
OnboardingTestSetup(composeTestRule).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@MediumTest
|
||||
fun layout() {
|
||||
newTestSetup()
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.onboarding_create_new_wallet),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.onboarding_create_new_wallet),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.onboarding_import_existing_wallet),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.onboarding_import_existing_wallet),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
it.assertIsEnabled()
|
||||
it.assertHasClickAction()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -32,11 +32,12 @@ class ReceiveViewTest {
|
|||
newTestSetup()
|
||||
|
||||
// Enable substring for ellipsizing
|
||||
composeTestRule.onNodeWithText(
|
||||
text = "${WalletAddressFixture.UNIFIED_ADDRESS_STRING.take(20)}...",
|
||||
substring = true,
|
||||
useUnmergedTree = true
|
||||
).assertExists()
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = "${WalletAddressFixture.UNIFIED_ADDRESS_STRING.take(20)}...",
|
||||
substring = true,
|
||||
useUnmergedTree = true
|
||||
).assertExists()
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -47,11 +48,12 @@ class ReceiveViewTest {
|
|||
|
||||
assertEquals(0, testSetup.getOnSettingsCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(
|
||||
getStringResource(R.string.settings_menu_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
getStringResource(R.string.settings_menu_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnSettingsCount())
|
||||
}
|
||||
|
|
|
@ -34,7 +34,9 @@ class RestoreViewSecuredScreenTest : UiTestPrerequisites() {
|
|||
assertEquals(1, testSetup.getSecureScreenCount())
|
||||
}
|
||||
|
||||
private class TestSetup(composeTestRule: ComposeContentTestRule) {
|
||||
private class TestSetup(
|
||||
composeTestRule: ComposeContentTestRule
|
||||
) {
|
||||
private val screenSecurity = ScreenSecurity()
|
||||
|
||||
fun getSecureScreenCount() = screenSecurity.referenceCount.value
|
||||
|
|
|
@ -61,17 +61,19 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
it.assertTextContains("ab")
|
||||
}
|
||||
|
||||
composeTestRule.onNode(
|
||||
matcher = hasText("abandon", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
composeTestRule
|
||||
.onNode(
|
||||
matcher = hasText("abandon", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
composeTestRule.onNode(
|
||||
matcher = hasText("able", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
composeTestRule
|
||||
.onNode(
|
||||
matcher = hasText("able", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -83,11 +85,12 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
it.performTextInput("ab")
|
||||
}
|
||||
|
||||
composeTestRule.onNode(
|
||||
matcher = hasText("abandon", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNode(
|
||||
matcher = hasText("abandon", substring = true) and hasTestTag(RestoreTag.AUTOCOMPLETE_ITEM)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag(RestoreTag.AUTOCOMPLETE_LAYOUT).also {
|
||||
it.assertDoesNotExist()
|
||||
|
@ -119,7 +122,8 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
it.assertDoesNotExist()
|
||||
}
|
||||
|
||||
composeTestRule.onNode(matcher = hasText(text = "abandon", substring = true))
|
||||
composeTestRule
|
||||
.onNode(matcher = hasText(text = "abandon", substring = true))
|
||||
.also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
@ -130,12 +134,13 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
fun seed_invalid_phrase_does_not_progress() {
|
||||
newTestSetup(initialWordsList = generateSequence { "abandon" }.take(SeedPhrase.SEED_PHRASE_SIZE).toList())
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_seed_button_next),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsNotEnabled()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_seed_button_next),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsNotEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -146,12 +151,13 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
|
||||
newTestSetup(initialWordsList = SeedPhraseFixture.new().split)
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_seed_button_next),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_seed_button_next),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -159,23 +165,25 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
fun seed_clear() {
|
||||
newTestSetup(initialWordsList = listOf("abandon"))
|
||||
|
||||
composeTestRule.onNode(
|
||||
matcher = hasText(text = "abandon", substring = true),
|
||||
useUnmergedTree = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
composeTestRule
|
||||
.onNode(
|
||||
matcher = hasText(text = "abandon", substring = true),
|
||||
useUnmergedTree = true
|
||||
).also {
|
||||
it.assertExists()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(getStringResource(R.string.restore_button_clear)).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
composeTestRule.onNode(
|
||||
matcher = hasText("abandon", substring = true) and hasTestTag(CommonTag.CHIP),
|
||||
useUnmergedTree = true
|
||||
).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
composeTestRule
|
||||
.onNode(
|
||||
matcher = hasText("abandon", substring = true) and hasTestTag(CommonTag.CHIP),
|
||||
useUnmergedTree = true
|
||||
).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -187,14 +195,15 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
initialWordsList = SeedPhraseFixture.new().split
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performScrollTo()
|
||||
it.assertIsEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performScrollTo()
|
||||
it.assertIsEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(testSetup.getRestoreHeight(), null)
|
||||
assertEquals(1, testSetup.getOnFinishedCount())
|
||||
|
@ -210,16 +219,20 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
)
|
||||
|
||||
composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also {
|
||||
it.performTextInput(ZcashNetwork.Mainnet.saplingActivationHeight.value.toString())
|
||||
it.performTextInput(
|
||||
ZcashNetwork.Mainnet.saplingActivationHeight.value
|
||||
.toString()
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(testSetup.getRestoreHeight(), ZcashNetwork.Mainnet.saplingActivationHeight)
|
||||
assertEquals(1, testSetup.getOnFinishedCount())
|
||||
|
@ -234,24 +247,26 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
initialWordsList = SeedPhraseFixture.new().split
|
||||
)
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsEnabled()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsEnabled()
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also {
|
||||
it.performTextInput((ZcashNetwork.Mainnet.saplingActivationHeight.value - 1L).toString())
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsNotEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsNotEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertNull(testSetup.getRestoreHeight())
|
||||
assertEquals(0, testSetup.getOnFinishedCount())
|
||||
|
@ -270,13 +285,14 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
it.performTextInput("1.2")
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsNotEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertIsNotEnabled()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertNull(testSetup.getRestoreHeight())
|
||||
assertEquals(0, testSetup.getOnFinishedCount())
|
||||
|
@ -293,12 +309,13 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getOnFinishedCount())
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.restore_birthday_button_restore),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnFinishedCount())
|
||||
}
|
||||
|
@ -310,11 +327,12 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getOnBackCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getOnBackCount())
|
||||
}
|
||||
|
@ -330,11 +348,12 @@ class RestoreViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getOnBackCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
// There appears to be a bug introduced in Compose 1.4.0 which makes this necessary
|
||||
composeTestRule.mainClock.autoAdvance = false
|
||||
|
|
|
@ -67,12 +67,13 @@ class ScanViewBasicTest : UiTestPrerequisites() {
|
|||
|
||||
// Permission denied ui items (not visible):
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.scan_settings_button),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.scan_settings_button),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.assertDoesNotExist()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -7,7 +7,9 @@ import co.electriccoin.zcash.ui.fixture.VersionInfoFixture
|
|||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class SecurityWarningViewTestSetup(private val composeTestRule: ComposeContentTestRule) {
|
||||
class SecurityWarningViewTestSetup(
|
||||
private val composeTestRule: ComposeContentTestRule
|
||||
) {
|
||||
private val onBackCount = AtomicInteger(0)
|
||||
|
||||
private val onAcknowledged = AtomicBoolean(false)
|
||||
|
|
|
@ -29,11 +29,12 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getBackCount())
|
||||
|
||||
composeTestRule.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithContentDescription(
|
||||
getStringResource(R.string.back_navigation_content_description)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getBackCount())
|
||||
}
|
||||
|
@ -45,12 +46,13 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getFeedbackCount())
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.settings_feedback),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.settings_feedback),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getFeedbackCount())
|
||||
}
|
||||
|
@ -62,12 +64,13 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getAdvancedSettingsCount())
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.settings_advanced_settings),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.settings_advanced_settings),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getAdvancedSettingsCount())
|
||||
}
|
||||
|
@ -79,13 +82,14 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
assertEquals(0, testSetup.getAboutCount())
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
text = getStringResource(R.string.settings_about_us),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
text = getStringResource(R.string.settings_about_us),
|
||||
ignoreCase = true
|
||||
).also {
|
||||
it.performScrollTo()
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getAboutCount())
|
||||
}
|
||||
|
@ -145,11 +149,12 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
composeTestRule.openTroubleshootingMenu()
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
getStringResource(R.string.settings_troubleshooting_enable_background_sync)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
getStringResource(R.string.settings_troubleshooting_enable_background_sync)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getBackgroundSyncCount())
|
||||
}
|
||||
|
@ -168,11 +173,12 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
composeTestRule.openTroubleshootingMenu()
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
getStringResource(R.string.settings_troubleshooting_enable_keep_screen_on)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
getStringResource(R.string.settings_troubleshooting_enable_keep_screen_on)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getKeepScreenOnSyncCount())
|
||||
}
|
||||
|
@ -191,11 +197,12 @@ class SettingsViewTest : UiTestPrerequisites() {
|
|||
|
||||
composeTestRule.openTroubleshootingMenu()
|
||||
|
||||
composeTestRule.onNodeWithText(
|
||||
getStringResource(R.string.settings_troubleshooting_enable_analytics)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
composeTestRule
|
||||
.onNodeWithText(
|
||||
getStringResource(R.string.settings_troubleshooting_enable_analytics)
|
||||
).also {
|
||||
it.performClick()
|
||||
}
|
||||
|
||||
assertEquals(1, testSetup.getAnalyticsCount())
|
||||
}
|
||||
|
|
|
@ -26,7 +26,11 @@ class QrCodeAnalyzerImpl(
|
|||
override fun analyze(image: ImageProxy) {
|
||||
image.use {
|
||||
if (image.format in supportedImageFormats) {
|
||||
val bytes = image.planes.first().buffer.toByteArray()
|
||||
val bytes =
|
||||
image.planes
|
||||
.first()
|
||||
.buffer
|
||||
.toByteArray()
|
||||
|
||||
Twig.verbose {
|
||||
"Scan result: " +
|
||||
|
@ -89,14 +93,15 @@ class QrCodeAnalyzerImpl(
|
|||
|
||||
runCatching {
|
||||
val result =
|
||||
MultiFormatReader().apply {
|
||||
setHints(
|
||||
mapOf(
|
||||
DecodeHintType.POSSIBLE_FORMATS to arrayListOf(BarcodeFormat.QR_CODE),
|
||||
DecodeHintType.ALSO_INVERTED to true
|
||||
MultiFormatReader()
|
||||
.apply {
|
||||
setHints(
|
||||
mapOf(
|
||||
DecodeHintType.POSSIBLE_FORMATS to arrayListOf(BarcodeFormat.QR_CODE),
|
||||
DecodeHintType.ALSO_INVERTED to true
|
||||
)
|
||||
)
|
||||
)
|
||||
}.decodeWithState(binaryBmp)
|
||||
}.decodeWithState(binaryBmp)
|
||||
|
||||
onQrCodeScanned(result.text)
|
||||
}.onFailure {
|
||||
|
|
|
@ -12,8 +12,8 @@ fun WalletCoordinator.Companion.newInstance(
|
|||
context: Context,
|
||||
encryptedPreferenceProvider: EncryptedPreferenceProvider,
|
||||
persistableWalletPreference: PersistableWalletPreferenceDefault
|
||||
): WalletCoordinator {
|
||||
return WalletCoordinator(
|
||||
): WalletCoordinator =
|
||||
WalletCoordinator(
|
||||
context = context,
|
||||
persistableWallet =
|
||||
flow {
|
||||
|
@ -22,6 +22,5 @@ fun WalletCoordinator.Companion.newInstance(
|
|||
accountName = context.getString(R.string.zashi_wallet_name),
|
||||
keySource = ZASHI_KEYSOURCE
|
||||
)
|
||||
}
|
||||
|
||||
private const val ZASHI_KEYSOURCE = "zashi"
|
||||
|
|
|
@ -42,11 +42,11 @@ class BiometricActivity : FragmentActivity() {
|
|||
)
|
||||
|
||||
val promptInfo =
|
||||
BiometricPrompt.PromptInfo.Builder()
|
||||
BiometricPrompt.PromptInfo
|
||||
.Builder()
|
||||
.setTitle(
|
||||
getString(R.string.authentication_system_ui_title, getString(R.string.app_name))
|
||||
)
|
||||
.setSubtitle(subtitle)
|
||||
).setSubtitle(subtitle)
|
||||
.setAllowedAuthenticators(biometricRepository.allowedAuthenticators)
|
||||
.build()
|
||||
|
||||
|
|
|
@ -472,7 +472,11 @@ private fun MainActivity.NavigationHome(
|
|||
|
||||
val isEnoughSpace by storageCheckViewModel.isEnoughSpace.collectAsStateWithLifecycle()
|
||||
|
||||
val sdkStatus = walletViewModel.currentWalletSnapshot.collectAsStateWithLifecycle().value?.status
|
||||
val sdkStatus =
|
||||
walletViewModel.currentWalletSnapshot
|
||||
.collectAsStateWithLifecycle()
|
||||
.value
|
||||
?.status
|
||||
|
||||
val currentAppState = applicationStateProvider.state.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -69,13 +69,21 @@ class NavigationRouterImpl : NavigationRouter {
|
|||
}
|
||||
|
||||
sealed interface NavigationCommand {
|
||||
data class Forward(val route: Any) : NavigationCommand
|
||||
data class Forward(
|
||||
val route: Any
|
||||
) : NavigationCommand
|
||||
|
||||
data class Replace(val route: Any) : NavigationCommand
|
||||
data class Replace(
|
||||
val route: Any
|
||||
) : NavigationCommand
|
||||
|
||||
data class ReplaceAll(val route: Any) : NavigationCommand
|
||||
data class ReplaceAll(
|
||||
val route: Any
|
||||
) : NavigationCommand
|
||||
|
||||
data class NewRoot(val route: Any) : NavigationCommand
|
||||
data class NewRoot(
|
||||
val route: Any
|
||||
) : NavigationCommand
|
||||
|
||||
data object Back : NavigationCommand
|
||||
|
||||
|
|
|
@ -106,8 +106,7 @@ class AccountDataSourceImpl(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
?.flatMapLatest { accountsWithAddresses ->
|
||||
}?.flatMapLatest { accountsWithAddresses ->
|
||||
if (accountsWithAddresses == null) {
|
||||
flowOf(null)
|
||||
} else {
|
||||
|
@ -132,8 +131,7 @@ class AccountDataSourceImpl(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?.retryWhen { _, attempt ->
|
||||
}?.retryWhen { _, attempt ->
|
||||
emit(null)
|
||||
delay(attempt.coerceAtMost(RETRY_DELAY).seconds)
|
||||
true
|
||||
|
@ -189,8 +187,7 @@ class AccountDataSourceImpl(
|
|||
accounts.size == 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
?.sortedDescending()
|
||||
}?.sortedDescending()
|
||||
}.flowOn(Dispatchers.IO)
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
|
@ -202,15 +199,13 @@ class AccountDataSourceImpl(
|
|||
allAccounts
|
||||
.map { account ->
|
||||
account?.firstOrNull { it.isSelected }
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
}.distinctUntilChanged()
|
||||
|
||||
override val zashiAccount: Flow<ZashiAccount?> =
|
||||
allAccounts
|
||||
.map { account ->
|
||||
account?.filterIsInstance<ZashiAccount>()?.firstOrNull()
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
}.distinctUntilChanged()
|
||||
|
||||
override suspend fun getAllAccounts() =
|
||||
withContext(Dispatchers.IO) {
|
||||
|
@ -250,7 +245,8 @@ class AccountDataSourceImpl(
|
|||
index: Long
|
||||
): Account =
|
||||
withContext(Dispatchers.IO) {
|
||||
synchronizerProvider.getSynchronizer()
|
||||
synchronizerProvider
|
||||
.getSynchronizer()
|
||||
.importAccountByUfvk(
|
||||
AccountImportSetup(
|
||||
accountName = context.getString(R.string.keystone_wallet_name),
|
||||
|
|
|
@ -108,7 +108,10 @@ class LocalAddressBookDataSourceImpl(
|
|||
lastUpdated = lastUpdated,
|
||||
version = ADDRESS_BOOK_SERIALIZATION_V1,
|
||||
contacts =
|
||||
addressBook?.contacts.orEmpty().toMutableList()
|
||||
addressBook
|
||||
?.contacts
|
||||
.orEmpty()
|
||||
.toMutableList()
|
||||
.apply {
|
||||
set(
|
||||
indexOf(contact),
|
||||
|
@ -118,8 +121,7 @@ class LocalAddressBookDataSourceImpl(
|
|||
lastUpdated = Clock.System.now()
|
||||
)
|
||||
)
|
||||
}
|
||||
.toList(),
|
||||
}.toList(),
|
||||
).also {
|
||||
addressBook = it
|
||||
}
|
||||
|
@ -138,11 +140,13 @@ class LocalAddressBookDataSourceImpl(
|
|||
lastUpdated = lastUpdated,
|
||||
version = ADDRESS_BOOK_SERIALIZATION_V1,
|
||||
contacts =
|
||||
addressBook?.contacts.orEmpty().toMutableList()
|
||||
addressBook
|
||||
?.contacts
|
||||
.orEmpty()
|
||||
.toMutableList()
|
||||
.apply {
|
||||
remove(addressBookContact)
|
||||
}
|
||||
.toList(),
|
||||
}.toList(),
|
||||
).also {
|
||||
addressBook = it
|
||||
}
|
||||
|
@ -176,7 +180,8 @@ class LocalAddressBookDataSourceImpl(
|
|||
}
|
||||
|
||||
return if (unencryptedFile != null) {
|
||||
addressBookProvider.readLegacyUnencryptedAddressBookFromFile(unencryptedFile)
|
||||
addressBookProvider
|
||||
.readLegacyUnencryptedAddressBookFromFile(unencryptedFile)
|
||||
.also { unencryptedAddressBook ->
|
||||
writeAddressBookToLocalStorage(unencryptedAddressBook, addressBookKey)
|
||||
unencryptedFile.deleteSuspend()
|
||||
|
|
|
@ -167,8 +167,8 @@ class MetadataDataSourceImpl(
|
|||
txId: String,
|
||||
key: MetadataKey,
|
||||
transform: (AnnotationMetadata) -> AnnotationMetadata
|
||||
): Metadata {
|
||||
return updateMetadata(
|
||||
): Metadata =
|
||||
updateMetadata(
|
||||
key = key,
|
||||
transform = { metadata ->
|
||||
metadata.copy(
|
||||
|
@ -184,14 +184,13 @@ class MetadataDataSourceImpl(
|
|||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun updateMetadataBookmark(
|
||||
txId: String,
|
||||
key: MetadataKey,
|
||||
transform: (BookmarkMetadata) -> BookmarkMetadata
|
||||
): Metadata {
|
||||
return updateMetadata(
|
||||
): Metadata =
|
||||
updateMetadata(
|
||||
key = key,
|
||||
transform = { metadata ->
|
||||
metadata.copy(
|
||||
|
@ -207,13 +206,12 @@ class MetadataDataSourceImpl(
|
|||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun updateMetadata(
|
||||
key: MetadataKey,
|
||||
transform: (AccountMetadata) -> AccountMetadata
|
||||
): Metadata {
|
||||
return withContext(Dispatchers.IO) {
|
||||
): Metadata =
|
||||
withContext(Dispatchers.IO) {
|
||||
val metadata = getMetadataInternal(key)
|
||||
|
||||
val accountMetadata = metadata.accountMetadata
|
||||
|
@ -228,7 +226,6 @@ class MetadataDataSourceImpl(
|
|||
|
||||
updatedMetadata
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun defaultAccountMetadata() =
|
||||
|
@ -258,11 +255,11 @@ private fun <T : Any> List<T>.replaceOrAdd(
|
|||
): List<T> {
|
||||
val index = this.indexOfFirst(predicate)
|
||||
return if (index != -1) {
|
||||
this.toMutableList()
|
||||
this
|
||||
.toMutableList()
|
||||
.apply {
|
||||
set(index, transform(this[index]))
|
||||
}
|
||||
.toList()
|
||||
}.toList()
|
||||
} else {
|
||||
this + transform(null)
|
||||
}
|
||||
|
|
|
@ -62,7 +62,9 @@ interface ProposalDataSource {
|
|||
suspend fun redactPcztForSigner(pczt: Pczt): Pczt
|
||||
}
|
||||
|
||||
class TransactionProposalNotCreatedException(reason: Exception) : Exception(reason)
|
||||
class TransactionProposalNotCreatedException(
|
||||
reason: Exception
|
||||
) : Exception(reason)
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class ProposalDataSourceImpl(
|
||||
|
@ -150,7 +152,8 @@ class ProposalDataSourceImpl(
|
|||
|
||||
override suspend fun addProofsToPczt(pczt: Pczt): Pczt =
|
||||
withContext(Dispatchers.IO) {
|
||||
synchronizerProvider.getSynchronizer()
|
||||
synchronizerProvider
|
||||
.getSynchronizer()
|
||||
.addProofsToPczt(pczt)
|
||||
}
|
||||
|
||||
|
@ -178,7 +181,8 @@ class ProposalDataSourceImpl(
|
|||
|
||||
override suspend fun redactPcztForSigner(pczt: Pczt): Pczt =
|
||||
withContext(Dispatchers.IO) {
|
||||
synchronizerProvider.getSynchronizer()
|
||||
synchronizerProvider
|
||||
.getSynchronizer()
|
||||
.redactPcztForSigner(pczt)
|
||||
}
|
||||
|
||||
|
@ -232,13 +236,12 @@ class ProposalDataSourceImpl(
|
|||
}
|
||||
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
private inline fun <T : Any> getOrThrow(block: () -> T): T {
|
||||
return try {
|
||||
private inline fun <T : Any> getOrThrow(block: () -> T): T =
|
||||
try {
|
||||
block()
|
||||
} catch (e: Exception) {
|
||||
throw TransactionProposalNotCreatedException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun AddressType.toWalletAddress(value: String) =
|
||||
when (this) {
|
||||
|
|
|
@ -23,8 +23,8 @@ class TransactionHistoryMapper {
|
|||
data: ListTransactionData,
|
||||
restoreTimestamp: Instant,
|
||||
onTransactionClick: (Transaction) -> Unit
|
||||
): TransactionState {
|
||||
return TransactionState(
|
||||
): TransactionState =
|
||||
TransactionState(
|
||||
key = data.transaction.id.txIdString(),
|
||||
icon = getIcon(data),
|
||||
title = getTitle(data),
|
||||
|
@ -34,7 +34,6 @@ class TransactionHistoryMapper {
|
|||
onClick = { onTransactionClick(data.transaction) },
|
||||
isUnread = isUnread(data, restoreTimestamp)
|
||||
)
|
||||
}
|
||||
|
||||
private fun isUnread(
|
||||
data: ListTransactionData,
|
||||
|
|
|
@ -6,4 +6,7 @@ import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
|
|||
* @property servers an ascended sorted list of fastest servers, or null if none loaded
|
||||
* @property isLoading indicates whether newer data is being loaded
|
||||
*/
|
||||
data class FastestServersState(val servers: List<LightWalletEndpoint>?, val isLoading: Boolean)
|
||||
data class FastestServersState(
|
||||
val servers: List<LightWalletEndpoint>?,
|
||||
val isLoading: Boolean
|
||||
)
|
||||
|
|
|
@ -3,23 +3,31 @@ package co.electriccoin.zcash.ui.common.model
|
|||
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
|
||||
|
||||
sealed interface SubmitResult {
|
||||
data class Success(val txIds: List<String>) : SubmitResult
|
||||
data class Success(
|
||||
val txIds: List<String>
|
||||
) : SubmitResult
|
||||
|
||||
data class MultipleTrxFailure(val results: List<TransactionSubmitResult>) : SubmitResult
|
||||
data class MultipleTrxFailure(
|
||||
val results: List<TransactionSubmitResult>
|
||||
) : SubmitResult
|
||||
|
||||
sealed interface SimpleTrxFailure : SubmitResult {
|
||||
fun toErrorMessage(): String
|
||||
|
||||
fun toErrorStacktrace(): String
|
||||
|
||||
data class SimpleTrxFailureGrpc(val result: TransactionSubmitResult.Failure) : SimpleTrxFailure {
|
||||
data class SimpleTrxFailureGrpc(
|
||||
val result: TransactionSubmitResult.Failure
|
||||
) : SimpleTrxFailure {
|
||||
// Currently, we intentionally do not include any error related details
|
||||
override fun toErrorMessage() = ""
|
||||
|
||||
override fun toErrorStacktrace() = ""
|
||||
}
|
||||
|
||||
data class SimpleTrxFailureSubmit(val result: TransactionSubmitResult.Failure) : SimpleTrxFailure {
|
||||
data class SimpleTrxFailureSubmit(
|
||||
val result: TransactionSubmitResult.Failure
|
||||
) : SimpleTrxFailure {
|
||||
override fun toErrorMessage() =
|
||||
buildString {
|
||||
appendLine("Error code: ${result.code}")
|
||||
|
@ -29,7 +37,9 @@ sealed interface SubmitResult {
|
|||
override fun toErrorStacktrace(): String = toErrorMessage()
|
||||
}
|
||||
|
||||
data class SimpleTrxFailureOther(val error: Throwable) : SimpleTrxFailure {
|
||||
data class SimpleTrxFailureOther(
|
||||
val error: Throwable
|
||||
) : SimpleTrxFailure {
|
||||
override fun toErrorMessage() = error.message ?: "Unknown error"
|
||||
|
||||
override fun toErrorStacktrace(): String = error.stackTraceToString()
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
package co.electriccoin.zcash.ui.common.model
|
||||
|
||||
@JvmInline
|
||||
value class ValidContactName(val value: String)
|
||||
value class ValidContactName(
|
||||
val value: String
|
||||
)
|
||||
|
|
|
@ -49,17 +49,18 @@ data class VersionInfo(
|
|||
)
|
||||
}
|
||||
|
||||
private fun resolveBestReleaseNotes(): String {
|
||||
return if (Locale.getDefault().language.contains("es", ignoreCase = true)) {
|
||||
private fun resolveBestReleaseNotes(): String =
|
||||
if (Locale.getDefault().language.contains("es", ignoreCase = true)) {
|
||||
releaseNotesEs
|
||||
} else {
|
||||
releaseNotesEn
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class DistributionDimension(val value: String) {
|
||||
enum class DistributionDimension(
|
||||
val value: String
|
||||
) {
|
||||
STORE("store"),
|
||||
FOSS("foss")
|
||||
}
|
||||
|
|
|
@ -22,9 +22,7 @@ class AddressBookKeyStorageProviderImpl(
|
|||
) : AddressBookKeyStorageProvider {
|
||||
private val default = AddressBookKeyPreferenceDefault()
|
||||
|
||||
override suspend fun getAddressBookKey(): AddressBookKey? {
|
||||
return default.getValue(encryptedPreferenceProvider())
|
||||
}
|
||||
override suspend fun getAddressBookKey(): AddressBookKey? = default.getValue(encryptedPreferenceProvider())
|
||||
|
||||
override suspend fun storeAddressBookKey(addressBookKey: AddressBookKey) {
|
||||
default.putValue(encryptedPreferenceProvider(), addressBookKey)
|
||||
|
|
|
@ -43,18 +43,16 @@ class AddressBookProviderImpl(
|
|||
override fun readAddressBookFromFile(
|
||||
file: File,
|
||||
addressBookKey: AddressBookKey
|
||||
): AddressBook {
|
||||
return file.inputStream().use { stream ->
|
||||
): AddressBook =
|
||||
file.inputStream().use { stream ->
|
||||
addressBookEncryptor.decrypt(
|
||||
key = addressBookKey,
|
||||
inputStream = stream
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun readLegacyUnencryptedAddressBookFromFile(file: File): AddressBook {
|
||||
return file.inputStream().use { stream ->
|
||||
override fun readLegacyUnencryptedAddressBookFromFile(file: File): AddressBook =
|
||||
file.inputStream().use { stream ->
|
||||
addressBookSerializer.deserializeAddressBook(stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,15 +20,13 @@ interface AddressBookStorageProvider {
|
|||
class AddressBookStorageProviderImpl(
|
||||
private val context: Context
|
||||
) : AddressBookStorageProvider {
|
||||
override fun getStorageFile(addressBookKey: AddressBookKey): File? {
|
||||
return File(getOrCreateAddressBookDir(), addressBookKey.fileIdentifier())
|
||||
override fun getStorageFile(addressBookKey: AddressBookKey): File? =
|
||||
File(getOrCreateAddressBookDir(), addressBookKey.fileIdentifier())
|
||||
.takeIf { it.exists() && it.isFile }
|
||||
}
|
||||
|
||||
override fun getLegacyUnencryptedStorageFile(): File? {
|
||||
return File(context.noBackupFilesDir, LEGACY_UNENCRYPTED_ADDRESS_BOOK_FILE_NAME)
|
||||
override fun getLegacyUnencryptedStorageFile(): File? =
|
||||
File(context.noBackupFilesDir, LEGACY_UNENCRYPTED_ADDRESS_BOOK_FILE_NAME)
|
||||
.takeIf { it.exists() && it.isFile }
|
||||
}
|
||||
|
||||
override fun getOrCreateStorageFile(addressBookKey: AddressBookKey): File {
|
||||
val file = File(getOrCreateAddressBookDir(), addressBookKey.fileIdentifier())
|
||||
|
|
|
@ -20,9 +20,7 @@ class ApplicationStateProviderImpl : ApplicationStateProvider {
|
|||
|
||||
override val state = _state.asStateFlow()
|
||||
|
||||
override suspend fun getApplicationState(): Lifecycle.Event? {
|
||||
return _state.last()
|
||||
}
|
||||
override suspend fun getApplicationState(): Lifecycle.Event? = _state.last()
|
||||
|
||||
override fun setApplicationState(newState: Lifecycle.Event) {
|
||||
_state.update { newState }
|
||||
|
|
|
@ -8,7 +8,9 @@ import co.electriccoin.lightwallet.client.model.LightWalletEndpoint
|
|||
|
||||
// TODO [#1273]: Add ChooseServer Tests #1273
|
||||
// TODO [#1273]: https://github.com/Electric-Coin-Company/zashi-android/issues/1273
|
||||
class GetDefaultServersProvider(private val application: Application) {
|
||||
class GetDefaultServersProvider(
|
||||
private val application: Application
|
||||
) {
|
||||
private val lightWalletEndpoints by lazy {
|
||||
if (ZcashNetwork.fromResources(application) == ZcashNetwork.Mainnet) {
|
||||
listOf(
|
||||
|
|
|
@ -3,6 +3,8 @@ package co.electriccoin.zcash.ui.common.provider
|
|||
import android.app.Application
|
||||
import co.electriccoin.zcash.ui.common.model.VersionInfo
|
||||
|
||||
class GetVersionInfoProvider(private val application: Application) {
|
||||
class GetVersionInfoProvider(
|
||||
private val application: Application
|
||||
) {
|
||||
operator fun invoke() = VersionInfo.new(application)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ package co.electriccoin.zcash.ui.common.provider
|
|||
import android.app.Application
|
||||
import cash.z.ecc.sdk.type.ZcashCurrency
|
||||
|
||||
class GetZcashCurrencyProvider(private val application: Application) {
|
||||
class GetZcashCurrencyProvider(
|
||||
private val application: Application
|
||||
) {
|
||||
operator fun invoke() = ZcashCurrency.fromResources(application)
|
||||
|
||||
fun getLocalizedName() = ZcashCurrency.getLocalizedName(application)
|
||||
|
|
|
@ -25,12 +25,11 @@ class MetadataKeyStorageProviderImpl(
|
|||
) : MetadataKeyStorageProvider {
|
||||
private val default = MetadataKeyPreferenceDefault()
|
||||
|
||||
override suspend fun get(account: WalletAccount): MetadataKey? {
|
||||
return default.getValue(
|
||||
override suspend fun get(account: WalletAccount): MetadataKey? =
|
||||
default.getValue(
|
||||
walletAccount = account,
|
||||
preferenceProvider = encryptedPreferenceProvider(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun store(
|
||||
key: MetadataKey,
|
||||
|
@ -51,11 +50,11 @@ private class MetadataKeyPreferenceDefault {
|
|||
suspend fun getValue(
|
||||
walletAccount: WalletAccount,
|
||||
preferenceProvider: PreferenceProvider,
|
||||
): MetadataKey? {
|
||||
return preferenceProvider.getStringSet(
|
||||
key = getKey(walletAccount)
|
||||
)?.decode()
|
||||
}
|
||||
): MetadataKey? =
|
||||
preferenceProvider
|
||||
.getStringSet(
|
||||
key = getKey(walletAccount)
|
||||
)?.decode()
|
||||
|
||||
suspend fun putValue(
|
||||
newValue: MetadataKey?,
|
||||
|
@ -73,20 +72,19 @@ private class MetadataKeyPreferenceDefault {
|
|||
PreferenceKey("metadata_key_${walletAccount.sdkAccount.accountUuid.value.toHexString()}")
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
private fun MetadataKey?.encode(): Set<String>? {
|
||||
return this
|
||||
private fun MetadataKey?.encode(): Set<String>? =
|
||||
this
|
||||
?.bytes
|
||||
?.map {
|
||||
Base64.encode(it.toByteArray(secretKeyAccess))
|
||||
}
|
||||
?.toSet()
|
||||
}
|
||||
}?.toSet()
|
||||
|
||||
@OptIn(ExperimentalEncodingApi::class)
|
||||
private fun Set<String>?.decode() =
|
||||
if (this != null) {
|
||||
MetadataKey(
|
||||
this.toList()
|
||||
this
|
||||
.toList()
|
||||
.map {
|
||||
SecretBytes.copyFrom(Base64.decode(it), secretKeyAccess)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue