diff --git a/app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt b/app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt index 4e674862..b59f533a 100644 --- a/app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt +++ b/app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt @@ -10,7 +10,6 @@ import org.junit.Assert.assertEquals import org.junit.Test class AndroidApiTest { - @Test @SmallTest fun checkTargetApi() { diff --git a/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt b/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt index 058540a2..91165649 100644 --- a/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt +++ b/app/src/main/java/co/electriccoin/zcash/app/ZcashApplication.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.launch @Suppress("unused") class ZcashApplication : CoroutineApplication() { - override fun onCreate() { super.onCreate() diff --git a/build-conventions-secant/src/main/kotlin/secant.ktlint-conventions.gradle.kts b/build-conventions-secant/src/main/kotlin/secant.ktlint-conventions.gradle.kts index 386fca8b..ae2db55a 100644 --- a/build-conventions-secant/src/main/kotlin/secant.ktlint-conventions.gradle.kts +++ b/build-conventions-secant/src/main/kotlin/secant.ktlint-conventions.gradle.kts @@ -5,7 +5,7 @@ plugins { val ktlint by configurations.creating dependencies { - ktlint("com.pinterest:ktlint:${project.property("KTLINT_VERSION")}") { + ktlint("com.pinterest.ktlint:ktlint-cli:${project.property("KTLINT_VERSION")}") { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) } diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/ConfigurationProvider.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/ConfigurationProvider.kt index 8532aacd..525316c4 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/ConfigurationProvider.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/ConfigurationProvider.kt @@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.Flow * Provides a remote config implementation. */ interface ConfigurationProvider { - /** * @return The configuration if it has been loaded already. If not loaded, returns an empty configuration. */ diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProvider.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProvider.kt index 45e15dd3..a923dcbb 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProvider.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProvider.kt @@ -40,19 +40,28 @@ private data class MergingConfiguration(private val configurations: PersistentLi return null != configurations.firstWithKey(key) } - override fun getBoolean(key: ConfigKey, defaultValue: Boolean): Boolean { + override fun getBoolean( + key: ConfigKey, + defaultValue: Boolean + ): Boolean { return configurations.firstWithKey(key)?.let { return it.getBoolean(key, defaultValue) } ?: defaultValue } - override fun getInt(key: ConfigKey, defaultValue: Int): Int { + override fun getInt( + key: ConfigKey, + defaultValue: Int + ): Int { return configurations.firstWithKey(key)?.let { return it.getInt(key, defaultValue) } ?: defaultValue } - override fun getString(key: ConfigKey, defaultValue: String): String { + override fun getString( + key: ConfigKey, + defaultValue: String + ): String { return configurations.firstWithKey(key)?.let { return it.getString(key, defaultValue) } ?: defaultValue diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/BooleanConfigurationEntry.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/BooleanConfigurationEntry.kt index 15b6f1d8..b5381516 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/BooleanConfigurationEntry.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/BooleanConfigurationEntry.kt @@ -6,7 +6,5 @@ data class BooleanConfigurationEntry( override val key: ConfigKey, private val defaultValue: Boolean ) : DefaultEntry { - - override fun getValue(configuration: Configuration) = - configuration.getBoolean(key, defaultValue) + override fun getValue(configuration: Configuration) = configuration.getBoolean(key, defaultValue) } diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/DefaultEntry.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/DefaultEntry.kt index 8cd042d5..36604356 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/DefaultEntry.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/DefaultEntry.kt @@ -8,16 +8,16 @@ import co.electriccoin.zcash.configuration.model.map.Configuration * variation in default value. Clients define the key and default value together, rather than just * the key. */ -/* - * API note: the default value is not available through the public interface in order to prevent - * clients from accidentally using the default value instead of the configuration value. - * - * Implementation note: although primitives would be nice, Objects don't increase memory usage much. - * The autoboxing cache solves Booleans, and Strings are already objects, so that just leaves Integers. - * Overall the number of Integer configuration entries is expected to be low compared to Booleans, - * and perhaps many Integer values will also fit within the autoboxing cache. - */ interface DefaultEntry { + /* + * API note: the default value is not available through the public interface in order to prevent + * clients from accidentally using the default value instead of the configuration value. + * + * Implementation note: although primitives would be nice, Objects don't increase memory usage much. + * The autoboxing cache solves Booleans, and Strings are already objects, so that just leaves Integers. + * Overall the number of Integer configuration entries is expected to be low compared to Booleans, + * and perhaps many Integer values will also fit within the autoboxing cache. + */ val key: ConfigKey diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/IntegerConfigurationEntry.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/IntegerConfigurationEntry.kt index 230c869f..715fb493 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/IntegerConfigurationEntry.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/IntegerConfigurationEntry.kt @@ -6,6 +6,5 @@ data class IntegerConfigurationEntry( override val key: ConfigKey, private val defaultValue: Int ) : DefaultEntry { - override fun getValue(configuration: Configuration) = configuration.getInt(key, defaultValue) } diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/StringConfigurationEntry.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/StringConfigurationEntry.kt index 1baef519..48a70ab7 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/StringConfigurationEntry.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/entry/StringConfigurationEntry.kt @@ -6,6 +6,5 @@ data class StringConfigurationEntry( override val key: ConfigKey, private val defaultValue: String ) : DefaultEntry { - override fun getValue(configuration: Configuration) = configuration.getString(key, defaultValue) } diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/Configuration.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/Configuration.kt index 23f0da12..7c17ab2b 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/Configuration.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/Configuration.kt @@ -26,7 +26,10 @@ interface Configuration { * be returned if type coercion fails. * @return boolean mapping for `key` or `defaultValue`. */ - fun getBoolean(key: ConfigKey, defaultValue: Boolean): Boolean + fun getBoolean( + key: ConfigKey, + defaultValue: Boolean + ): Boolean /** * @param key Key to use to retrieve the value. @@ -35,7 +38,10 @@ interface Configuration { * be returned if type coercion fails. * @return int mapping for `key` or `defaultValue`. */ - fun getInt(key: ConfigKey, defaultValue: Int): Int + fun getInt( + key: ConfigKey, + defaultValue: Int + ): Int /** * @param key Key to use to retrieve the value. @@ -44,5 +50,8 @@ interface Configuration { * be returned if type coercion fails. * @return String mapping for `key` or `defaultValue`. */ - fun getString(key: ConfigKey, defaultValue: String): String + fun getString( + key: ConfigKey, + defaultValue: String + ): String } diff --git a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/StringConfiguration.kt b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/StringConfiguration.kt index 26d4d3ff..eb57237e 100644 --- a/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/StringConfiguration.kt +++ b/configuration-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/configuration/model/map/StringConfiguration.kt @@ -10,30 +10,38 @@ data class StringConfiguration( val configurationMapping: PersistentMap, override val updatedAt: Instant? ) : Configuration { - override fun getBoolean( key: ConfigKey, defaultValue: Boolean ) = configurationMapping[key.key]?.let { try { it.toBooleanStrict() - } catch (@Suppress("SwallowedException") e: IllegalArgumentException) { + } catch ( + @Suppress("SwallowedException") e: IllegalArgumentException + ) { // In the future, log coercion failure as this could mean someone made an error in the remote config console defaultValue } } ?: defaultValue - override fun getInt(key: ConfigKey, defaultValue: Int) = configurationMapping[key.key]?.let { + override fun getInt( + key: ConfigKey, + defaultValue: Int + ) = configurationMapping[key.key]?.let { try { it.toInt() - } catch (@Suppress("SwallowedException") e: NumberFormatException) { + } catch ( + @Suppress("SwallowedException") e: NumberFormatException + ) { // In the future, log coercion failure as this could mean someone made an error in the remote config console defaultValue } } ?: defaultValue - override fun getString(key: ConfigKey, defaultValue: String) = - configurationMapping.getOrElse(key.key) { defaultValue } + override fun getString( + key: ConfigKey, + defaultValue: String + ) = configurationMapping.getOrElse(key.key) { defaultValue } override fun hasKey(key: ConfigKey) = configurationMapping.containsKey(key.key) } diff --git a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProviderTest.kt b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProviderTest.kt index bda735f3..2153f469 100644 --- a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProviderTest.kt +++ b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/api/MergingConfigurationProviderTest.kt @@ -18,72 +18,98 @@ import kotlin.test.assertTrue class MergingConfigurationProviderTest { @Test fun peek_ordering() { - val configurationProvider = MergingConfigurationProvider( - persistentListOf( - MockConfigurationProvider( - StringConfiguration(persistentMapOf(BooleanDefaultEntryFixture.KEY.key to true.toString()), null) - ), - MockConfigurationProvider( - StringConfiguration(persistentMapOf(BooleanDefaultEntryFixture.KEY.key to false.toString()), null) + val configurationProvider = + MergingConfigurationProvider( + persistentListOf( + MockConfigurationProvider( + StringConfiguration( + persistentMapOf(BooleanDefaultEntryFixture.KEY.key to true.toString()), + null + ) + ), + MockConfigurationProvider( + StringConfiguration( + persistentMapOf(BooleanDefaultEntryFixture.KEY.key to false.toString()), + null + ) + ) ) ) - ) assertTrue(BooleanDefaultEntryFixture.newTrueEntry().getValue(configurationProvider.peekConfiguration())) } @Test - fun getFlow_ordering() = runTest { - val configurationProvider = MergingConfigurationProvider( - persistentListOf( - MockConfigurationProvider( - StringConfiguration(persistentMapOf(BooleanDefaultEntryFixture.KEY.key to true.toString()), null) - ), - MockConfigurationProvider( - StringConfiguration(persistentMapOf(BooleanDefaultEntryFixture.KEY.key to false.toString()), null) + fun getFlow_ordering() = + runTest { + val configurationProvider = + MergingConfigurationProvider( + persistentListOf( + MockConfigurationProvider( + StringConfiguration( + persistentMapOf(BooleanDefaultEntryFixture.KEY.key to true.toString()), + null + ) + ), + MockConfigurationProvider( + StringConfiguration( + persistentMapOf(BooleanDefaultEntryFixture.KEY.key to false.toString()), + null + ) + ) + ) ) - ) - ) - assertTrue( - BooleanDefaultEntryFixture.newTrueEntry().getValue(configurationProvider.getConfigurationFlow().first()) - ) - } + assertTrue( + BooleanDefaultEntryFixture.newTrueEntry().getValue(configurationProvider.getConfigurationFlow().first()) + ) + } @Test - fun getFlow_empty() = runTest { - val configurationProvider = MergingConfigurationProvider( - emptyList().toPersistentList() - ) + fun getFlow_empty() = + runTest { + val configurationProvider = + MergingConfigurationProvider( + emptyList().toPersistentList() + ) - val firstMergedConfiguration = configurationProvider.getConfigurationFlow().first() + val firstMergedConfiguration = configurationProvider.getConfigurationFlow().first() - assertTrue(BooleanDefaultEntryFixture.newTrueEntry().getValue(firstMergedConfiguration)) - } + assertTrue(BooleanDefaultEntryFixture.newTrueEntry().getValue(firstMergedConfiguration)) + } @Test - fun getUpdatedAt_newest() = runTest { - val older = "2023-01-15T08:38:45.415Z".toInstant() - val newer = "2023-01-17T08:38:45.415Z".toInstant() + fun getUpdatedAt_newest() = + runTest { + val older = "2023-01-15T08:38:45.415Z".toInstant() + val newer = "2023-01-17T08:38:45.415Z".toInstant() - val configurationProvider = MergingConfigurationProvider( - persistentListOf( - MockConfigurationProvider( - StringConfiguration(persistentMapOf(BooleanDefaultEntryFixture.KEY.key to true.toString()), older) - ), - MockConfigurationProvider( - StringConfiguration(persistentMapOf(BooleanDefaultEntryFixture.KEY.key to false.toString()), newer) + val configurationProvider = + MergingConfigurationProvider( + persistentListOf( + MockConfigurationProvider( + StringConfiguration( + persistentMapOf(BooleanDefaultEntryFixture.KEY.key to true.toString()), + older + ) + ), + MockConfigurationProvider( + StringConfiguration( + persistentMapOf( + BooleanDefaultEntryFixture.KEY.key to false.toString() + ), + newer + ) + ) + ) ) - ) - ) - val updatedAt = configurationProvider.getConfigurationFlow().first().updatedAt - assertEquals(newer, updatedAt) - } + val updatedAt = configurationProvider.getConfigurationFlow().first().updatedAt + assertEquals(newer, updatedAt) + } } private class MockConfigurationProvider(private val configuration: Configuration) : ConfigurationProvider { - override fun peekConfiguration(): Configuration { return configuration } diff --git a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/MockConfiguration.kt b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/MockConfiguration.kt index 1e654c25..04ebad56 100644 --- a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/MockConfiguration.kt +++ b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/MockConfiguration.kt @@ -11,7 +11,6 @@ import kotlinx.datetime.Instant * though, making the initial mapping thread-safe. */ class MockConfiguration(private val configurationMapping: Map = emptyMap()) : Configuration { - override val updatedAt: Instant? = null override fun getBoolean( @@ -20,23 +19,32 @@ class MockConfiguration(private val configurationMapping: Map = ) = configurationMapping[key.key]?.let { try { it.toBooleanStrict() - } catch (@Suppress("SwallowedException") e: IllegalArgumentException) { + } catch ( + @Suppress("SwallowedException") e: IllegalArgumentException + ) { // In the future, log coercion failure as this could mean someone made an error in the remote config console defaultValue } } ?: defaultValue - override fun getInt(key: ConfigKey, defaultValue: Int) = configurationMapping[key.key]?.let { + override fun getInt( + key: ConfigKey, + defaultValue: Int + ) = configurationMapping[key.key]?.let { try { it.toInt() - } catch (@Suppress("SwallowedException") e: NumberFormatException) { + } catch ( + @Suppress("SwallowedException") e: NumberFormatException + ) { // In the future, log coercion failure as this could mean someone made an error in the remote config console defaultValue } } ?: defaultValue - override fun getString(key: ConfigKey, defaultValue: String) = - configurationMapping.getOrElse(key.key) { defaultValue } + override fun getString( + key: ConfigKey, + defaultValue: String + ) = configurationMapping.getOrElse(key.key) { defaultValue } override fun hasKey(key: ConfigKey) = configurationMapping.containsKey(key.key) } diff --git a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/BooleanDefaultEntryFixture.kt b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/BooleanDefaultEntryFixture.kt index 9144ee69..4e4dd1f7 100644 --- a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/BooleanDefaultEntryFixture.kt +++ b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/BooleanDefaultEntryFixture.kt @@ -4,7 +4,6 @@ import co.electriccoin.zcash.configuration.model.entry.BooleanConfigurationEntry import co.electriccoin.zcash.configuration.model.entry.ConfigKey object BooleanDefaultEntryFixture { - val KEY = ConfigKey("some_boolean_key") // $NON-NLS fun newTrueEntry() = BooleanConfigurationEntry(KEY, true) diff --git a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/IntegerDefaultEntryFixture.kt b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/IntegerDefaultEntryFixture.kt index c8ce150b..06389a19 100644 --- a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/IntegerDefaultEntryFixture.kt +++ b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/IntegerDefaultEntryFixture.kt @@ -6,5 +6,9 @@ import co.electriccoin.zcash.configuration.model.entry.IntegerConfigurationEntry object IntegerDefaultEntryFixture { val KEY = ConfigKey("some_string_key") // $NON-NLS const val DEFAULT_VALUE = 123 - fun newEntry(key: ConfigKey = KEY, value: Int = DEFAULT_VALUE) = IntegerConfigurationEntry(key, value) + + fun newEntry( + key: ConfigKey = KEY, + value: Int = DEFAULT_VALUE + ) = IntegerConfigurationEntry(key, value) } diff --git a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/StringDefaultEntryFixture.kt b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/StringDefaultEntryFixture.kt index 76271876..6fb742d8 100644 --- a/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/StringDefaultEntryFixture.kt +++ b/configuration-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/configuration/test/fixture/StringDefaultEntryFixture.kt @@ -6,5 +6,9 @@ import co.electriccoin.zcash.configuration.model.entry.StringConfigurationEntry object StringDefaultEntryFixture { val KEY = ConfigKey("some_string_key") // $NON-NLS const val DEFAULT_VALUE = "some_default_value" // $NON-NLS - fun newEntryEntry(key: ConfigKey = KEY, value: String = DEFAULT_VALUE) = StringConfigurationEntry(key, value) + + fun newEntryEntry( + key: ConfigKey = KEY, + value: String = DEFAULT_VALUE + ) = StringConfigurationEntry(key, value) } diff --git a/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/AndroidConfigurationFactory.kt b/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/AndroidConfigurationFactory.kt index 14f1c458..0f695df8 100644 --- a/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/AndroidConfigurationFactory.kt +++ b/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/AndroidConfigurationFactory.kt @@ -8,25 +8,28 @@ import co.electriccoin.zcash.spackle.LazyWithArgument import kotlinx.collections.immutable.toPersistentList object AndroidConfigurationFactory { - - private val instance = LazyWithArgument { context -> - new(context) - } + private val instance = + LazyWithArgument { context -> + new(context) + } fun getInstance(context: Context): ConfigurationProvider = instance.getInstance(context) // Context will be needed for most cloud providers, e.g. to integrate with Firebase or other // remote configuration providers. - private fun new(@Suppress("UNUSED_PARAMETER") context: Context): ConfigurationProvider { - val configurationProviders = buildList { - // For ordering, ensure the IntentConfigurationProvider is first so that it can - // override any other configuration providers. - if (BuildConfig.DEBUG) { - add(IntentConfigurationProvider) - } + private fun new( + @Suppress("UNUSED_PARAMETER") context: Context + ): ConfigurationProvider { + val configurationProviders = + buildList { + // For ordering, ensure the IntentConfigurationProvider is first so that it can + // override any other configuration providers. + if (BuildConfig.DEBUG) { + add(IntentConfigurationProvider) + } - // In the future, add a third party cloud-based configuration provider - } + // In the future, add a third party cloud-based configuration provider + } return MergingConfigurationProvider(configurationProviders.toPersistentList()) } diff --git a/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationProvider.kt b/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationProvider.kt index 1ffa9f5b..823feb60 100644 --- a/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationProvider.kt +++ b/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationProvider.kt @@ -8,7 +8,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow internal object IntentConfigurationProvider : ConfigurationProvider { - private val configurationStateFlow = MutableStateFlow(StringConfiguration(persistentMapOf(), null)) override fun peekConfiguration() = configurationStateFlow.value diff --git a/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationReceiver.kt b/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationReceiver.kt index 544b918c..153e6d6d 100644 --- a/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationReceiver.kt +++ b/configuration-impl-android-lib/src/main/java/co/electriccoin/zcash/configuration/internal/intent/IntentConfigurationReceiver.kt @@ -8,18 +8,22 @@ import kotlinx.collections.immutable.toPersistentMap import kotlinx.datetime.Clock class IntentConfigurationReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { + override fun onReceive( + context: Context?, + intent: Intent? + ) { intent?.defuse()?.let { val key = it.getStringExtra(ConfigurationIntent.EXTRA_STRING_KEY) val value = it.getStringExtra(ConfigurationIntent.EXTRA_STRING_VALUE) if (null != key) { val existingConfiguration = IntentConfigurationProvider.peekConfiguration().configurationMapping - val newConfiguration = if (null == value) { - existingConfiguration.remove(key) - } else { - existingConfiguration + (key to value) - } + val newConfiguration = + if (null == value) { + existingConfiguration.remove(key) + } else { + existingConfiguration + (key to value) + } IntentConfigurationProvider.setConfiguration( StringConfiguration(newConfiguration.toPersistentMap(), Clock.System.now()) @@ -34,7 +38,9 @@ private fun Intent.defuse(): Intent? { return try { extras?.containsKey(null) this - } catch (@Suppress("SwallowedException", "TooGenericExceptionCaught") e: Exception) { + } catch ( + @Suppress("SwallowedException", "TooGenericExceptionCaught") e: Exception + ) { null } } diff --git a/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandlerTest.kt b/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandlerTest.kt index ebd7e511..68a21868 100644 --- a/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandlerTest.kt +++ b/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandlerTest.kt @@ -9,7 +9,6 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicBoolean class AndroidUncaughtExceptionHandlerTest { - @Test(expected = IllegalStateException::class) fun requires_main_thread() { AndroidUncaughtExceptionHandler.register(ApplicationProvider.getApplicationContext()) diff --git a/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/Components.kt b/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/Components.kt index 4f392879..74c13221 100644 --- a/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/Components.kt +++ b/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/Components.kt @@ -12,7 +12,6 @@ import org.junit.Assert.assertTrue import org.junit.Test class Components { - @Test @SmallTest fun process_names() { @@ -26,16 +25,18 @@ class Components { } } -private fun PackageManager.getProviderInfoCompat(componentName: ComponentName) = if (AndroidApiVersion.isAtLeastT) { - getProviderInfo(componentName, PackageManager.ComponentInfoFlags.of(0)) -} else { - @Suppress("Deprecation") - getProviderInfo(componentName, 0) -} +private fun PackageManager.getProviderInfoCompat(componentName: ComponentName) = + if (AndroidApiVersion.isAtLeastT) { + getProviderInfo(componentName, PackageManager.ComponentInfoFlags.of(0)) + } else { + @Suppress("Deprecation") + getProviderInfo(componentName, 0) + } -private fun PackageManager.getReceiverInfoCompat(componentName: ComponentName) = if (AndroidApiVersion.isAtLeastT) { - getReceiverInfo(componentName, PackageManager.ComponentInfoFlags.of(0)) -} else { - @Suppress("Deprecation") - getReceiverInfo(componentName, 0) -} +private fun PackageManager.getReceiverInfoCompat(componentName: ComponentName) = + if (AndroidApiVersion.isAtLeastT) { + getReceiverInfo(componentName, PackageManager.ComponentInfoFlags.of(0)) + } else { + @Suppress("Deprecation") + getReceiverInfo(componentName, 0) + } diff --git a/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/ReportableExceptionTest.kt b/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/ReportableExceptionTest.kt index 62b96ee6..7b7f0fed 100644 --- a/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/ReportableExceptionTest.kt +++ b/crash-android-lib/src/androidTest/kotlin/co/electriccoin/zcash/crash/android/internal/local/ReportableExceptionTest.kt @@ -6,7 +6,6 @@ import org.junit.Assert.assertEquals import org.junit.Test class ReportableExceptionTest { - @Test fun bundle() { val reportableException = ReportableExceptionFixture.new() diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/AndroidExceptionPath.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/AndroidExceptionPath.kt index 047018e5..b4b28adf 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/AndroidExceptionPath.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/AndroidExceptionPath.kt @@ -9,8 +9,9 @@ import java.io.File @Suppress("ReturnCount") suspend fun ExceptionPath.getExceptionDirectory(context: Context): File? { - val exceptionDirectory = context.getExternalFilesDirSuspend(null) - ?.let { File(File(it, ExceptionPath.LOG_DIRECTORY_NAME), ExceptionPath.EXCEPTION_DIRECTORY_NAME) } + val exceptionDirectory = + context.getExternalFilesDirSuspend(null) + ?.let { File(File(it, ExceptionPath.LOG_DIRECTORY_NAME), ExceptionPath.EXCEPTION_DIRECTORY_NAME) } if (null == exceptionDirectory) { Twig.info { "Unable to get external storage directory; external storage may not be available" } @@ -27,9 +28,13 @@ suspend fun ExceptionPath.getExceptionDirectory(context: Context): File? { return exceptionDirectory } -suspend fun ExceptionPath.getExceptionPath(context: Context, exception: ReportableException): File? { - val exceptionDirectory = getExceptionDirectory(context) - ?: return null +suspend fun ExceptionPath.getExceptionPath( + context: Context, + exception: ReportableException +): File? { + val exceptionDirectory = + getExceptionDirectory(context) + ?: return null return File(exceptionDirectory, newExceptionFileName(exception)) } diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/GlobalCrashReporter.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/GlobalCrashReporter.kt index 19bc657c..e2f76cc2 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/GlobalCrashReporter.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/GlobalCrashReporter.kt @@ -11,7 +11,6 @@ import co.electriccoin.zcash.spackle.process.ProcessNameCompat import java.util.Collections object GlobalCrashReporter { - internal const val CRASH_PROCESS_NAME_SUFFIX = ":crash" // $NON-NLS private val intrinsicLock = Any() @@ -33,17 +32,18 @@ object GlobalCrashReporter { synchronized(intrinsicLock) { if (registeredCrashReporters == null) { - registeredCrashReporters = Collections.synchronizedList( - // To prevent a race condition, register the LocalCrashReporter first. - // FirebaseCrashReporter does some asynchronous registration internally, while - // LocalCrashReporter uses AndroidUncaughtExceptionHandler which needs to read - // and write the default UncaughtExceptionHandler. The only way to ensure - // interleaving doesn't happen is to register the LocalCrashReporter first. - listOfNotNull( - LocalCrashReporter.getInstance(context), - FirebaseCrashReporter(context), + registeredCrashReporters = + Collections.synchronizedList( + // To prevent a race condition, register the LocalCrashReporter first. + // FirebaseCrashReporter does some asynchronous registration internally, while + // LocalCrashReporter uses AndroidUncaughtExceptionHandler which needs to read + // and write the default UncaughtExceptionHandler. The only way to ensure + // interleaving doesn't happen is to register the LocalCrashReporter first. + listOfNotNull( + LocalCrashReporter.getInstance(context), + FirebaseCrashReporter(context), + ) ) - ) } } diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/CrashReporter.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/CrashReporter.kt index d5d34ddd..3f232935 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/CrashReporter.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/CrashReporter.kt @@ -3,7 +3,6 @@ package co.electriccoin.zcash.crash.android.internal import androidx.annotation.AnyThread interface CrashReporter { - /** * Report a caught exception, e.g. within a try-catch. */ diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseAppCache.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseAppCache.kt index 974109f1..3862aa43 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseAppCache.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseAppCache.kt @@ -30,9 +30,10 @@ object FirebaseAppCache { } } -private suspend fun getFirebaseAppContainer(context: Context): FirebaseAppContainer = withContext(Dispatchers.IO) { - val firebaseApp = FirebaseApp.initializeApp(context) - FirebaseAppContainer(firebaseApp) -} +private suspend fun getFirebaseAppContainer(context: Context): FirebaseAppContainer = + withContext(Dispatchers.IO) { + val firebaseApp = FirebaseApp.initializeApp(context) + FirebaseAppContainer(firebaseApp) + } private class FirebaseAppContainer(val firebaseApp: FirebaseApp?) diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseCrashReporter.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseCrashReporter.kt index 72d9ba0b..92868c8a 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseCrashReporter.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/firebase/FirebaseCrashReporter.kt @@ -25,13 +25,13 @@ import kotlinx.coroutines.async internal class FirebaseCrashReporter( context: Context ) : CrashReporter { - @OptIn(kotlinx.coroutines.DelicateCoroutinesApi::class) private val analyticsScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - private val initFirebaseJob: Deferred = analyticsScope.async { - FirebaseCrashReporterImpl.getInstance(context) - } + private val initFirebaseJob: Deferred = + analyticsScope.async { + FirebaseCrashReporterImpl.getInstance(context) + } @AnyThread override fun reportCaughtException(exception: Throwable) { @@ -67,7 +67,6 @@ private class FirebaseCrashReporterImpl( private val firebaseCrashlytics: FirebaseCrashlytics, private val firebaseInstallations: FirebaseInstallations ) : CrashReporter { - @AnyThread override fun reportCaughtException(exception: Throwable) { firebaseCrashlytics.recordException(exception) @@ -90,30 +89,32 @@ private class FirebaseCrashReporterImpl( * early crashes may be missed. This is a tradeoff we are willing to make in order to avoid * ANRs. */ - private val lazyWithArgument = SuspendingLazy { - if (it.resources.getBoolean(R.bool.co_electriccoin_zcash_crash_is_firebase_enabled)) { + private val lazyWithArgument = + SuspendingLazy { + if (it.resources.getBoolean(R.bool.co_electriccoin_zcash_crash_is_firebase_enabled)) { - // Workaround for disk IO on main thread in Firebase initialization - val firebaseApp = FirebaseAppCache.getFirebaseApp(it) - if (firebaseApp == null) { - Twig.warn { "Unable to initialize Crashlytics. FirebaseApp is null" } - return@SuspendingLazy null + // Workaround for disk IO on main thread in Firebase initialization + val firebaseApp = FirebaseAppCache.getFirebaseApp(it) + if (firebaseApp == null) { + Twig.warn { "Unable to initialize Crashlytics. FirebaseApp is null" } + return@SuspendingLazy null + } + + val firebaseInstallations = FirebaseInstallations.getInstance(firebaseApp) + val firebaseCrashlytics = + FirebaseCrashlytics.getInstance().apply { + setCustomKey( + CrashlyticsUserProperties.IS_TEST, + EmulatorWtfUtil.isEmulatorWtf(it) || FirebaseTestLabUtil.isFirebaseTestLab(it) + ) + } + + FirebaseCrashReporterImpl(firebaseCrashlytics, firebaseInstallations) + } else { + Twig.warn { "Unable to initialize Crashlytics. Configure API keys in the app module" } + null } - - val firebaseInstallations = FirebaseInstallations.getInstance(firebaseApp) - val firebaseCrashlytics = FirebaseCrashlytics.getInstance().apply { - setCustomKey( - CrashlyticsUserProperties.IS_TEST, - EmulatorWtfUtil.isEmulatorWtf(it) || FirebaseTestLabUtil.isFirebaseTestLab(it) - ) - } - - FirebaseCrashReporterImpl(firebaseCrashlytics, firebaseInstallations) - } else { - Twig.warn { "Unable to initialize Crashlytics. Configure API keys in the app module" } - null } - } suspend fun getInstance(context: Context): CrashReporter? { return lazyWithArgument.getInstance(context) diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidExceptionReporter.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidExceptionReporter.kt index ec0ad1ee..fd64e8cc 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidExceptionReporter.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidExceptionReporter.kt @@ -8,9 +8,13 @@ import co.electriccoin.zcash.crash.android.getExceptionPath import co.electriccoin.zcash.crash.write internal object AndroidExceptionReporter { - internal suspend fun reportException(context: Context, reportableException: ReportableException) { - val exceptionPath = ExceptionPath.getExceptionPath(context, reportableException) - ?: return + internal suspend fun reportException( + context: Context, + reportableException: ReportableException + ) { + val exceptionPath = + ExceptionPath.getExceptionPath(context, reportableException) + ?: return reportableException.write(exceptionPath) diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidReportableException.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidReportableException.kt index 8a5afbe5..87fae440 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidReportableException.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidReportableException.kt @@ -13,8 +13,9 @@ internal fun ReportableException.Companion.new( isUncaught: Boolean, clock: Clock = Clock.System ): ReportableException { - val versionName = context.packageManager.getPackageInfoCompat(context.packageName, 0L).versionName - ?: "null" + val versionName = + context.packageManager.getPackageInfoCompat(context.packageName, 0L).versionName + ?: "null" return ReportableException( throwable.javaClass.name, @@ -25,15 +26,16 @@ internal fun ReportableException.Companion.new( ) } -internal fun ReportableException.toBundle() = Bundle().apply { - // Although Exception is Serializable, some Kotlin Coroutines exception classes break this - // API contract. Therefore we have to convert to a string here. - putSerializable(ReportableException.EXTRA_STRING_CLASS_NAME, exceptionClass) - putSerializable(ReportableException.EXTRA_STRING_TRACE, exceptionTrace) - putString(ReportableException.EXTRA_STRING_APP_VERSION, appVersion) - putBoolean(ReportableException.EXTRA_BOOLEAN_IS_UNCAUGHT, isUncaught) - putLong(ReportableException.EXTRA_LONG_WALLTIME_MILLIS, time.toEpochMilliseconds()) -} +internal fun ReportableException.toBundle() = + Bundle().apply { + // Although Exception is Serializable, some Kotlin Coroutines exception classes break this + // API contract. Therefore we have to convert to a string here. + putSerializable(ReportableException.EXTRA_STRING_CLASS_NAME, exceptionClass) + putSerializable(ReportableException.EXTRA_STRING_TRACE, exceptionTrace) + putString(ReportableException.EXTRA_STRING_APP_VERSION, appVersion) + putBoolean(ReportableException.EXTRA_BOOLEAN_IS_UNCAUGHT, isUncaught) + putLong(ReportableException.EXTRA_LONG_WALLTIME_MILLIS, time.toEpochMilliseconds()) + } internal fun ReportableException.Companion.fromBundle(bundle: Bundle): ReportableException { val className = bundle.getString(EXTRA_STRING_CLASS_NAME)!! diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandler.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandler.kt index 9a730939..1b2bd671 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandler.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/AndroidUncaughtExceptionHandler.kt @@ -12,14 +12,17 @@ internal class AndroidUncaughtExceptionHandler( context: Context, private val defaultUncaughtExceptionHandler: Thread.UncaughtExceptionHandler ) : Thread.UncaughtExceptionHandler { - private val applicationContext = context.applicationContext - override fun uncaughtException(t: Thread, e: Throwable) { + override fun uncaughtException( + t: Thread, + e: Throwable + ) { val reportableException = ReportableException.new(applicationContext, e, true) - val isUseSecondaryProcess = applicationContext.resources - .getBoolean(R.bool.co_electriccoin_zcash_crash_is_use_secondary_process) + val isUseSecondaryProcess = + applicationContext.resources + .getBoolean(R.bool.co_electriccoin_zcash_crash_is_use_secondary_process) if (isUseSecondaryProcess) { applicationContext.sendBroadcast(ExceptionReceiver.newIntent(applicationContext, reportableException)) @@ -31,7 +34,6 @@ internal class AndroidUncaughtExceptionHandler( } companion object { - private val isInitialized = AtomicBoolean(false) /** diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/ExceptionReceiver.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/ExceptionReceiver.kt index 91f06ea7..aab4111e 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/ExceptionReceiver.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/ExceptionReceiver.kt @@ -8,16 +8,18 @@ import kotlinx.coroutines.GlobalScope @OptIn(kotlinx.coroutines.DelicateCoroutinesApi::class) class ExceptionReceiver : CoroutineBroadcastReceiver(GlobalScope) { - - override suspend fun onReceiveSuspend(context: Context, intent: Intent) { - val reportableException = intent.extras?.let { ReportableException.fromBundle(it) } - ?: return + override suspend fun onReceiveSuspend( + context: Context, + intent: Intent + ) { + val reportableException = + intent.extras?.let { ReportableException.fromBundle(it) } + ?: return AndroidExceptionReporter.reportException(context, reportableException) } companion object { - /** * @return Explicit intent to broadcast to log the exception. */ diff --git a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/LocalCrashReporter.kt b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/LocalCrashReporter.kt index e3e0e8e7..47aa31e7 100644 --- a/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/LocalCrashReporter.kt +++ b/crash-android-lib/src/main/kotlin/co/electriccoin/zcash/crash/android/internal/local/LocalCrashReporter.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.launch * Registers an exception handler to write exceptions to disk. */ internal class LocalCrashReporter(private val applicationContext: Context) : CrashReporter { - private val crashReportingScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) @AnyThread @@ -36,10 +35,11 @@ internal class LocalCrashReporter(private val applicationContext: Context) : Cra } companion object { - private val lazyWithArgument = LazyWithArgument { - AndroidUncaughtExceptionHandler.register(it) - LocalCrashReporter(it.applicationContext) - } + private val lazyWithArgument = + LazyWithArgument { + AndroidUncaughtExceptionHandler.register(it) + LocalCrashReporter(it.applicationContext) + } fun getInstance(context: Context): CrashReporter { return lazyWithArgument.getInstance(context) diff --git a/crash-lib/src/commonMain/kotlin/co/electriccoin/zcash/crash/ReportableException.kt b/crash-lib/src/commonMain/kotlin/co/electriccoin/zcash/crash/ReportableException.kt index c359e2cb..9fcb8df6 100644 --- a/crash-lib/src/commonMain/kotlin/co/electriccoin/zcash/crash/ReportableException.kt +++ b/crash-lib/src/commonMain/kotlin/co/electriccoin/zcash/crash/ReportableException.kt @@ -9,6 +9,5 @@ data class ReportableException( val isUncaught: Boolean, val time: Instant ) { - companion object } diff --git a/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ExceptionPath.kt b/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ExceptionPath.kt index 9af2cec0..3162ce45 100644 --- a/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ExceptionPath.kt +++ b/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ExceptionPath.kt @@ -14,8 +14,10 @@ object ExceptionPath { const val TYPE = "txt" @Suppress("MaxLineLength") - fun newExceptionFileName(exception: ReportableException, uuid: UUID = UUID.randomUUID()) = - "${exception.time.epochSeconds}$SEPARATOR$uuid$SEPARATOR${exception.exceptionClass}$SEPARATOR${exception.isUncaught}.$TYPE" + fun newExceptionFileName( + exception: ReportableException, + uuid: UUID = UUID.randomUUID() + ) = "${exception.time.epochSeconds}$SEPARATOR$uuid$SEPARATOR${exception.exceptionClass}$SEPARATOR${exception.isUncaught}.$TYPE" // The exceptions are really just for debugging @Suppress("ThrowsCount") diff --git a/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportableExceptionExt.kt b/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportableExceptionExt.kt index e05d1cb7..22474845 100644 --- a/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportableExceptionExt.kt +++ b/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportableExceptionExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.crash @@ -8,12 +8,13 @@ import kotlinx.coroutines.withContext import java.io.File suspend fun ReportableException.write(path: File) { - val exceptionString = buildString { - appendLine("App version: $appVersion") - appendLine("Is uncaught: $isUncaught") - appendLine("Time: $time") - append(exceptionTrace) - } + val exceptionString = + buildString { + appendLine("App version: $appVersion") + appendLine("Is uncaught: $isUncaught") + appendLine("Time: $time") + append(exceptionTrace) + } withContext(Dispatchers.IO) { path.writeAtomically { tempFile -> diff --git a/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportedExceptionExt.kt b/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportedExceptionExt.kt index a72688d1..34619cfd 100644 --- a/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportedExceptionExt.kt +++ b/crash-lib/src/jvmMain/kotlin/co/electriccoin/zcash/crash/ReportedExceptionExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.crash diff --git a/gradle.properties b/gradle.properties index ae5b46d3..0a28dad4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -143,7 +143,7 @@ FULLADLE_VERSION=0.17.4 GOOGLE_PLAY_SERVICES_GRADLE_PLUGIN_VERSION=4.3.15 GRADLE_VERSIONS_PLUGIN_VERSION=0.47.0 JGIT_VERSION=6.4.0.202211300538-r -KTLINT_VERSION=0.49.0 +KTLINT_VERSION=1.0.1 ACCOMPANIST_PERMISSIONS_VERSION=0.32.0 ANDROIDX_ACTIVITY_VERSION=1.8.1 diff --git a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/api/PreferenceProvider.kt b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/api/PreferenceProvider.kt index 6b65280b..80c993bf 100644 --- a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/api/PreferenceProvider.kt +++ b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/api/PreferenceProvider.kt @@ -4,10 +4,12 @@ import co.electriccoin.zcash.preference.model.entry.PreferenceKey import kotlinx.coroutines.flow.Flow interface PreferenceProvider { - suspend fun hasKey(key: PreferenceKey): Boolean - suspend fun putString(key: PreferenceKey, value: String?) + suspend fun putString( + key: PreferenceKey, + value: String? + ) suspend fun getString(key: PreferenceKey): String? diff --git a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefault.kt b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefault.kt index 6ece6f93..d81e4d5d 100644 --- a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefault.kt +++ b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefault.kt @@ -6,19 +6,22 @@ data class BooleanPreferenceDefault( override val key: PreferenceKey, private val defaultValue: Boolean ) : PreferenceDefault { - @Suppress("SwallowedException") - override suspend fun getValue(preferenceProvider: PreferenceProvider) = preferenceProvider.getString(key)?.let { - try { - it.toBooleanStrict() - } catch (e: IllegalArgumentException) { - // TODO [#32]: Log coercion failure instead of just silently returning default - // TODO [#32]: https://github.com/Electric-Coin-Company/zashi-android/issues/32 - defaultValue - } - } ?: defaultValue + override suspend fun getValue(preferenceProvider: PreferenceProvider) = + preferenceProvider.getString(key)?.let { + try { + it.toBooleanStrict() + } catch (e: IllegalArgumentException) { + // TODO [#32]: Log coercion failure instead of just silently returning default + // TODO [#32]: https://github.com/Electric-Coin-Company/zashi-android/issues/32 + defaultValue + } + } ?: defaultValue - override suspend fun putValue(preferenceProvider: PreferenceProvider, newValue: Boolean) { + override suspend fun putValue( + preferenceProvider: PreferenceProvider, + newValue: Boolean + ) { preferenceProvider.putString(key, newValue.toString()) } } diff --git a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefault.kt b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefault.kt index f7ce581b..f7005337 100644 --- a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefault.kt +++ b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefault.kt @@ -6,18 +6,21 @@ data class IntegerPreferenceDefault( override val key: PreferenceKey, private val defaultValue: Int ) : PreferenceDefault { + override suspend fun getValue(preferenceProvider: PreferenceProvider) = + preferenceProvider.getString(key)?.let { + try { + it.toInt() + } catch (e: NumberFormatException) { + // TODO [#32]: Log coercion failure instead of just silently returning default + // TODO [#32]: https://github.com/Electric-Coin-Company/zashi-android/issues/32 + defaultValue + } + } ?: defaultValue - override suspend fun getValue(preferenceProvider: PreferenceProvider) = preferenceProvider.getString(key)?.let { - try { - it.toInt() - } catch (e: NumberFormatException) { - // TODO [#32]: Log coercion failure instead of just silently returning default - // TODO [#32]: https://github.com/Electric-Coin-Company/zashi-android/issues/32 - defaultValue - } - } ?: defaultValue - - override suspend fun putValue(preferenceProvider: PreferenceProvider, newValue: Int) { + override suspend fun putValue( + preferenceProvider: PreferenceProvider, + newValue: Int + ) { preferenceProvider.putString(key, newValue.toString()) } } diff --git a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/PreferenceDefault.kt b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/PreferenceDefault.kt index 6f36fd57..9b72a965 100644 --- a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/PreferenceDefault.kt +++ b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/PreferenceDefault.kt @@ -11,16 +11,16 @@ import kotlinx.coroutines.flow.map * variation in default value. Clients define the key and default value together, rather than just * the key. */ -/* - * API note: the default value is not available through the public interface in order to prevent - * clients from accidentally using the default value instead of the preference value. - * - * Implementation note: although primitives would be nice, Objects don't increase memory usage much. - * The autoboxing cache solves Booleans, and Strings are already objects, so that just leaves Integers. - * Overall the number of Integer preference entries is expected to be low compared to Booleans, - * and perhaps many Integer values will also fit within the autoboxing cache. - */ interface PreferenceDefault { + /* + * API note: the default value is not available through the public interface in order to prevent + * clients from accidentally using the default value instead of the preference value. + * + * Implementation note: although primitives would be nice, Objects don't increase memory usage much. + * The autoboxing cache solves Booleans, and Strings are already objects, so that just leaves Integers. + * Overall the number of Integer preference entries is expected to be low compared to Booleans, + * and perhaps many Integer values will also fit within the autoboxing cache. + */ val key: PreferenceKey @@ -34,14 +34,18 @@ interface PreferenceDefault { * @param preferenceProvider Provides actual preference values. * @param newValue New value to write. */ - suspend fun putValue(preferenceProvider: PreferenceProvider, newValue: T) + suspend fun putValue( + preferenceProvider: PreferenceProvider, + newValue: T + ) /** * @param preferenceProvider Provides actual preference values. * @return Flow that emits preference changes. Note that implementations should emit an initial value * indicating what was stored in the preferences, in addition to subsequent updates. */ - fun observe(preferenceProvider: PreferenceProvider): Flow = preferenceProvider.observe(key) - .map { getValue(preferenceProvider) } - .distinctUntilChanged() + fun observe(preferenceProvider: PreferenceProvider): Flow = + preferenceProvider.observe(key) + .map { getValue(preferenceProvider) } + .distinctUntilChanged() } diff --git a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefault.kt b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefault.kt index caaf5921..3c26acf6 100644 --- a/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefault.kt +++ b/preference-api-lib/src/commonMain/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefault.kt @@ -6,11 +6,14 @@ data class StringPreferenceDefault( override val key: PreferenceKey, private val defaultValue: String ) : PreferenceDefault { + override suspend fun getValue(preferenceProvider: PreferenceProvider) = + preferenceProvider.getString(key) + ?: defaultValue - override suspend fun getValue(preferenceProvider: PreferenceProvider) = preferenceProvider.getString(key) - ?: defaultValue - - override suspend fun putValue(preferenceProvider: PreferenceProvider, newValue: String) { + override suspend fun putValue( + preferenceProvider: PreferenceProvider, + newValue: String + ) { preferenceProvider.putString(key, newValue) } } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefaultTest.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefaultTest.kt index 6e6823c0..e8477817 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefaultTest.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/BooleanPreferenceDefaultTest.kt @@ -16,32 +16,38 @@ class BooleanPreferenceDefaultTest { } @Test - fun value_default_true() = runTest { - val entry = BooleanPreferenceDefaultFixture.newTrue() - assertTrue(entry.getValue(MockPreferenceProvider())) - } - - @Test - fun value_default_false() = runTest { - val entry = BooleanPreferenceDefaultFixture.newFalse() - assertFalse(entry.getValue(MockPreferenceProvider())) - } - - @Test - fun value_from_config_false() = runTest { - val entry = BooleanPreferenceDefaultFixture.newTrue() - val mockPreferenceProvider = MockPreferenceProvider { - mutableMapOf(BooleanPreferenceDefaultFixture.KEY.key to false.toString()) + fun value_default_true() = + runTest { + val entry = BooleanPreferenceDefaultFixture.newTrue() + assertTrue(entry.getValue(MockPreferenceProvider())) } - assertFalse(entry.getValue(mockPreferenceProvider)) - } @Test - fun value_from_config_true() = runTest { - val entry = BooleanPreferenceDefaultFixture.newTrue() - val mockPreferenceProvider = MockPreferenceProvider { - mutableMapOf(BooleanPreferenceDefaultFixture.KEY.key to true.toString()) + fun value_default_false() = + runTest { + val entry = BooleanPreferenceDefaultFixture.newFalse() + assertFalse(entry.getValue(MockPreferenceProvider())) + } + + @Test + fun value_from_config_false() = + runTest { + val entry = BooleanPreferenceDefaultFixture.newTrue() + val mockPreferenceProvider = + MockPreferenceProvider { + mutableMapOf(BooleanPreferenceDefaultFixture.KEY.key to false.toString()) + } + assertFalse(entry.getValue(mockPreferenceProvider)) + } + + @Test + fun value_from_config_true() = + runTest { + val entry = BooleanPreferenceDefaultFixture.newTrue() + val mockPreferenceProvider = + MockPreferenceProvider { + mutableMapOf(BooleanPreferenceDefaultFixture.KEY.key to true.toString()) + } + assertTrue(entry.getValue(mockPreferenceProvider)) } - assertTrue(entry.getValue(mockPreferenceProvider)) - } } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefaultTest.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefaultTest.kt index fb1363e3..ba360295 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefaultTest.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/IntegerPreferenceDefaultTest.kt @@ -15,20 +15,23 @@ class IntegerPreferenceDefaultTest { } @Test - fun value_default() = runTest { - val entry = IntegerPreferenceDefaultFixture.new() - assertEquals(IntegerPreferenceDefaultFixture.DEFAULT_VALUE, entry.getValue(MockPreferenceProvider())) - } - - @Test - fun value_override() = runTest { - val expected = IntegerPreferenceDefaultFixture.DEFAULT_VALUE + 5 - - val entry = IntegerPreferenceDefaultFixture.new() - val mockPreferenceProvider = MockPreferenceProvider { - mutableMapOf(StringDefaultPreferenceFixture.KEY.key to expected.toString()) + fun value_default() = + runTest { + val entry = IntegerPreferenceDefaultFixture.new() + assertEquals(IntegerPreferenceDefaultFixture.DEFAULT_VALUE, entry.getValue(MockPreferenceProvider())) } - assertEquals(expected, entry.getValue(mockPreferenceProvider)) - } + @Test + fun value_override() = + runTest { + val expected = IntegerPreferenceDefaultFixture.DEFAULT_VALUE + 5 + + val entry = IntegerPreferenceDefaultFixture.new() + val mockPreferenceProvider = + MockPreferenceProvider { + mutableMapOf(StringDefaultPreferenceFixture.KEY.key to expected.toString()) + } + + assertEquals(expected, entry.getValue(mockPreferenceProvider)) + } } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefaultTest.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefaultTest.kt index 4a38997b..26f95390 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefaultTest.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/model/entry/StringPreferenceDefaultTest.kt @@ -14,19 +14,22 @@ class StringPreferenceDefaultTest { } @Test - fun value_default() = runTest { - val entry = StringDefaultPreferenceFixture.new() - assertEquals(StringDefaultPreferenceFixture.DEFAULT_VALUE, entry.getValue(MockPreferenceProvider())) - } - - @Test - fun value_override() = runTest { - val entry = StringDefaultPreferenceFixture.new() - - val mockPreferenceProvider = MockPreferenceProvider { - mutableMapOf(StringDefaultPreferenceFixture.KEY.key to "override") + fun value_default() = + runTest { + val entry = StringDefaultPreferenceFixture.new() + assertEquals(StringDefaultPreferenceFixture.DEFAULT_VALUE, entry.getValue(MockPreferenceProvider())) } - assertEquals("override", entry.getValue(mockPreferenceProvider)) - } + @Test + fun value_override() = + runTest { + val entry = StringDefaultPreferenceFixture.new() + + val mockPreferenceProvider = + MockPreferenceProvider { + mutableMapOf(StringDefaultPreferenceFixture.KEY.key to "override") + } + + assertEquals("override", entry.getValue(mockPreferenceProvider)) + } } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/MockPreferenceProvider.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/MockPreferenceProvider.kt index 93b317d6..6b420f4c 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/MockPreferenceProvider.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/MockPreferenceProvider.kt @@ -11,7 +11,6 @@ import kotlinx.coroutines.flow.flow class MockPreferenceProvider( mutableMapFactory: () -> MutableMap = { mutableMapOf() } ) : PreferenceProvider { - private val map = mutableMapFactory() override suspend fun getString(key: PreferenceKey) = map[key.key] @@ -21,7 +20,10 @@ class MockPreferenceProvider( override suspend fun hasKey(key: PreferenceKey) = map.containsKey(key.key) - override suspend fun putString(key: PreferenceKey, value: String?) { + override suspend fun putString( + key: PreferenceKey, + value: String? + ) { map[key.key] = value } } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/BooleanPreferenceDefaultFixture.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/BooleanPreferenceDefaultFixture.kt index d3f99b93..9fe7c26b 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/BooleanPreferenceDefaultFixture.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/BooleanPreferenceDefaultFixture.kt @@ -5,6 +5,8 @@ import co.electriccoin.zcash.preference.model.entry.PreferenceKey object BooleanPreferenceDefaultFixture { val KEY = PreferenceKey("some_boolean_key") // $NON-NLS + fun newTrue() = BooleanPreferenceDefault(KEY, true) + fun newFalse() = BooleanPreferenceDefault(KEY, false) } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/IntegerPreferenceDefaultFixture.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/IntegerPreferenceDefaultFixture.kt index f77fa3d1..3b5058a4 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/IntegerPreferenceDefaultFixture.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/IntegerPreferenceDefaultFixture.kt @@ -6,6 +6,9 @@ import co.electriccoin.zcash.preference.model.entry.PreferenceKey object IntegerPreferenceDefaultFixture { val KEY = PreferenceKey("some_string_key") // $NON-NLS const val DEFAULT_VALUE = 123 - fun new(preferenceKey: PreferenceKey = KEY, value: Int = DEFAULT_VALUE) = - IntegerPreferenceDefault(preferenceKey, value) + + fun new( + preferenceKey: PreferenceKey = KEY, + value: Int = DEFAULT_VALUE + ) = IntegerPreferenceDefault(preferenceKey, value) } diff --git a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt index 25490feb..b1f1a30b 100644 --- a/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt +++ b/preference-api-lib/src/commonTest/kotlin/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt @@ -6,6 +6,9 @@ import co.electriccoin.zcash.preference.model.entry.StringPreferenceDefault object StringDefaultPreferenceFixture { val KEY = PreferenceKey("some_string_key") // $NON-NLS const val DEFAULT_VALUE = "some_default_value" // $NON-NLS - fun new(preferenceKey: PreferenceKey = KEY, value: String = DEFAULT_VALUE) = - StringPreferenceDefault(preferenceKey, value) + + fun new( + preferenceKey: PreferenceKey = KEY, + value: String = DEFAULT_VALUE + ) = StringPreferenceDefault(preferenceKey, value) } diff --git a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt index 246b114b..084248a5 100644 --- a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt +++ b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt @@ -35,61 +35,70 @@ class EncryptedPreferenceProviderTest { @Test @SmallTest - fun put_and_get_string() = runBlocking { - val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" + fun put_and_get_string() = + runBlocking { + val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" - val preferenceProvider = new().apply { - putString(StringDefaultPreferenceFixture.KEY, expectedValue) + val preferenceProvider = + new().apply { + putString(StringDefaultPreferenceFixture.KEY, expectedValue) + } + + assertEquals(expectedValue, StringDefaultPreferenceFixture.new().getValue(preferenceProvider)) } - assertEquals(expectedValue, StringDefaultPreferenceFixture.new().getValue(preferenceProvider)) - } - @Test @SmallTest - fun hasKey_false() = runBlocking { - val preferenceProvider = new() + fun hasKey_false() = + runBlocking { + val preferenceProvider = new() - assertFalse(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) - } + assertFalse(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) + } @Test @SmallTest - fun put_and_check_key() = runBlocking { - val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" + fun put_and_check_key() = + runBlocking { + val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" - val preferenceProvider = new().apply { - putString(StringDefaultPreferenceFixture.KEY, expectedValue) + val preferenceProvider = + new().apply { + putString(StringDefaultPreferenceFixture.KEY, expectedValue) + } + + assertTrue(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) } - assertTrue(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) - } - // Note: this test case relies on undocumented implementation details of SharedPreferences // e.g. the directory path and the fact the preferences are stored as XML @Test @SmallTest - fun verify_no_plaintext() = runBlocking { - val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" + fun verify_no_plaintext() = + runBlocking { + val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" - new().apply { - putString(StringDefaultPreferenceFixture.KEY, expectedValue) + new().apply { + putString(StringDefaultPreferenceFixture.KEY, expectedValue) + } + + val text = + File( + File(ApplicationProvider.getApplicationContext().dataDir, "shared_prefs"), + "$FILENAME.xml" + ).readText() + + assertFalse(text.contains(expectedValue)) + assertFalse(text.contains(StringDefaultPreferenceFixture.KEY.key)) } - val text = File( - File(ApplicationProvider.getApplicationContext().dataDir, "shared_prefs"), - "$FILENAME.xml" - ).readText() - - assertFalse(text.contains(expectedValue)) - assertFalse(text.contains(StringDefaultPreferenceFixture.KEY.key)) - } - companion object { private val FILENAME = "encrypted_preference_test" - private suspend fun new() = AndroidPreferenceProvider.newEncrypted( - ApplicationProvider.getApplicationContext(), - FILENAME - ) + + private suspend fun new() = + AndroidPreferenceProvider.newEncrypted( + ApplicationProvider.getApplicationContext(), + FILENAME + ) } } diff --git a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/StandardPreferenceProviderTest.kt b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/StandardPreferenceProviderTest.kt index 37f1217d..d7cea569 100644 --- a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/StandardPreferenceProviderTest.kt +++ b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/StandardPreferenceProviderTest.kt @@ -33,41 +33,48 @@ class StandardPreferenceProviderTest { @Test @SmallTest - fun put_and_get_string() = runBlocking { - val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" + fun put_and_get_string() = + runBlocking { + val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" - val preferenceProvider = new().apply { - putString(StringDefaultPreferenceFixture.KEY, expectedValue) + val preferenceProvider = + new().apply { + putString(StringDefaultPreferenceFixture.KEY, expectedValue) + } + + assertEquals(expectedValue, StringDefaultPreferenceFixture.new().getValue(preferenceProvider)) } - assertEquals(expectedValue, StringDefaultPreferenceFixture.new().getValue(preferenceProvider)) - } - @Test @SmallTest - fun hasKey_false() = runBlocking { - val preferenceProvider = new() + fun hasKey_false() = + runBlocking { + val preferenceProvider = new() - assertFalse(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) - } + assertFalse(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) + } @Test @SmallTest - fun put_and_check_key() = runBlocking { - val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" + fun put_and_check_key() = + runBlocking { + val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" - val preferenceProvider = new().apply { - putString(StringDefaultPreferenceFixture.KEY, expectedValue) + val preferenceProvider = + new().apply { + putString(StringDefaultPreferenceFixture.KEY, expectedValue) + } + + assertTrue(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) } - assertTrue(preferenceProvider.hasKey(StringDefaultPreferenceFixture.new().key)) - } - companion object { private val FILENAME = "encrypted_preference_test" - private suspend fun new() = AndroidPreferenceProvider.newStandard( - ApplicationProvider.getApplicationContext(), - FILENAME - ) + + private suspend fun new() = + AndroidPreferenceProvider.newStandard( + ApplicationProvider.getApplicationContext(), + FILENAME + ) } } diff --git a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt index c073bb06..b1f1a30b 100644 --- a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt +++ b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/test/fixture/StringDefaultPreferenceFixture.kt @@ -6,6 +6,7 @@ import co.electriccoin.zcash.preference.model.entry.StringPreferenceDefault object StringDefaultPreferenceFixture { val KEY = PreferenceKey("some_string_key") // $NON-NLS const val DEFAULT_VALUE = "some_default_value" // $NON-NLS + fun new( preferenceKey: PreferenceKey = KEY, value: String = DEFAULT_VALUE diff --git a/preference-impl-android-lib/src/main/java/co/electriccoin/zcash/preference/AndroidPreferenceProvider.kt b/preference-impl-android-lib/src/main/java/co/electriccoin/zcash/preference/AndroidPreferenceProvider.kt index f1ff4958..75933f21 100644 --- a/preference-impl-android-lib/src/main/java/co/electriccoin/zcash/preference/AndroidPreferenceProvider.kt +++ b/preference-impl-android-lib/src/main/java/co/electriccoin/zcash/preference/AndroidPreferenceProvider.kt @@ -26,21 +26,25 @@ import java.util.concurrent.Executors * this instance lives for the lifetime of the application. Constructing multiple instances will * potentially corrupt preference data and will leak resources. */ -/* - * Implementation note: EncryptedSharedPreferences are not thread-safe, so this implementation - * confines them to a single background thread. - */ class AndroidPreferenceProvider( private val sharedPreferences: SharedPreferences, private val dispatcher: CoroutineDispatcher ) : PreferenceProvider { + /* + * Implementation note: EncryptedSharedPreferences are not thread-safe, so this implementation + * confines them to a single background thread. + */ - override suspend fun hasKey(key: PreferenceKey) = withContext(dispatcher) { - sharedPreferences.contains(key.key) - } + override suspend fun hasKey(key: PreferenceKey) = + withContext(dispatcher) { + sharedPreferences.contains(key.key) + } @SuppressLint("ApplySharedPref") - override suspend fun putString(key: PreferenceKey, value: String?) = withContext(dispatcher) { + override suspend fun putString( + key: PreferenceKey, + value: String? + ) = withContext(dispatcher) { val editor = sharedPreferences.edit() editor.putString(key.key, value) @@ -50,65 +54,77 @@ class AndroidPreferenceProvider( Unit } - override suspend fun getString(key: PreferenceKey) = withContext(dispatcher) { - sharedPreferences.getString(key.key, null) - } + override suspend fun getString(key: PreferenceKey) = + withContext(dispatcher) { + sharedPreferences.getString(key.key, null) + } - override fun observe(key: PreferenceKey): Flow = callbackFlow { - val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> - // Callback on main thread + override fun observe(key: PreferenceKey): Flow = + callbackFlow { + val listener = + SharedPreferences.OnSharedPreferenceChangeListener { _, _ -> + // Callback on main thread + trySend(Unit) + } + sharedPreferences.registerOnSharedPreferenceChangeListener(listener) + + // Kickstart the emissions trySend(Unit) - } - sharedPreferences.registerOnSharedPreferenceChangeListener(listener) - // Kickstart the emissions - trySend(Unit) - - awaitClose { - sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) - } - }.flowOn(dispatcher) - .map { getString(key) } + awaitClose { + sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) + } + }.flowOn(dispatcher) + .map { getString(key) } companion object { - suspend fun newStandard(context: Context, filename: String): PreferenceProvider { + suspend fun newStandard( + context: Context, + filename: String + ): PreferenceProvider { /* * Because of this line, we don't want multiple instances of this object created * because we don't clean up the thread afterwards. */ val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() - val sharedPreferences = withContext(singleThreadedDispatcher) { - context.getSharedPreferences(filename, Context.MODE_PRIVATE) - } + val sharedPreferences = + withContext(singleThreadedDispatcher) { + context.getSharedPreferences(filename, Context.MODE_PRIVATE) + } return AndroidPreferenceProvider(sharedPreferences, singleThreadedDispatcher) } - suspend fun newEncrypted(context: Context, filename: String): PreferenceProvider { + suspend fun newEncrypted( + context: Context, + filename: String + ): PreferenceProvider { /* * Because of this line, we don't want multiple instances of this object created * because we don't clean up the thread afterwards. */ val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() - val mainKey = withContext(singleThreadedDispatcher) { - @Suppress("BlockingMethodInNonBlockingContext") - MasterKey.Builder(context).apply { - setKeyScheme(MasterKey.KeyScheme.AES256_GCM) - }.build() - } + val mainKey = + withContext(singleThreadedDispatcher) { + @Suppress("BlockingMethodInNonBlockingContext") + MasterKey.Builder(context).apply { + setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + }.build() + } - val sharedPreferences = withContext(singleThreadedDispatcher) { - @Suppress("BlockingMethodInNonBlockingContext") - EncryptedSharedPreferences.create( - context, - filename, - mainKey, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) - } + val sharedPreferences = + withContext(singleThreadedDispatcher) { + @Suppress("BlockingMethodInNonBlockingContext") + EncryptedSharedPreferences.create( + context, + filename, + mainKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + } return AndroidPreferenceProvider(sharedPreferences, singleThreadedDispatcher) } diff --git a/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/extension/PercentDecimalExtTest.kt b/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/extension/PercentDecimalExtTest.kt index 72f5cc62..8654d3f2 100644 --- a/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/extension/PercentDecimalExtTest.kt +++ b/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/extension/PercentDecimalExtTest.kt @@ -8,52 +8,57 @@ import org.junit.Test import kotlin.test.assertEquals class PercentDecimalExtTest { + @Test + @SmallTest + fun parse_non_zero_percent_decimal_test() = + runTest { + val parsed = PercentDecimal(0.1234f).toPercentageWithDecimal() + + assertEquals("12${MonetarySeparators.current().decimal}34", parsed) + } @Test @SmallTest - fun parse_non_zero_percent_decimal_test() = runTest { - val parsed = PercentDecimal(0.1234f).toPercentageWithDecimal() + fun parse_zero_percent_decimal_test() = + runTest { + val parsed = PercentDecimal(0.0000f).toPercentageWithDecimal() - assertEquals("12${MonetarySeparators.current().decimal}34", parsed) - } + assertEquals("0${MonetarySeparators.current().decimal}00", parsed) + } @Test @SmallTest - fun parse_zero_percent_decimal_test() = runTest { - val parsed = PercentDecimal(0.0000f).toPercentageWithDecimal() + fun parse_max_percent_decimal_test() = + runTest { + val parsed = PercentDecimal(1f).toPercentageWithDecimal() - assertEquals("0${MonetarySeparators.current().decimal}00", parsed) - } + assertEquals("100${MonetarySeparators.current().decimal}00", parsed) + } @Test @SmallTest - fun parse_max_percent_decimal_test() = runTest { - val parsed = PercentDecimal(1f).toPercentageWithDecimal() + fun parse_min_percent_decimal_test() = + runTest { + val parsed = PercentDecimal(0f).toPercentageWithDecimal() - assertEquals("100${MonetarySeparators.current().decimal}00", parsed) - } + assertEquals("0${MonetarySeparators.current().decimal}00", parsed) + } @Test @SmallTest - fun parse_min_percent_decimal_test() = runTest { - val parsed = PercentDecimal(0f).toPercentageWithDecimal() + fun parse_round_down_percent_decimal_test() = + runTest { + val parsed = PercentDecimal(0.11111f).toPercentageWithDecimal() - assertEquals("0${MonetarySeparators.current().decimal}00", parsed) - } + assertEquals("11${MonetarySeparators.current().decimal}11", parsed) + } @Test @SmallTest - fun parse_round_down_percent_decimal_test() = runTest { - val parsed = PercentDecimal(0.11111f).toPercentageWithDecimal() + fun parse_round_up_percent_decimal_test() = + runTest { + val parsed = PercentDecimal(0.11119f).toPercentageWithDecimal() - assertEquals("11${MonetarySeparators.current().decimal}11", parsed) - } - - @Test - @SmallTest - fun parse_round_up_percent_decimal_test() = runTest { - val parsed = PercentDecimal(0.11119f).toPercentageWithDecimal() - - assertEquals("11${MonetarySeparators.current().decimal}12", parsed) - } + assertEquals("11${MonetarySeparators.current().decimal}12", parsed) + } } diff --git a/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/model/ZecRequestTest.kt b/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/model/ZecRequestTest.kt index 81371a8d..326f0447 100644 --- a/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/model/ZecRequestTest.kt +++ b/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/model/ZecRequestTest.kt @@ -15,7 +15,6 @@ import kotlin.test.assertNotNull import kotlin.test.assertTrue class ZecRequestTest { - companion object { private const val URI: String = "zcash:tmXuTnE11JojToagTqxXUn6KvdxDE3iLKbp?amount=1&message=Hello%20world!" @@ -23,100 +22,111 @@ class ZecRequestTest { private val AMOUNT = Zatoshi(1) private val MESSAGE = ZecRequestMessage("Hello world!") private const val ADDRESS_STRING = "tmXuTnE11JojToagTqxXUn6KvdxDE3iLKbp" - private val ADDRESS: WalletAddress.Unified = runBlocking { - WalletAddress.Unified.new(ADDRESS_STRING) - } + private val ADDRESS: WalletAddress.Unified = + runBlocking { + WalletAddress.Unified.new(ADDRESS_STRING) + } val REQUEST = ZecRequest(ADDRESS, AMOUNT, MESSAGE) } @Test @SmallTest - fun parse_uri_not_null() = runTest { - val parsed = ZecRequest.fromUri(Zip321UriParseFixture.URI) + fun parse_uri_not_null() = + runTest { + val parsed = ZecRequest.fromUri(Zip321UriParseFixture.URI) - assertNotNull(parsed) - } + assertNotNull(parsed) + } @Test @SmallTest - fun parse_uri_valid_result() = runTest { - val parsed = ZecRequest.fromUri(Zip321UriParseFixture.URI) + fun parse_uri_valid_result() = + runTest { + val parsed = ZecRequest.fromUri(Zip321UriParseFixture.URI) - assertTrue(parsed.message.value.length <= ZecRequestMessage.MAX_MESSAGE_LENGTH) - assertTrue(parsed.address.address.isNotEmpty()) - assertTrue(parsed.amount.value >= 0) - } + assertTrue(parsed.message.value.length <= ZecRequestMessage.MAX_MESSAGE_LENGTH) + assertTrue(parsed.address.address.isNotEmpty()) + assertTrue(parsed.amount.value >= 0) + } @Test @SmallTest - fun parse_uri_correct_result() = runTest { - val parsed = ZecRequest.fromUri(Zip321UriParseFixture.URI) - val expected = ZecRequest( - WalletAddress.Unified.new(Zip321UriParseFixture.ADDRESS), - Zip321UriParseFixture.AMOUNT, - Zip321UriParseFixture.MESSAGE - ) + fun parse_uri_correct_result() = + runTest { + val parsed = ZecRequest.fromUri(Zip321UriParseFixture.URI) + val expected = + ZecRequest( + WalletAddress.Unified.new(Zip321UriParseFixture.ADDRESS), + Zip321UriParseFixture.AMOUNT, + Zip321UriParseFixture.MESSAGE + ) - assertEquals(parsed, expected) - } + assertEquals(parsed, expected) + } @Test @SmallTest // TODO [#397]: Waiting for an implementation of Uri parser in SDK project @Ignore("Waiting for an implementation of Uri parser in SDK project") - fun parse_uri_incorrect_result() = runTest { - val parsed = ZecRequest.fromUri(URI) - val expected = REQUEST - val actual = ZecRequest( - WalletAddress.Unified.new(Zip321UriParseFixture.ADDRESS), - Zip321UriParseFixture.AMOUNT, - Zip321UriParseFixture.MESSAGE - ) + fun parse_uri_incorrect_result() = + runTest { + val parsed = ZecRequest.fromUri(URI) + val expected = REQUEST + val actual = + ZecRequest( + WalletAddress.Unified.new(Zip321UriParseFixture.ADDRESS), + Zip321UriParseFixture.AMOUNT, + Zip321UriParseFixture.MESSAGE + ) - assertNotEquals(parsed, expected) - assertEquals(parsed, actual) - } + assertNotEquals(parsed, expected) + assertEquals(parsed, actual) + } @Test @SmallTest - fun build_uri_not_null() = runTest { - val request = Zip321UriBuildFixture.REQUEST - val built = request.toUri() + fun build_uri_not_null() = + runTest { + val request = Zip321UriBuildFixture.REQUEST + val built = request.toUri() - assertNotNull(built) - } + assertNotNull(built) + } @Test @SmallTest - fun build_uri_valid_result() = runTest { - val request = Zip321UriBuildFixture.REQUEST - val built = request.toUri() + fun build_uri_valid_result() = + runTest { + val request = Zip321UriBuildFixture.REQUEST + val built = request.toUri() - assertTrue(built.isNotEmpty()) - assertTrue(built.startsWith("zcash")) - } + assertTrue(built.isNotEmpty()) + assertTrue(built.startsWith("zcash")) + } @Test @SmallTest - fun built_uri_correct_result() = runTest { - val request = Zip321UriBuildFixture.REQUEST - val built = request.toUri() - val expected = Zip321UriBuildFixture.URI + fun built_uri_correct_result() = + runTest { + val request = Zip321UriBuildFixture.REQUEST + val built = request.toUri() + val expected = Zip321UriBuildFixture.URI - assertEquals(built, expected) - } + assertEquals(built, expected) + } @Test @SmallTest // TODO [#397]: Waiting for an implementation of Uri parser in SDK project @Ignore("Waiting for an implementation of Uri parser in SDK project") - fun build_uri_incorrect_result() = runTest { - val request = Zip321UriBuildFixture.REQUEST - val built = request.toUri() - val expected = URI - val actual = Zip321UriBuildFixture.URI + fun build_uri_incorrect_result() = + runTest { + val request = Zip321UriBuildFixture.REQUEST + val built = request.toUri() + val expected = URI + val actual = Zip321UriBuildFixture.URI - assertNotEquals(built, expected) - assertEquals(built, actual) - } + assertNotEquals(built, expected) + assertEquals(built, actual) + } } diff --git a/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/type/ZcashCurrencyTest.kt b/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/type/ZcashCurrencyTest.kt index 84959e33..b631a91e 100644 --- a/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/type/ZcashCurrencyTest.kt +++ b/sdk-ext-lib/src/androidTest/java/cash/z/ecc/sdk/type/ZcashCurrencyTest.kt @@ -7,7 +7,6 @@ import org.junit.Test import kotlin.test.assertNotSame class ZcashCurrencyTest { - @SmallTest @Test fun check_is_zec_type() { diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/PercentDecimalExt.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/PercentDecimalExt.kt index b521676e..50841c00 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/PercentDecimalExt.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/PercentDecimalExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package cash.z.ecc.sdk.extension @@ -12,9 +12,10 @@ fun PercentDecimal.toPercentageWithDecimal(decimalFormat: DecimalFormat = prepar return decimalFormat.format(decimal * 100) } -private fun preparePercentDecimalFormat(): DecimalFormat = DecimalFormat().apply { - val monetarySeparators = MonetarySeparators.current() - val localizedPattern = "##0${monetarySeparators.decimal}00" - applyLocalizedPattern(localizedPattern) - roundingMode = RoundingMode.HALF_UP -} +private fun preparePercentDecimalFormat(): DecimalFormat = + DecimalFormat().apply { + val monetarySeparators = MonetarySeparators.current() + val localizedPattern = "##0${monetarySeparators.decimal}00" + applyLocalizedPattern(localizedPattern) + roundingMode = RoundingMode.HALF_UP + } diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/SynchronizerExt.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/SynchronizerExt.kt index ae886f0e..e8030261 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/SynchronizerExt.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/extension/SynchronizerExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package cash.z.ecc.sdk.extension @@ -6,7 +6,10 @@ import cash.z.ecc.android.sdk.Synchronizer import cash.z.ecc.android.sdk.model.UnifiedSpendingKey import cash.z.ecc.android.sdk.model.ZecSend -suspend fun Synchronizer.send(spendingKey: UnifiedSpendingKey, send: ZecSend) = sendToAddress( +suspend fun Synchronizer.send( + spendingKey: UnifiedSpendingKey, + send: ZecSend +) = sendToAddress( spendingKey, send.amount, send.destination.address, diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/PersistableWalletFixture.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/PersistableWalletFixture.kt index 1aeccc7d..6974c53b 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/PersistableWalletFixture.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/PersistableWalletFixture.kt @@ -9,7 +9,6 @@ import cash.z.ecc.android.sdk.model.ZcashNetwork import co.electriccoin.lightwallet.client.model.LightWalletEndpoint object PersistableWalletFixture { - val NETWORK = ZcashNetwork.Mainnet val ENDPOINT = LightWalletEndpoint.Mainnet diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/SeedPhraseFixture.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/SeedPhraseFixture.kt index 7c8ff73d..00b3b54d 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/SeedPhraseFixture.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/SeedPhraseFixture.kt @@ -3,8 +3,9 @@ package cash.z.ecc.sdk.fixture import cash.z.ecc.android.sdk.model.SeedPhrase object SeedPhraseFixture { - @Suppress("MaxLineLength") - val SEED_PHRASE = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" + const val SEED_PHRASE = + "still champion voice habit trend flight survey between bitter process artefact blind carbon " + + "truly provide dizzy crush flush breeze blouse charge solid fish spread" fun new(seedPhrase: String = SEED_PHRASE) = SeedPhrase.new(seedPhrase) } diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriBuildFixture.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriBuildFixture.kt index aa90de27..64cb220d 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriBuildFixture.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriBuildFixture.kt @@ -9,15 +9,17 @@ import kotlinx.coroutines.runBlocking object Zip321UriBuildFixture { // TODO [#161]: Pending SDK support - const val URI: String = "zcash:Unified%20GitHub%20Issue%20#161?amount=123&message=Thank%20you%20" + - "for%20your%20purchase" + const val URI: String = + "zcash:Unified%20GitHub%20Issue%20#161?amount=123&message=Thank%20you%20" + + "for%20your%20purchase" @Suppress("MagicNumber") val AMOUNT = Zatoshi(123) val MESSAGE = ZecRequestMessage("Thank you for your purchase") - val ADDRESS: WalletAddress.Unified = runBlocking { - WalletAddress.Unified.new(WalletAddressFixture.UNIFIED_ADDRESS_STRING) - } + val ADDRESS: WalletAddress.Unified = + runBlocking { + WalletAddress.Unified.new(WalletAddressFixture.UNIFIED_ADDRESS_STRING) + } val REQUEST = ZecRequest(ADDRESS, AMOUNT, MESSAGE) // TODO [#397]: Waiting for an implementation of Uri parser in SDK project diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriParseFixture.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriParseFixture.kt index b49fe89c..d1828a7a 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriParseFixture.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/fixture/Zip321UriParseFixture.kt @@ -8,8 +8,9 @@ import cash.z.ecc.sdk.model.ZecRequestMessage object Zip321UriParseFixture { // TODO [#161]: Pending SDK support - const val URI: String = "zcash:Unified%20GitHub%20Issue%20#161?amount=123&message=Thank%20you%20" + - "for%20your%20purchase" + const val URI: String = + "zcash:Unified%20GitHub%20Issue%20#161?amount=123&message=Thank%20you%20" + + "for%20your%20purchase" const val ADDRESS: String = WalletAddressFixture.UNIFIED_ADDRESS_STRING @@ -20,7 +21,5 @@ object Zip321UriParseFixture { // TODO [#397]: Waiting for an implementation of Uri parser in SDK project // Should return ZecRequest.fromUri(toParse) ideally, but it'd end up with an infinite loop for now. @Suppress("UNUSED_PARAMETER") - suspend fun new( - toParse: String = URI - ) = ZecRequest(WalletAddress.Unified.new(ADDRESS), AMOUNT, MESSAGE) + suspend fun new(toParse: String = URI) = ZecRequest(WalletAddress.Unified.new(ADDRESS), AMOUNT, MESSAGE) } diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/SeedPhraseValidation.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/SeedPhraseValidation.kt index ff216317..c2ca4a92 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/SeedPhraseValidation.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/SeedPhraseValidation.kt @@ -10,8 +10,11 @@ import java.util.Locale // there as part of creating the object sealed class SeedPhraseValidation { object BadCount : SeedPhraseValidation() + object BadWord : SeedPhraseValidation() + object FailedChecksum : SeedPhraseValidation() + class Valid(val seedPhrase: SeedPhrase) : SeedPhraseValidation() companion object { diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/ZecRequest.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/ZecRequest.kt index 58de66ee..ea3f09d2 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/ZecRequest.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/model/ZecRequest.kt @@ -6,7 +6,6 @@ import cash.z.ecc.sdk.fixture.Zip321UriBuildFixture import cash.z.ecc.sdk.fixture.Zip321UriParseFixture 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 { diff --git a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/type/ZcashNetwork.kt b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/type/ZcashNetwork.kt index 2f7e6287..fbbd793c 100644 --- a/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/type/ZcashNetwork.kt +++ b/sdk-ext-lib/src/main/java/cash/z/ecc/sdk/type/ZcashNetwork.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package cash.z.ecc.sdk.type @@ -21,6 +21,7 @@ import cash.z.ecc.sdk.ext.R * - Using a ContentProvider for dynamic injection, where the URI is defined * - Using AndroidManifest metadata for dynamic injection */ + /** * @return Zcash network determined from resources. A resource overlay of [R.bool.zcash_is_testnet] * can be used for different build variants to change the network type. diff --git a/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProviderTest.kt b/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProviderTest.kt index 87e4c23f..69b4d7d9 100644 --- a/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProviderTest.kt +++ b/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProviderTest.kt @@ -13,16 +13,19 @@ class AbstractProcessNameContentProviderTest { @SmallTest fun getProcessName_from_provider_info() { val expectedApplicationProcessName = "beep" // $NON-NLS - val ctx: ContextWrapper = object : ContextWrapper(ApplicationProvider.getApplicationContext()) { - override fun getApplicationInfo() = ApplicationInfo().apply { - processName = expectedApplicationProcessName + val ctx: ContextWrapper = + object : ContextWrapper(ApplicationProvider.getApplicationContext()) { + override fun getApplicationInfo() = + ApplicationInfo().apply { + processName = expectedApplicationProcessName + } } - } - val actualProcessName = AbstractProcessNameContentProvider.getProcessNameLegacy( - ctx, - ProviderInfo() - ) + val actualProcessName = + AbstractProcessNameContentProvider.getProcessNameLegacy( + ctx, + ProviderInfo() + ) assertEquals(expectedApplicationProcessName, actualProcessName) } diff --git a/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/VersionCodeCompatTest.kt b/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/VersionCodeCompatTest.kt index 6e16da56..3a2f8a96 100644 --- a/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/VersionCodeCompatTest.kt +++ b/spackle-android-lib/src/androidTest/java/co/electriccoin/zcash/spackle/process/VersionCodeCompatTest.kt @@ -13,13 +13,14 @@ class VersionCodeCompatTest { fun versionCodeCompat() { val expectedVersionCode = 123L - val packageInfo = PackageInfo().apply { - @Suppress("Deprecation") - versionCode = expectedVersionCode.toInt() - if (AndroidApiVersion.isAtLeastT) { - longVersionCode = expectedVersionCode + val packageInfo = + PackageInfo().apply { + @Suppress("Deprecation") + versionCode = expectedVersionCode.toInt() + if (AndroidApiVersion.isAtLeastT) { + longVersionCode = expectedVersionCode + } } - } assertEquals(expectedVersionCode, packageInfo.versionCodeCompat) } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt index 6bb6a11c..87acc051 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt @@ -11,7 +11,9 @@ object AndroidApiVersion { * [sdk]. */ @ChecksSdkIntAtLeast(parameter = 0) - fun isAtLeast(@IntRange(from = Build.VERSION_CODES.BASE.toLong()) sdk: Int): Boolean { + fun isAtLeast( + @IntRange(from = Build.VERSION_CODES.BASE.toLong()) sdk: Int + ): Boolean { return Build.VERSION.SDK_INT >= sdk } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerExt.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerExt.kt index 19302aef..fdc1fce2 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerExt.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.spackle @@ -7,6 +7,7 @@ import android.content.ClipboardManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -suspend fun ClipboardManager.setPrimaryClipSuspend(data: ClipData) = withContext(Dispatchers.IO) { - setPrimaryClip(data) -} +suspend fun ClipboardManager.setPrimaryClipSuspend(data: ClipData) = + withContext(Dispatchers.IO) { + setPrimaryClip(data) + } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerUtil.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerUtil.kt index 12f957dd..ba7ffe56 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerUtil.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ClipboardManagerUtil.kt @@ -7,7 +7,6 @@ import android.widget.Toast import kotlinx.coroutines.runBlocking object ClipboardManagerUtil { - fun copyToClipboard( context: Context, label: String, @@ -15,10 +14,11 @@ object ClipboardManagerUtil { ) { Twig.info { "Copied to clipboard: label: $label, value: $value" } val clipboardManager = context.getSystemService(ClipboardManager::class.java) - val data = ClipData.newPlainText( - label, - value - ) + val data = + ClipData.newPlainText( + label, + value + ) if (AndroidApiVersion.isAtLeastT) { // API 33 and later implement their system Toast UI. clipboardManager.setPrimaryClip(data) diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ContextExt.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ContextExt.kt index fb437093..b5462a0f 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ContextExt.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/ContextExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.spackle @@ -6,6 +6,7 @@ import android.content.Context import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -suspend fun Context.getExternalFilesDirSuspend(type: String?) = withContext(Dispatchers.IO) { - getExternalFilesDir(type) -} +suspend fun Context.getExternalFilesDirSuspend(type: String?) = + withContext(Dispatchers.IO) { + getExternalFilesDir(type) + } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/CoroutineBroadcastReceiver.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/CoroutineBroadcastReceiver.kt index ae9007d6..7c7843bb 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/CoroutineBroadcastReceiver.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/CoroutineBroadcastReceiver.kt @@ -11,7 +11,10 @@ import kotlinx.coroutines.launch * It is not recommended to cancel this scope. */ abstract class CoroutineBroadcastReceiver(private val broadcastReceiverScope: CoroutineScope) : BroadcastReceiver() { - final override fun onReceive(context: Context, intent: Intent) { + final override fun onReceive( + context: Context, + intent: Intent + ) { val pendingResult = goAsync() broadcastReceiverScope.launch { @@ -29,5 +32,8 @@ abstract class CoroutineBroadcastReceiver(private val broadcastReceiverScope: Co * the Android timeout for broadcast receivers. This method is suitable for brief disk IO but * not suitable for network calls. */ - abstract suspend fun onReceiveSuspend(context: Context, intent: Intent) + abstract suspend fun onReceiveSuspend( + context: Context, + intent: Intent + ) } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/EmulatorWtfUtil.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/EmulatorWtfUtil.kt index 13e50860..5a3d05ed 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/EmulatorWtfUtil.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/EmulatorWtfUtil.kt @@ -11,9 +11,10 @@ object EmulatorWtfUtil { private const val EMULATOR_WTF_SETTING = "emulator.wtf" // $NON-NLS private const val SETTING_TRUE = "true" // $NON-NLS - private val isEmulatorWtfCached = LazyWithArgument { - isEmulatorWtfImpl(it) - } + private val isEmulatorWtfCached = + LazyWithArgument { + isEmulatorWtfImpl(it) + } /** * @return True if the environment is emulator.wtf diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/FirebaseTestLabUtil.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/FirebaseTestLabUtil.kt index 2b064c1e..05c61f28 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/FirebaseTestLabUtil.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/FirebaseTestLabUtil.kt @@ -11,9 +11,10 @@ object FirebaseTestLabUtil { private const val FIREBASE_TEST_LAB_SETTING = "firebase.test.lab" // $NON-NLS private const val SETTING_TRUE = "true" // $NON-NLS - private val isFirebaseTestLabCached = LazyWithArgument { - isFirebaseTestLabImpl(it) - } + private val isFirebaseTestLabCached = + LazyWithArgument { + isFirebaseTestLabImpl(it) + } /** * @return True if the environment is Firebase Test Lab. @@ -24,10 +25,10 @@ object FirebaseTestLabUtil { /* * Per the documentation at https://firebase.google.com/docs/test-lab/android-studio */ - // Tested with the benchmark library, this is very fast. There shouldn't be a need to make - // this a suspend function. That said, we'll still cache the result as a just-in-case - // since IPC may be involved. return runCatching { + // Tested with the benchmark library, this is very fast. There shouldn't be a need to make + // this a suspend function. That said, we'll still cache the result as a just-in-case + // since IPC may be involved. SETTING_TRUE == Settings.System.getString(context.contentResolver, FIREBASE_TEST_LAB_SETTING) }.recover { // Fail-safe in case an error occurs diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/PackageManagerCompat.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/PackageManagerCompat.kt index 256f0cd3..e6097249 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/PackageManagerCompat.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/PackageManagerCompat.kt @@ -7,14 +7,20 @@ import android.os.Build import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -fun PackageManager.getPackageInfoCompat(packageName: String, flags: Long): PackageInfo = +fun PackageManager.getPackageInfoCompat( + packageName: String, + flags: Long +): PackageInfo = if (AndroidApiVersion.isAtLeastT) { getPackageInfoTPlus(packageName, flags) } else { getPackageInfoLegacy(packageName, flags) } -suspend fun PackageManager.getPackageInfoCompatSuspend(packageName: String, flags: Long): PackageInfo = +suspend fun PackageManager.getPackageInfoCompatSuspend( + packageName: String, + flags: Long +): PackageInfo = if (AndroidApiVersion.isAtLeastT) { withContext(Dispatchers.IO) { getPackageInfoTPlus(packageName, flags) } } else { @@ -22,9 +28,13 @@ suspend fun PackageManager.getPackageInfoCompatSuspend(packageName: String, flag } @TargetApi(Build.VERSION_CODES.TIRAMISU) -private fun PackageManager.getPackageInfoTPlus(packageName: String, flags: Long) = - getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags)) +private fun PackageManager.getPackageInfoTPlus( + packageName: String, + flags: Long +) = getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags)) @Suppress("Deprecation") -private fun PackageManager.getPackageInfoLegacy(packageName: String, flags: Long) = - getPackageInfo(packageName, flags.toInt()) +private fun PackageManager.getPackageInfoLegacy( + packageName: String, + flags: Long +) = getPackageInfo(packageName, flags.toInt()) diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt index efe383cc..9d29b453 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.os.StrictMode object StrictModeCompat { - fun enableStrictMode(isCrashOnViolation: Boolean) { configureStrictMode(isCrashOnViolation) } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/Twig.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/Twig.kt index 5b3aed04..b97c7de8 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/Twig.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/Twig.kt @@ -40,7 +40,10 @@ object Twig { // JVMStatic is to simplify ProGuard/R8 rules for stripping this @JvmStatic - fun verbose(throwable: Throwable, message: () -> String) { + fun verbose( + throwable: Throwable, + message: () -> String + ) { Log.v(tag, formatMessage(message), throwable) } @@ -52,7 +55,10 @@ object Twig { // JVMStatic is to simplify ProGuard/R8 rules for stripping this @JvmStatic - fun debug(throwable: Throwable, message: () -> String) { + fun debug( + throwable: Throwable, + message: () -> String + ) { Log.d(tag, formatMessage(message), throwable) } @@ -64,7 +70,10 @@ object Twig { // JVMStatic is to simplify ProGuard/R8 rules for stripping this @JvmStatic - fun info(throwable: Throwable, message: () -> String) { + fun info( + throwable: Throwable, + message: () -> String + ) { Log.i(tag, formatMessage(message), throwable) } @@ -76,7 +85,10 @@ object Twig { // JVMStatic is to simplify ProGuard/R8 rules for stripping this @JvmStatic - fun warn(throwable: Throwable, message: () -> String) { + fun warn( + throwable: Throwable, + message: () -> String + ) { Log.w(tag, formatMessage(message), throwable) } @@ -88,7 +100,10 @@ object Twig { // JVMStatic is to simplify ProGuard/R8 rules for stripping this @JvmStatic - fun error(throwable: Throwable, message: () -> String) { + fun error( + throwable: Throwable, + message: () -> String + ) { Log.e(tag, formatMessage(message), throwable) } @@ -96,11 +111,12 @@ object Twig { * Can be called in a release build to test that `assumenosideeffects` ProGuard rules have been * properly processed to strip out logging messages. */ - // JVMStatic is to simplify ProGuard/R8 rules for stripping this - @JvmStatic + @JvmStatic // JVMStatic is to simplify ProGuard/R8 rules for stripping this fun assertLoggingStripped() { - @Suppress("MaxLineLength") - throw AssertionError("Logging was not disabled by ProGuard or R8. Logging should be disabled in release builds to reduce risk of sensitive information being leaked.") // $NON-NLS-1$ + throw AssertionError( + "Logging was not disabled by ProGuard or R8. Logging should be disabled in release builds to reduce risk " + + "of sensitive information being leaked." + ) // $NON-NLS-1$ } private const val CALL_DEPTH = 4 diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/VersionCodeCompat.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/VersionCodeCompat.kt index f9b98b84..d7c4fbed 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/VersionCodeCompat.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/VersionCodeCompat.kt @@ -5,11 +5,12 @@ import android.content.pm.PackageInfo import android.os.Build val PackageInfo.versionCodeCompat - get() = if (AndroidApiVersion.isAtLeastP) { - getVersionCodePPlus() - } else { - versionCodeLegacy.toLong() - } + get() = + if (AndroidApiVersion.isAtLeastP) { + getVersionCodePPlus() + } else { + versionCodeLegacy.toLong() + } @Suppress("Deprecation") private val PackageInfo.versionCodeLegacy diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProvider.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProvider.kt index 77aaade7..04ef4784 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProvider.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/AbstractProcessNameContentProvider.kt @@ -20,16 +20,20 @@ import co.electriccoin.zcash.spackle.AndroidApiVersion open class AbstractProcessNameContentProvider : ContentProvider() { override fun onCreate() = true - override fun attachInfo(context: Context, info: ProviderInfo) { + override fun attachInfo( + context: Context, + info: ProviderInfo + ) { super.attachInfo(context, info) - val processName: String = if (AndroidApiVersion.isAtLeastT) { - getProcessNameTPlus() - } else if (AndroidApiVersion.isAtLeastP) { - getProcessNamePPlus() - } else { - getProcessNameLegacy(context, info) - } + val processName: String = + if (AndroidApiVersion.isAtLeastT) { + getProcessNameTPlus() + } else if (AndroidApiVersion.isAtLeastP) { + getProcessNamePPlus() + } else { + getProcessNameLegacy(context, info) + } ProcessNameCompat.setProcessName(processName) } @@ -54,11 +58,18 @@ open class AbstractProcessNameContentProvider : ContentProvider() { throw UnsupportedOperationException() } - override fun insert(uri: Uri, values: ContentValues?): Uri? { + override fun insert( + uri: Uri, + values: ContentValues? + ): Uri? { throw UnsupportedOperationException() } - override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + override fun delete( + uri: Uri, + selection: String?, + selectionArgs: Array? + ): Int { throw UnsupportedOperationException() } @@ -72,7 +83,9 @@ open class AbstractProcessNameContentProvider : ContentProvider() { } companion object { - internal fun getProcessNameLegacy(context: Context, info: ProviderInfo) = - info.processName ?: context.applicationInfo.processName ?: context.packageName + internal fun getProcessNameLegacy( + context: Context, + info: ProviderInfo + ) = info.processName ?: context.applicationInfo.processName ?: context.packageName } } diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/ProcessNameCompat.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/ProcessNameCompat.kt index 2ef8952c..f7cfb15d 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/ProcessNameCompat.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/process/ProcessNameCompat.kt @@ -20,7 +20,6 @@ import co.electriccoin.zcash.spackle.process.ProcessNameCompat.getProcessName * way to get process name on older Android versions. */ object ProcessNameCompat { - // GuardedBy intrinsicLock private var processName: String? = null diff --git a/spackle-lib/src/commonTest/kotlin/co/electriccoin/zcash/spackle/model/ProgressTest.kt b/spackle-lib/src/commonTest/kotlin/co/electriccoin/zcash/spackle/model/ProgressTest.kt index fb689b87..419b5ff2 100644 --- a/spackle-lib/src/commonTest/kotlin/co/electriccoin/zcash/spackle/model/ProgressTest.kt +++ b/spackle-lib/src/commonTest/kotlin/co/electriccoin/zcash/spackle/model/ProgressTest.kt @@ -4,7 +4,6 @@ import kotlin.test.Test import kotlin.test.assertFailsWith class ProgressTest { - @Test fun last_greater_than_zero() { assertFailsWith(IllegalArgumentException::class) { diff --git a/spackle-lib/src/jvmMain/kotlin/co/electriccoin/zcash/spackle/io/FileExt.kt b/spackle-lib/src/jvmMain/kotlin/co/electriccoin/zcash/spackle/io/FileExt.kt index 6f744b69..f63a6291 100644 --- a/spackle-lib/src/jvmMain/kotlin/co/electriccoin/zcash/spackle/io/FileExt.kt +++ b/spackle-lib/src/jvmMain/kotlin/co/electriccoin/zcash/spackle/io/FileExt.kt @@ -8,37 +8,45 @@ import java.io.File import java.io.IOException import java.util.UUID -suspend fun File.existsSuspend() = withContext(Dispatchers.IO) { - exists() -} +suspend fun File.existsSuspend() = + withContext(Dispatchers.IO) { + exists() + } -suspend fun File.mkdirsSuspend() = withContext(Dispatchers.IO) { - mkdirs() -} +suspend fun File.mkdirsSuspend() = + withContext(Dispatchers.IO) { + mkdirs() + } -suspend fun File.isDirectorySuspend() = withContext(Dispatchers.IO) { - isDirectory -} +suspend fun File.isDirectorySuspend() = + withContext(Dispatchers.IO) { + isDirectory + } -suspend fun File.isFileSuspend() = withContext(Dispatchers.IO) { - isFile -} +suspend fun File.isFileSuspend() = + withContext(Dispatchers.IO) { + isFile + } -suspend fun File.canWriteSuspend() = withContext(Dispatchers.IO) { - canWrite() -} +suspend fun File.canWriteSuspend() = + withContext(Dispatchers.IO) { + canWrite() + } -suspend fun File.deleteSuspend() = withContext(Dispatchers.IO) { - delete() -} +suspend fun File.deleteSuspend() = + withContext(Dispatchers.IO) { + delete() + } -suspend fun File.renameToSuspend(destination: File) = withContext(Dispatchers.IO) { - renameTo(destination) -} +suspend fun File.renameToSuspend(destination: File) = + withContext(Dispatchers.IO) { + renameTo(destination) + } -suspend fun File.listFilesSuspend() = withContext(Dispatchers.IO) { - listFiles() -} +suspend fun File.listFilesSuspend() = + withContext(Dispatchers.IO) { + listFiles() + } /** * Given an ultimate output file destination, this generates a temporary file that [action] can write to. After action @@ -50,18 +58,21 @@ suspend fun File.listFilesSuspend() = withContext(Dispatchers.IO) { * delete, rename, or do other operations in the filesystem. */ suspend fun File.writeAtomically(action: (suspend (File) -> Unit)) { - val tempFile = withContext(Dispatchers.IO) { - File(parentFile, name.newTempFileName()).also { - it.deleteOnExit() + val tempFile = + withContext(Dispatchers.IO) { + File(parentFile, name.newTempFileName()).also { + it.deleteOnExit() + } } - } var isWriteSuccessful = false try { action(tempFile) isWriteSuccessful = true - } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { + } catch ( + @Suppress("TooGenericExceptionCaught") e: Exception + ) { tempFile.deleteSuspend() throw e } finally { diff --git a/spackle-lib/src/jvmTest/kotlin/co/electriccoin/zcash/spackle/io/WriteAtomicallyTest.kt b/spackle-lib/src/jvmTest/kotlin/co/electriccoin/zcash/spackle/io/WriteAtomicallyTest.kt index 9df28f52..961cbb64 100644 --- a/spackle-lib/src/jvmTest/kotlin/co/electriccoin/zcash/spackle/io/WriteAtomicallyTest.kt +++ b/spackle-lib/src/jvmTest/kotlin/co/electriccoin/zcash/spackle/io/WriteAtomicallyTest.kt @@ -16,47 +16,50 @@ class WriteAtomicallyTest { private fun newFile() = File(File("build"), "atomic_file_test-${UUID.randomUUID()}") @Test - fun `file has temp name`() = runTest { - val testFile = newFile() - try { - testFile.writeAtomically { - it.writeText("test text") - assertNotEquals(testFile.name, it.name) + fun `file has temp name`() = + runTest { + val testFile = newFile() + try { + testFile.writeAtomically { + it.writeText("test text") + assertNotEquals(testFile.name, it.name) + } + } finally { + testFile.delete() } - } finally { - testFile.delete() } - } @Test - fun `temp file deleted`() = runTest { - val testFile = newFile() - try { - var tempFile: File? = null + fun `temp file deleted`() = + runTest { + val testFile = newFile() + try { + var tempFile: File? = null - testFile.writeAtomically { - tempFile = it - it.writeText("test text") + testFile.writeAtomically { + tempFile = it + it.writeText("test text") + } + + assertNotNull(tempFile) + assertFalse(tempFile!!.exists()) + } finally { + testFile.delete() } - - assertNotNull(tempFile) - assertFalse(tempFile!!.exists()) - } finally { - testFile.delete() } - } @Test - fun `file is renamed`() = runTest { - val testFile = newFile() - try { - testFile.writeAtomically { - it.writeText("test text") - } + fun `file is renamed`() = + runTest { + val testFile = newFile() + try { + testFile.writeAtomically { + it.writeText("test text") + } - assertTrue(testFile.exists()) - } finally { - testFile.delete() + assertTrue(testFile.exists()) + } finally { + testFile.delete() + } } - } } diff --git a/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt b/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt index c16056b0..d42d21a3 100644 --- a/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt +++ b/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt @@ -10,8 +10,9 @@ import org.junit.Before * Subclass this in view unit and integration tests. This verifies that * prerequisites necessary for reliable UI tests are met, and it provides more useful error messages. */ -// Originally hoped to put this into ZcashUiTestRunner, although it causes reporting of test results to fail open class UiTestPrerequisites { + // Originally hoped to put this into ZcashUiTestRunner, although it causes reporting of test results to fail + @Before fun verifyPrerequisites() { assertScreenIsOn() @@ -26,8 +27,9 @@ open class UiTestPrerequisites { } private fun isScreenOn(): Boolean { - val powerService = ApplicationProvider.getApplicationContext() - .getSystemService(Context.POWER_SERVICE) as PowerManager + val powerService = + ApplicationProvider.getApplicationContext() + .getSystemService(Context.POWER_SERVICE) as PowerManager return powerService.isInteractive } @@ -41,7 +43,7 @@ open class UiTestPrerequisites { val keyguardService = ( ApplicationProvider.getApplicationContext() .getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager - ) + ) return keyguardService.isKeyguardLocked } diff --git a/test-lib/src/main/kotlin/co/electriccoin/zcash/test/ZcashUiTestRunner.kt b/test-lib/src/main/kotlin/co/electriccoin/zcash/test/ZcashUiTestRunner.kt index e27d7004..1e54cf84 100644 --- a/test-lib/src/main/kotlin/co/electriccoin/zcash/test/ZcashUiTestRunner.kt +++ b/test-lib/src/main/kotlin/co/electriccoin/zcash/test/ZcashUiTestRunner.kt @@ -12,8 +12,9 @@ class ZcashUiTestRunner : AndroidJUnitRunner() { override fun onCreate(arguments: Bundle?) { super.onCreate(arguments) - val powerManager = ApplicationProvider.getApplicationContext() - .getSystemService(Context.POWER_SERVICE) as PowerManager + val powerManager = + ApplicationProvider.getApplicationContext() + .getSystemService(Context.POWER_SERVICE) as PowerManager // There is no alternative to this deprecated API. The suggestion of a view to keep the screen // on won't work well for our tests. diff --git a/tools/.editorconfig b/tools/.editorconfig index 0ba3b084..840b2a5a 100644 --- a/tools/.editorconfig +++ b/tools/.editorconfig @@ -2,4 +2,6 @@ root = true [*.{kt,kts}] ktlint_standard_trailing-comma-on-call-site = disabled -ktlint_standard_trailing-comma-on-declaration-site = disabled \ No newline at end of file +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 \ No newline at end of file diff --git a/ui-benchmark-test/src/main/java/co/electriccoin/zcash/ui/benchmark/BasicStartupBenchmark.kt b/ui-benchmark-test/src/main/java/co/electriccoin/zcash/ui/benchmark/BasicStartupBenchmark.kt index ed8c05a0..fe168146 100644 --- a/ui-benchmark-test/src/main/java/co/electriccoin/zcash/ui/benchmark/BasicStartupBenchmark.kt +++ b/ui-benchmark-test/src/main/java/co/electriccoin/zcash/ui/benchmark/BasicStartupBenchmark.kt @@ -18,7 +18,6 @@ import org.junit.Test * version and later on. */ class BasicStartupBenchmark { - companion object { private const val APP_TARGET_PACKAGE_NAME = "co.electriccoin.zcash" } @@ -27,13 +26,14 @@ class BasicStartupBenchmark { val benchmarkRule = MacrobenchmarkRule() @Test - fun startup() = benchmarkRule.measureRepeated( - packageName = APP_TARGET_PACKAGE_NAME, - metrics = listOf(StartupTimingMetric()), - iterations = 5, - startupMode = StartupMode.COLD - ) { - pressHome() - startActivityAndWait() - } + fun startup() = + benchmarkRule.measureRepeated( + packageName = APP_TARGET_PACKAGE_NAME, + metrics = listOf(StartupTimingMetric()), + iterations = 5, + startupMode = StartupMode.COLD + ) { + pressHome() + startActivityAndWait() + } } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt index c45a3be7..dffe06bc 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt @@ -59,10 +59,11 @@ fun PrimaryButton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, - outerPaddingValues: PaddingValues = PaddingValues( - horizontal = ZcashTheme.dimens.spacingNone, - vertical = ZcashTheme.dimens.spacingSmall - ), + outerPaddingValues: PaddingValues = + PaddingValues( + horizontal = ZcashTheme.dimens.spacingNone, + vertical = ZcashTheme.dimens.spacingSmall + ), enabled: Boolean = true, buttonColor: Color = MaterialTheme.colorScheme.primary, textColor: Color = MaterialTheme.colorScheme.onPrimary, @@ -70,27 +71,30 @@ fun PrimaryButton( Button( shape = RectangleShape, enabled = enabled, - modifier = modifier.then(Modifier.fillMaxWidth()) - .padding(outerPaddingValues) - .shadow( - contentColor = textColor, - strokeColor = buttonColor, - strokeWidth = 1.dp, - offsetX = ZcashTheme.dimens.buttonShadowOffsetX, - offsetY = ZcashTheme.dimens.buttonShadowOffsetY, - spread = ZcashTheme.dimens.buttonShadowSpread, - ) - .translationClick( - translationX = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp, // + 6dp to exactly cover the bottom shadow - translationY = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp - ) - .defaultMinSize(ZcashTheme.dimens.buttonWidth, ZcashTheme.dimens.buttonHeight) - .border(1.dp, Color.Black), - colors = buttonColors( - containerColor = buttonColor, - disabledContainerColor = ZcashTheme.colors.disabledButtonColor, - disabledContentColor = ZcashTheme.colors.disabledButtonTextColor - ), + modifier = + modifier.then(Modifier.fillMaxWidth()) + .padding(outerPaddingValues) + .shadow( + contentColor = textColor, + strokeColor = buttonColor, + strokeWidth = 1.dp, + offsetX = ZcashTheme.dimens.buttonShadowOffsetX, + offsetY = ZcashTheme.dimens.buttonShadowOffsetY, + spread = ZcashTheme.dimens.buttonShadowSpread, + ) + .translationClick( + // + 6dp to exactly cover the bottom shadow + translationX = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp, + translationY = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp + ) + .defaultMinSize(ZcashTheme.dimens.buttonWidth, ZcashTheme.dimens.buttonHeight) + .border(1.dp, Color.Black), + colors = + buttonColors( + containerColor = buttonColor, + disabledContainerColor = ZcashTheme.colors.disabledButtonColor, + disabledContentColor = ZcashTheme.colors.disabledButtonTextColor + ), onClick = onClick, ) { Text( @@ -108,10 +112,11 @@ fun SecondaryButton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, - outerPaddingValues: PaddingValues = PaddingValues( - horizontal = ZcashTheme.dimens.spacingNone, - vertical = ZcashTheme.dimens.spacingSmall - ), + outerPaddingValues: PaddingValues = + PaddingValues( + horizontal = ZcashTheme.dimens.spacingNone, + vertical = ZcashTheme.dimens.spacingSmall + ), enabled: Boolean = true, buttonColor: Color = MaterialTheme.colorScheme.secondary, textColor: Color = MaterialTheme.colorScheme.onSecondary, @@ -119,26 +124,29 @@ fun SecondaryButton( Button( shape = RectangleShape, enabled = enabled, - modifier = modifier.then(Modifier.fillMaxWidth()) - .padding(outerPaddingValues) - .shadow( - contentColor = textColor, - strokeColor = textColor, - offsetX = ZcashTheme.dimens.buttonShadowOffsetX, - offsetY = ZcashTheme.dimens.buttonShadowOffsetY, - spread = ZcashTheme.dimens.buttonShadowSpread, - ) - .translationClick( - translationX = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp, // + 6dp to exactly cover the bottom shadow - translationY = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp - ) - .defaultMinSize(ZcashTheme.dimens.buttonWidth, ZcashTheme.dimens.buttonHeight) - .border(1.dp, Color.Black), - colors = buttonColors( - containerColor = buttonColor, - disabledContainerColor = ZcashTheme.colors.disabledButtonColor, - disabledContentColor = ZcashTheme.colors.disabledButtonTextColor - ), + modifier = + modifier.then(Modifier.fillMaxWidth()) + .padding(outerPaddingValues) + .shadow( + contentColor = textColor, + strokeColor = textColor, + offsetX = ZcashTheme.dimens.buttonShadowOffsetX, + offsetY = ZcashTheme.dimens.buttonShadowOffsetY, + spread = ZcashTheme.dimens.buttonShadowSpread, + ) + .translationClick( + // + 6dp to exactly cover the bottom shadow + translationX = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp, + translationY = ZcashTheme.dimens.buttonShadowOffsetX + 6.dp + ) + .defaultMinSize(ZcashTheme.dimens.buttonWidth, ZcashTheme.dimens.buttonHeight) + .border(1.dp, Color.Black), + colors = + buttonColors( + containerColor = buttonColor, + disabledContainerColor = ZcashTheme.colors.disabledButtonColor, + disabledContentColor = ZcashTheme.colors.disabledButtonTextColor + ), onClick = onClick, ) { Text( @@ -155,18 +163,20 @@ fun NavigationButton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, - outerPaddingValues: PaddingValues = PaddingValues( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingSmall - ), + outerPaddingValues: PaddingValues = + PaddingValues( + horizontal = ZcashTheme.dimens.spacingDefault, + vertical = ZcashTheme.dimens.spacingSmall + ), ) { Button( shape = RectangleShape, onClick = onClick, - modifier = modifier.then( - Modifier - .padding(outerPaddingValues) - ), + modifier = + modifier.then( + Modifier + .padding(outerPaddingValues) + ), colors = buttonColors(containerColor = MaterialTheme.colorScheme.secondary) ) { Text( @@ -183,20 +193,22 @@ fun TertiaryButton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, - outerPaddingValues: PaddingValues = PaddingValues( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingSmall - ), + outerPaddingValues: PaddingValues = + PaddingValues( + horizontal = ZcashTheme.dimens.spacingDefault, + vertical = ZcashTheme.dimens.spacingSmall + ), enabled: Boolean = true ) { Button( shape = RectangleShape, onClick = onClick, - modifier = modifier.then( - Modifier - .fillMaxWidth() - .padding(outerPaddingValues) - ), + modifier = + modifier.then( + Modifier + .fillMaxWidth() + .padding(outerPaddingValues) + ), enabled = enabled, elevation = ButtonDefaults.buttonElevation(0.dp, 0.dp, 0.dp), colors = buttonColors(containerColor = ZcashTheme.colors.tertiary) @@ -215,19 +227,21 @@ fun DangerousButton( onClick: () -> Unit, text: String, modifier: Modifier = Modifier, - outerPaddingValues: PaddingValues = PaddingValues( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingSmall - ), + outerPaddingValues: PaddingValues = + PaddingValues( + horizontal = ZcashTheme.dimens.spacingDefault, + vertical = ZcashTheme.dimens.spacingSmall + ), ) { Button( shape = RectangleShape, onClick = onClick, - modifier = modifier.then( - Modifier - .fillMaxWidth() - .padding(outerPaddingValues) - ), + modifier = + modifier.then( + Modifier + .fillMaxWidth() + .padding(outerPaddingValues) + ), colors = buttonColors(containerColor = ZcashTheme.colors.dangerous) ) { Text( @@ -291,6 +305,7 @@ fun Modifier.shadow( ) private enum class ButtonState { Pressed, Idle } + fun Modifier.translationClick( translationX: Dp = 0.dp, translationY: Dp = 0.dp @@ -298,26 +313,30 @@ fun Modifier.translationClick( var buttonState by remember { mutableStateOf(ButtonState.Idle) } val translationXAnimated by animateFloatAsState( - targetValue = if (buttonState == ButtonState.Pressed) { - translationX.value - } else { - 0f - }, + targetValue = + if (buttonState == ButtonState.Pressed) { + translationX.value + } else { + 0f + }, label = "ClickTranslationXAnimation", - animationSpec = tween( - durationMillis = 100 - ) + animationSpec = + tween( + durationMillis = 100 + ) ) val translationYAnimated by animateFloatAsState( - targetValue = if (buttonState == ButtonState.Pressed) { - translationY.value - } else { - 0f - }, + targetValue = + if (buttonState == ButtonState.Pressed) { + translationY.value + } else { + 0f + }, label = "ClickTranslationYAnimation", - animationSpec = tween( - durationMillis = 100 - ) + animationSpec = + tween( + durationMillis = 100 + ) ) this @@ -327,13 +346,14 @@ fun Modifier.translationClick( } .pointerInput(buttonState) { awaitPointerEventScope { - buttonState = if (buttonState == ButtonState.Pressed) { - waitForUpOrCancellation() - ButtonState.Idle - } else { - awaitFirstDown(false) - ButtonState.Pressed - } + buttonState = + if (buttonState == ButtonState.Pressed) { + waitForUpOrCancellation() + ButtonState.Idle + } else { + awaitFirstDown(false) + ButtonState.Pressed + } } } } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Checkbox.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Checkbox.kt index 6e8810c6..531f7dcf 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Checkbox.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Checkbox.kt @@ -41,19 +41,20 @@ fun CheckBox( verticalAlignment = Alignment.CenterVertically, modifier = modifier ) { - val checkBoxModifier = Modifier - .padding( - top = ZcashTheme.dimens.spacingTiny, - bottom = ZcashTheme.dimens.spacingTiny, - end = ZcashTheme.dimens.spacingTiny - ) - .then( - if (checkBoxTestTag != null) { - Modifier.testTag(checkBoxTestTag) - } else { - Modifier - } - ) + val checkBoxModifier = + Modifier + .padding( + top = ZcashTheme.dimens.spacingTiny, + bottom = ZcashTheme.dimens.spacingTiny, + end = ZcashTheme.dimens.spacingTiny + ) + .then( + if (checkBoxTestTag != null) { + Modifier.testTag(checkBoxTestTag) + } else { + Modifier + } + ) val (checkedState, setCheckedState) = rememberSaveable { mutableStateOf(checked) } Checkbox( checked = checkedState, diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Chip.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Chip.kt index 11c5b3b2..e5ba26e5 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Chip.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Chip.kt @@ -85,14 +85,16 @@ fun ChipOnSurface( ) { Surface( shape = RectangleShape, - modifier = modifier - .padding(horizontal = ZcashTheme.dimens.spacingTiny) - .border( - border = BorderStroke( - width = ZcashTheme.dimens.chipStroke, - color = ZcashTheme.colors.layoutStroke - ) - ), + modifier = + modifier + .padding(horizontal = ZcashTheme.dimens.spacingTiny) + .border( + border = + BorderStroke( + width = ZcashTheme.dimens.chipStroke, + color = ZcashTheme.colors.layoutStroke + ) + ), color = MaterialTheme.colorScheme.secondary, shadowElevation = ZcashTheme.dimens.chipShadowElevation, ) { @@ -100,12 +102,13 @@ fun ChipOnSurface( text = text, style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onSecondary, - modifier = Modifier - .padding( - vertical = ZcashTheme.dimens.spacingSmall, - horizontal = ZcashTheme.dimens.spacingDefault - ) - .testTag(CommonTag.CHIP) + modifier = + Modifier + .padding( + vertical = ZcashTheme.dimens.spacingSmall, + horizontal = ZcashTheme.dimens.spacingDefault + ) + .testTag(CommonTag.CHIP) ) } } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ChipGrid.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ChipGrid.kt index bf65d541..d6c58e95 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ChipGrid.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ChipGrid.kt @@ -48,14 +48,16 @@ fun ChipGrid( horizontalArrangement = Arrangement.Center ) { Row( - modifier = Modifier - .wrapContentWidth() - .clickable( - interactionSource = interactionSource, - indication = null, // Disable ripple - onClick = onGridClick - ) - .testTag(CommonTag.CHIP_LAYOUT) + modifier = + Modifier + .wrapContentWidth() + .clickable( + interactionSource = interactionSource, + // Disable ripple + indication = null, + onClick = onGridClick + ) + .testTag(CommonTag.CHIP_LAYOUT) ) { wordList.chunked(CHIP_GRID_COLUMN_SIZE).forEachIndexed { chunkIndex, chunk -> // TODO [#1043]: Correctly align numbers and words on Recovery screen diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/GradientSurface.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/GradientSurface.kt index 0949a36e..1f4e8817 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/GradientSurface.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/GradientSurface.kt @@ -9,11 +9,15 @@ import androidx.compose.ui.graphics.RectangleShape import co.electriccoin.zcash.ui.design.theme.ZcashTheme @Composable -fun GradientSurface(modifier: Modifier = Modifier, content: @Composable () -> Unit) { +fun GradientSurface( + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) { Surface( color = Color.Transparent, - modifier = modifier - .background(ZcashTheme.colors.surfaceGradient()), + modifier = + modifier + .background(ZcashTheme.colors.surfaceGradient()), shape = RectangleShape, content = content ) diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Override.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Override.kt index 1aeb38b7..6f36aca0 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Override.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Override.kt @@ -15,7 +15,10 @@ import kotlinx.coroutines.flow.StateFlow * for automated tests. */ @Composable -fun Override(configurationOverrideFlow: StateFlow, content: @Composable () -> Unit) { +fun Override( + configurationOverrideFlow: StateFlow, + content: @Composable () -> Unit +) { val configurationOverride = configurationOverrideFlow.collectAsState().value if (null == configurationOverride) { @@ -23,15 +26,16 @@ fun Override(configurationOverrideFlow: StateFlow, conte } else { val configuration = configurationOverride.newConfiguration(LocalConfiguration.current) - val contextWrapper = run { - val context = LocalContext.current - object : ContextThemeWrapper() { - init { - attachBaseContext(context) - applyOverrideConfiguration(configuration) + val contextWrapper = + run { + val context = LocalContext.current + object : ContextThemeWrapper() { + init { + attachBaseContext(context) + applyOverrideConfiguration(configuration) + } } } - } CompositionLocalProvider( LocalConfiguration provides configuration, @@ -43,15 +47,16 @@ fun Override(configurationOverrideFlow: StateFlow, conte } data class ConfigurationOverride(val uiMode: UiMode?, val locale: LocaleList?) { - fun newConfiguration(fromConfiguration: Configuration) = Configuration(fromConfiguration).apply { - this@ConfigurationOverride.uiMode?.let { - uiMode = (uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) or it.flag() - } + fun newConfiguration(fromConfiguration: Configuration) = + Configuration(fromConfiguration).apply { + this@ConfigurationOverride.uiMode?.let { + uiMode = (uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) or it.flag() + } - this@ConfigurationOverride.locale?.let { - setLocales(it) + this@ConfigurationOverride.locale?.let { + setLocales(it) + } } - } } enum class UiMode { @@ -59,7 +64,8 @@ enum class UiMode { Dark } -private fun UiMode.flag() = when (this) { - UiMode.Light -> Configuration.UI_MODE_NIGHT_NO - UiMode.Dark -> Configuration.UI_MODE_NIGHT_YES -} +private fun UiMode.flag() = + when (this) { + UiMode.Light -> Configuration.UI_MODE_NIGHT_NO + UiMode.Dark -> Configuration.UI_MODE_NIGHT_YES + } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/SwitchWithLabel.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/SwitchWithLabel.kt index f41bcc5a..10dcc6b4 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/SwitchWithLabel.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/SwitchWithLabel.kt @@ -24,48 +24,53 @@ fun SwitchWithLabel( val interactionSource = remember { MutableInteractionSource() } ConstraintLayout( - modifier = modifier - .clickable( - interactionSource = interactionSource, - indication = null, // disable ripple - role = Role.Switch, - onClick = { onStateChange(!state) } - ) - .fillMaxWidth() + modifier = + modifier + .clickable( + interactionSource = interactionSource, + // disable ripple + indication = null, + role = Role.Switch, + onClick = { onStateChange(!state) } + ) + .fillMaxWidth() ) { val (text, spacer, switchButton) = createRefs() Body( text = label, - modifier = Modifier.constrainAs(text) { - top.linkTo(parent.top) - bottom.linkTo(parent.top) - start.linkTo(parent.start) - end.linkTo(spacer.start) - width = Dimension.fillToConstraints - } - ) - Spacer( - modifier = Modifier - .width(ZcashTheme.dimens.spacingDefault) - .constrainAs(spacer) { + modifier = + Modifier.constrainAs(text) { top.linkTo(parent.top) bottom.linkTo(parent.top) - start.linkTo(text.end) - end.linkTo(switchButton.start) + start.linkTo(parent.start) + end.linkTo(spacer.start) + width = Dimension.fillToConstraints } ) + Spacer( + modifier = + Modifier + .width(ZcashTheme.dimens.spacingDefault) + .constrainAs(spacer) { + top.linkTo(parent.top) + bottom.linkTo(parent.top) + start.linkTo(text.end) + end.linkTo(switchButton.start) + } + ) Switch( checked = state, onCheckedChange = { onStateChange(it) }, - modifier = Modifier.constrainAs(switchButton) { - top.linkTo(parent.top) - bottom.linkTo(parent.top) - start.linkTo(spacer.end) - end.linkTo(parent.end) - width = Dimension.wrapContent - } + modifier = + Modifier.constrainAs(switchButton) { + top.linkTo(parent.top) + bottom.linkTo(parent.top) + start.linkTo(spacer.end) + end.linkTo(parent.end) + width = Dimension.wrapContent + } ) } } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt index 33f78ee1..b66876bd 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Text.kt @@ -147,21 +147,23 @@ fun Reference( onClick: () -> Unit ) { Box( - modifier = Modifier - .wrapContentSize() - .clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner)) - .clickable { onClick() } + modifier = + Modifier + .wrapContentSize() + .clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner)) + .clickable { onClick() } ) { Text( text = text, - style = MaterialTheme.typography.bodyLarge - .merge( - TextStyle( - color = ZcashTheme.colors.reference, - textAlign = textAlign, - textDecoration = TextDecoration.Underline - ) - ), + style = + MaterialTheme.typography.bodyLarge + .merge( + TextStyle( + color = ZcashTheme.colors.reference, + textAlign = textAlign, + textDecoration = TextDecoration.Underline + ) + ), modifier = modifier ) } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TextField.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TextField.kt index 4da0236a..bede0fe1 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TextField.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TextField.kt @@ -27,15 +27,17 @@ fun FormTextField( leadingIcon: @Composable (() -> Unit)? = null, trailingIcon: @Composable (() -> Unit)? = null, keyboardOptions: KeyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), - colors: TextFieldColors = TextFieldDefaults.colors( - focusedContainerColor = Color.Transparent, - unfocusedContainerColor = Color.Transparent, - disabledContainerColor = Color.Transparent, - errorContainerColor = Color.Transparent, - ), + colors: TextFieldColors = + TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + disabledContainerColor = Color.Transparent, + errorContainerColor = Color.Transparent, + ), keyboardActions: KeyboardActions = KeyboardActions.Default, shape: Shape = TextFieldDefaults.shape, - withBorder: Boolean = true, // To enable border around the TextField + // To enable border around the TextField + withBorder: Boolean = true, ) { TextField( value = value, @@ -44,13 +46,14 @@ fun FormTextField( textStyle = textStyle, keyboardOptions = keyboardOptions, colors = colors, - modifier = modifier.then( - if (withBorder) { - modifier.border(width = 1.dp, color = MaterialTheme.colorScheme.primary) - } else { - Modifier - } - ), + modifier = + modifier.then( + if (withBorder) { + modifier.border(width = 1.dp, color = MaterialTheme.colorScheme.primary) + } else { + Modifier + } + ), leadingIcon = leadingIcon, trailingIcon = trailingIcon, keyboardActions = keyboardActions, diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TopAppBar.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TopAppBar.kt index 12c64fce..7d99823f 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TopAppBar.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/TopAppBar.kt @@ -181,9 +181,10 @@ private fun TopBarOneVisibleActionMenuExample( text = "Action 1", onClick = actionCallback, textAlign = TextAlign.Center, - modifier = modifier.then( - Modifier.padding(all = ZcashTheme.dimens.spacingDefault) - ) + modifier = + modifier.then( + Modifier.padding(all = ZcashTheme.dimens.spacingDefault) + ) ) } @@ -218,10 +219,11 @@ fun SmallTopAppBar( navigationIcon = { backText?.let { Box( - modifier = Modifier - .wrapContentSize() - .clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner)) - .clickable { onBack?.run { onBack() } } + modifier = + Modifier + .wrapContentSize() + .clip(RoundedCornerShape(ZcashTheme.dimens.topAppBarActionRippleCorner)) + .clickable { onBack?.run { onBack() } } ) { Row( modifier = Modifier.padding(all = ZcashTheme.dimens.spacingDefault), diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/Dimens.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/Dimens.kt index 92fea6ad..a48f7e82 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/Dimens.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/Dimens.kt @@ -18,30 +18,23 @@ data class Dimens( val spacingLarge: Dp, val spacingXlarge: Dp, val spacingHuge: Dp, - // List of custom spacings: - // Button: val buttonShadowOffsetX: Dp, val buttonShadowOffsetY: Dp, val buttonShadowSpread: Dp, val buttonWidth: Dp, val buttonHeight: Dp, - // Chip val chipShadowElevation: Dp, val chipStroke: Dp, - // TopAppBar: val topAppBarZcashLogoHeight: Dp, val topAppBarActionRippleCorner: Dp, - // TextField: val textFieldDefaultHeight: Dp, - // Any Layout: val layoutStroke: Dp, - // Screen custom spacings: val inScreenZcashLogoHeight: Dp, val inScreenZcashLogoWidth: Dp, @@ -49,35 +42,36 @@ data class Dimens( val screenHorizontalSpacing: Dp, ) -private val defaultDimens = Dimens( - spacingNone = 0.dp, - spacingXtiny = 2.dp, - spacingTiny = 4.dp, - spacingSmall = 8.dp, - spacingDefault = 16.dp, - spacingLarge = 24.dp, - spacingXlarge = 32.dp, - spacingHuge = 64.dp, - buttonShadowOffsetX = 20.dp, - buttonShadowOffsetY = 20.dp, - buttonShadowSpread = 10.dp, - buttonWidth = 230.dp, - buttonHeight = 50.dp, - chipShadowElevation = 4.dp, - chipStroke = 0.5.dp, - topAppBarZcashLogoHeight = 24.dp, - topAppBarActionRippleCorner = 28.dp, - textFieldDefaultHeight = 215.dp, - layoutStroke = 1.dp, - inScreenZcashLogoHeight = 100.dp, - inScreenZcashLogoWidth = 60.dp, - inScreenZcashTextLogoHeight = 30.dp, - screenHorizontalSpacing = 64.dp, -) +private val defaultDimens = + Dimens( + spacingNone = 0.dp, + spacingXtiny = 2.dp, + spacingTiny = 4.dp, + spacingSmall = 8.dp, + spacingDefault = 16.dp, + spacingLarge = 24.dp, + spacingXlarge = 32.dp, + spacingHuge = 64.dp, + buttonShadowOffsetX = 20.dp, + buttonShadowOffsetY = 20.dp, + buttonShadowSpread = 10.dp, + buttonWidth = 230.dp, + buttonHeight = 50.dp, + chipShadowElevation = 4.dp, + chipStroke = 0.5.dp, + topAppBarZcashLogoHeight = 24.dp, + topAppBarActionRippleCorner = 28.dp, + textFieldDefaultHeight = 215.dp, + layoutStroke = 1.dp, + inScreenZcashLogoHeight = 100.dp, + inScreenZcashLogoWidth = 60.dp, + inScreenZcashTextLogoHeight = 30.dp, + screenHorizontalSpacing = 64.dp, + ) private val normalDimens = defaultDimens -internal var LocalDimens = staticCompositionLocalOf { defaultDimens } +internal var localDimens = staticCompositionLocalOf { defaultDimens } /** * This is a convenience way on how to provide device specification based spacings. We use Configuration from Compose @@ -119,7 +113,7 @@ internal var LocalDimens = staticCompositionLocalOf { defaultDimens } * - rounded/normal screen shape */ @Composable -internal fun ProvideDimens(content: @Composable () -> Unit,) { +internal fun ProvideDimens(content: @Composable () -> Unit) { val resultDimens = normalDimens - CompositionLocalProvider(LocalDimens provides resultDimens, content = content) + CompositionLocalProvider(localDimens provides resultDimens, content = content) } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt index 85cd4f20..25f1d28a 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ExtendedColors.kt @@ -37,10 +37,12 @@ data class ExtendedColors( val welcomeAnimationColor: Color, ) { @Composable - fun surfaceGradient() = Brush.verticalGradient( - colors = listOf( - MaterialTheme.colorScheme.surface, - ZcashTheme.colors.surfaceEnd + fun surfaceGradient() = + Brush.verticalGradient( + colors = + listOf( + MaterialTheme.colorScheme.surface, + ZcashTheme.colors.surfaceEnd + ) ) - ) } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt index dd08efe6..87cb9874 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/ZcashTheme.kt @@ -31,17 +31,19 @@ fun ZcashTheme( // IS_APP_DARK_MODE_ENABLED, whether the device's system dark mode is on or off. val useDarkMode = forceDarkMode || (BuildConfig.IS_APP_DARK_MODE_ENABLED && isSystemInDarkTheme()) - val baseColors = if (useDarkMode) { - DarkColorPalette - } else { - LightColorPalette - } + val baseColors = + if (useDarkMode) { + DarkColorPalette + } else { + LightColorPalette + } - val extendedColors = if (useDarkMode) { - DarkExtendedColorPalette - } else { - LightExtendedColorPalette - } + val extendedColors = + if (useDarkMode) { + DarkExtendedColorPalette + } else { + LightExtendedColorPalette + } CompositionLocalProvider(LocalExtendedColors provides extendedColors) { ProvideDimens { @@ -72,5 +74,5 @@ object ZcashTheme { // TODO [#808]: https://github.com/Electric-Coin-Company/zashi-android/issues/808 val dimens: Dimens @Composable - get() = LocalDimens.current + get() = localDimens.current } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt index 3456bbf5..2037588d 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Color.kt @@ -137,117 +137,122 @@ internal object Light { val welcomeAnimationColor = Color(0xFF231F20) } -internal val DarkColorPalette = darkColorScheme( - primary = Dark.primaryButton, - secondary = Dark.secondaryButton, - onPrimary = Dark.textPrimaryButton, - onSecondary = Dark.textSecondaryButton, - surface = Dark.backgroundStart, - onSurface = Dark.textBodyOnBackground, - background = Dark.backgroundStart, - onBackground = Dark.textBodyOnBackground, -) +internal val DarkColorPalette = + darkColorScheme( + primary = Dark.primaryButton, + secondary = Dark.secondaryButton, + onPrimary = Dark.textPrimaryButton, + onSecondary = Dark.textSecondaryButton, + surface = Dark.backgroundStart, + onSurface = Dark.textBodyOnBackground, + background = Dark.backgroundStart, + onBackground = Dark.textBodyOnBackground, + ) -internal val LightColorPalette = lightColorScheme( - primary = Light.primaryButton, - secondary = Light.secondaryButton, - onPrimary = Light.textPrimaryButton, - onSecondary = Light.textSecondaryButton, - surface = Light.backgroundStart, - onSurface = Light.textBodyOnBackground, - background = Light.backgroundStart, - onBackground = Light.textBodyOnBackground, -) +internal val LightColorPalette = + lightColorScheme( + primary = Light.primaryButton, + secondary = Light.secondaryButton, + onPrimary = Light.textPrimaryButton, + onSecondary = Light.textSecondaryButton, + surface = Light.backgroundStart, + onSurface = Light.textBodyOnBackground, + background = Light.backgroundStart, + onBackground = Light.textBodyOnBackground, + ) -internal val DarkExtendedColorPalette = ExtendedColors( - surfaceEnd = Dark.backgroundEnd, - onBackgroundHeader = Dark.textHeaderOnBackground, - tertiary = Dark.tertiaryButton, - onTertiary = Dark.textTertiaryButton, - callout = Dark.callout, - onCallout = Dark.onCallout, - progressStart = Dark.progressStart, - progressEnd = Dark.progressEnd, - progressBackground = Dark.progressBackground, - chipIndex = Dark.textChipIndex, - textFieldHint = Dark.textFieldHint, - layoutStroke = Dark.layoutStroke, - overlay = Dark.overlay, - highlight = Dark.highlight, - addressHighlightBorder = Dark.addressHighlightBorder, - addressHighlightUnified = Dark.addressHighlightUnified, - addressHighlightSapling = Dark.addressHighlightSapling, - addressHighlightTransparent = Dark.addressHighlightTransparent, - dangerous = Dark.dangerous, - onDangerous = Dark.onDangerous, - disabledButtonTextColor = Dark.disabledButtonTextColor, - disabledButtonColor = Dark.disabledButtonColor, - reference = Dark.reference, - buttonShadowColor = Dark.buttonShadowColor, - screenTitleColor = Dark.screenTitleColor, - aboutTextColor = Dark.aboutTextColor, - welcomeAnimationColor = Dark.welcomeAnimationColor -) +internal val DarkExtendedColorPalette = + ExtendedColors( + surfaceEnd = Dark.backgroundEnd, + onBackgroundHeader = Dark.textHeaderOnBackground, + tertiary = Dark.tertiaryButton, + onTertiary = Dark.textTertiaryButton, + callout = Dark.callout, + onCallout = Dark.onCallout, + progressStart = Dark.progressStart, + progressEnd = Dark.progressEnd, + progressBackground = Dark.progressBackground, + chipIndex = Dark.textChipIndex, + textFieldHint = Dark.textFieldHint, + layoutStroke = Dark.layoutStroke, + overlay = Dark.overlay, + highlight = Dark.highlight, + addressHighlightBorder = Dark.addressHighlightBorder, + addressHighlightUnified = Dark.addressHighlightUnified, + addressHighlightSapling = Dark.addressHighlightSapling, + addressHighlightTransparent = Dark.addressHighlightTransparent, + dangerous = Dark.dangerous, + onDangerous = Dark.onDangerous, + disabledButtonTextColor = Dark.disabledButtonTextColor, + disabledButtonColor = Dark.disabledButtonColor, + reference = Dark.reference, + buttonShadowColor = Dark.buttonShadowColor, + screenTitleColor = Dark.screenTitleColor, + aboutTextColor = Dark.aboutTextColor, + welcomeAnimationColor = Dark.welcomeAnimationColor + ) -internal val LightExtendedColorPalette = ExtendedColors( - surfaceEnd = Light.backgroundEnd, - onBackgroundHeader = Light.textHeaderOnBackground, - tertiary = Light.tertiaryButton, - onTertiary = Light.textTertiaryButton, - callout = Light.callout, - onCallout = Light.onCallout, - progressStart = Light.progressStart, - progressEnd = Light.progressEnd, - progressBackground = Light.progressBackground, - chipIndex = Light.textChipIndex, - textFieldHint = Light.textFieldHint, - layoutStroke = Light.layoutStroke, - overlay = Light.overlay, - highlight = Light.highlight, - addressHighlightBorder = Light.addressHighlightBorder, - addressHighlightUnified = Light.addressHighlightUnified, - addressHighlightSapling = Light.addressHighlightSapling, - addressHighlightTransparent = Light.addressHighlightTransparent, - dangerous = Light.dangerous, - onDangerous = Light.onDangerous, - disabledButtonTextColor = Light.disabledButtonTextColor, - disabledButtonColor = Light.disabledButtonColor, - reference = Light.reference, - buttonShadowColor = Light.buttonShadowColor, - screenTitleColor = Light.screenTitleColor, - aboutTextColor = Light.aboutTextColor, - welcomeAnimationColor = Light.welcomeAnimationColor -) +internal val LightExtendedColorPalette = + ExtendedColors( + surfaceEnd = Light.backgroundEnd, + onBackgroundHeader = Light.textHeaderOnBackground, + tertiary = Light.tertiaryButton, + onTertiary = Light.textTertiaryButton, + callout = Light.callout, + onCallout = Light.onCallout, + progressStart = Light.progressStart, + progressEnd = Light.progressEnd, + progressBackground = Light.progressBackground, + chipIndex = Light.textChipIndex, + textFieldHint = Light.textFieldHint, + layoutStroke = Light.layoutStroke, + overlay = Light.overlay, + highlight = Light.highlight, + addressHighlightBorder = Light.addressHighlightBorder, + addressHighlightUnified = Light.addressHighlightUnified, + addressHighlightSapling = Light.addressHighlightSapling, + addressHighlightTransparent = Light.addressHighlightTransparent, + dangerous = Light.dangerous, + onDangerous = Light.onDangerous, + disabledButtonTextColor = Light.disabledButtonTextColor, + disabledButtonColor = Light.disabledButtonColor, + reference = Light.reference, + buttonShadowColor = Light.buttonShadowColor, + screenTitleColor = Light.screenTitleColor, + aboutTextColor = Light.aboutTextColor, + welcomeAnimationColor = Light.welcomeAnimationColor + ) @Suppress("CompositionLocalAllowlist") -internal val LocalExtendedColors = staticCompositionLocalOf { - ExtendedColors( - surfaceEnd = Color.Unspecified, - onBackgroundHeader = Color.Unspecified, - tertiary = Color.Unspecified, - onTertiary = Color.Unspecified, - callout = Color.Unspecified, - onCallout = Color.Unspecified, - progressStart = Color.Unspecified, - progressEnd = Color.Unspecified, - progressBackground = Color.Unspecified, - chipIndex = Color.Unspecified, - textFieldHint = Color.Unspecified, - layoutStroke = Color.Unspecified, - overlay = Color.Unspecified, - highlight = Color.Unspecified, - addressHighlightBorder = Color.Unspecified, - addressHighlightUnified = Color.Unspecified, - addressHighlightSapling = Color.Unspecified, - addressHighlightTransparent = Color.Unspecified, - dangerous = Color.Unspecified, - onDangerous = Color.Unspecified, - disabledButtonTextColor = Color.Unspecified, - disabledButtonColor = Color.Unspecified, - reference = Color.Unspecified, - buttonShadowColor = Color.Unspecified, - screenTitleColor = Color.Unspecified, - aboutTextColor = Color.Unspecified, - welcomeAnimationColor = Color.Unspecified, - ) -} +internal val LocalExtendedColors = + staticCompositionLocalOf { + ExtendedColors( + surfaceEnd = Color.Unspecified, + onBackgroundHeader = Color.Unspecified, + tertiary = Color.Unspecified, + onTertiary = Color.Unspecified, + callout = Color.Unspecified, + onCallout = Color.Unspecified, + progressStart = Color.Unspecified, + progressEnd = Color.Unspecified, + progressBackground = Color.Unspecified, + chipIndex = Color.Unspecified, + textFieldHint = Color.Unspecified, + layoutStroke = Color.Unspecified, + overlay = Color.Unspecified, + highlight = Color.Unspecified, + addressHighlightBorder = Color.Unspecified, + addressHighlightUnified = Color.Unspecified, + addressHighlightSapling = Color.Unspecified, + addressHighlightTransparent = Color.Unspecified, + dangerous = Color.Unspecified, + onDangerous = Color.Unspecified, + disabledButtonTextColor = Color.Unspecified, + disabledButtonColor = Color.Unspecified, + reference = Color.Unspecified, + buttonShadowColor = Color.Unspecified, + screenTitleColor = Color.Unspecified, + aboutTextColor = Color.Unspecified, + welcomeAnimationColor = Color.Unspecified, + ) + } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt index 45c9bb13..bd86cbf2 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/theme/internal/Typography.kt @@ -14,98 +14,123 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp import co.electriccoin.zcash.ui.design.R -private val provider = GoogleFont.Provider( - providerAuthority = "com.google.android.gms.fonts", - providerPackage = "com.google.android.gms", - certificates = R.array.com_google_android_gms_fonts_certs -) +private val provider = + GoogleFont.Provider( + providerAuthority = "com.google.android.gms.fonts", + providerPackage = "com.google.android.gms", + certificates = R.array.com_google_android_gms_fonts_certs + ) // We use bestEffort here to be able to get the closest font weight, if accidentally use // an unspecified font weight and not the default one. private val InterFont = GoogleFont(name = "Inter", bestEffort = true) private val ArchivoFont = GoogleFont(name = "Archivo", bestEffort = true) -private val InterFontFamily = FontFamily( - Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.Normal), // W400 - Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.Medium), // W500 - Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.SemiBold), // W600 - Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.Bold) // W700 -) -private val ArchivoFontFamily = FontFamily( - Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.Normal), // W400 - Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.Medium), // W500 - Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.SemiBold), // W600 - Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.Bold) // W700 -) +private val InterFontFamily = + FontFamily( + // W400 + Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.Normal), + // W500 + Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.Medium), + // W600 + Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.SemiBold), + // W700 + Font(googleFont = InterFont, fontProvider = provider, weight = FontWeight.Bold) + ) +private val ArchivoFontFamily = + FontFamily( + // W400 + Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.Normal), + // W500 + Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.Medium), + // W600 + Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.SemiBold), + // W700 + Font(googleFont = ArchivoFont, fontProvider = provider, weight = FontWeight.Bold) + ) -private val Zboto = FontFamily( - Font(R.font.zboto, FontWeight.Normal) -) +private val Zboto = + FontFamily( + Font(R.font.zboto, FontWeight.Normal) + ) // If you change this definition of our Typography, don't forget to check if you use only // the defined font weights above, otherwise the closest one will be used. -internal val PrimaryTypography = Typography( - headlineLarge = TextStyle( - fontFamily = InterFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 30.sp - ), - titleSmall = TextStyle( - fontFamily = InterFontFamily, - fontWeight = FontWeight.Bold, - fontSize = 14.sp - ), - bodyLarge = TextStyle( - fontFamily = InterFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 16.sp - ), - bodySmall = TextStyle( - fontFamily = InterFontFamily, - fontWeight = FontWeight.Medium, - fontSize = 16.sp - ), - labelLarge = TextStyle( - fontFamily = InterFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 16.sp +internal val PrimaryTypography = + Typography( + headlineLarge = + TextStyle( + fontFamily = InterFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 30.sp + ), + titleSmall = + TextStyle( + fontFamily = InterFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 14.sp + ), + bodyLarge = + TextStyle( + fontFamily = InterFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ), + bodySmall = + TextStyle( + fontFamily = InterFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 16.sp + ), + labelLarge = + TextStyle( + fontFamily = InterFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) ) -) -internal val SecondaryTypography = Typography( - headlineLarge = TextStyle( - fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 30.sp - ), - headlineMedium = TextStyle( - fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.SemiBold, - fontSize = 25.sp, - textAlign = TextAlign.Center - ), - headlineSmall = TextStyle( - fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.Bold, - fontSize = 20.sp, - textAlign = TextAlign.Center - ), - bodyLarge = TextStyle( - fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 16.sp - ), - bodySmall = TextStyle( - fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.Medium, - fontSize = 16.sp - ), - labelLarge = TextStyle( - fontFamily = ArchivoFontFamily, - fontWeight = FontWeight.Normal, - fontSize = 16.sp +internal val SecondaryTypography = + Typography( + headlineLarge = + TextStyle( + fontFamily = ArchivoFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 30.sp + ), + headlineMedium = + TextStyle( + fontFamily = ArchivoFontFamily, + fontWeight = FontWeight.SemiBold, + fontSize = 25.sp, + textAlign = TextAlign.Center + ), + headlineSmall = + TextStyle( + fontFamily = ArchivoFontFamily, + fontWeight = FontWeight.Bold, + fontSize = 20.sp, + textAlign = TextAlign.Center + ), + bodyLarge = + TextStyle( + fontFamily = ArchivoFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ), + bodySmall = + TextStyle( + fontFamily = ArchivoFontFamily, + fontWeight = FontWeight.Medium, + fontSize = 16.sp + ), + labelLarge = + TextStyle( + fontFamily = ArchivoFontFamily, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ) ) -) @Immutable data class Typography( @@ -128,50 +153,61 @@ data class ExtendedTypography( ) @Suppress("CompositionLocalAllowlist") -val LocalTypographies = staticCompositionLocalOf { - Typography( - primary = PrimaryTypography, - secondary = SecondaryTypography - ) -} +val LocalTypographies = + staticCompositionLocalOf { + Typography( + primary = PrimaryTypography, + secondary = SecondaryTypography + ) + } @Suppress("CompositionLocalAllowlist") -val LocalExtendedTypography = staticCompositionLocalOf { - ExtendedTypography( - chipIndex = PrimaryTypography.bodyLarge.copy( - fontSize = 10.sp, - baselineShift = BaselineShift.Superscript, - fontWeight = FontWeight.Bold - ), - listItem = PrimaryTypography.bodyLarge.copy( - fontSize = 24.sp - ), - zecBalance = TextStyle( - fontFamily = Zboto, - fontWeight = FontWeight.Normal, - fontSize = 30.sp - ), - aboutText = PrimaryTypography.bodyLarge.copy( - fontSize = 14.sp, - lineHeight = 20.sp - ), - buttonText = PrimaryTypography.bodySmall.copy( - fontSize = 14.sp - ), - checkboxText = PrimaryTypography.bodyMedium.copy( - fontSize = 14.sp - ), - securityWarningText = PrimaryTypography.bodySmall.copy( - lineHeight = 22.32.sp - ), - textFieldHint = PrimaryTypography.bodySmall.copy( - fontSize = 13.sp, - lineHeight = 15.73.sp, - fontWeight = FontWeight.Normal - ), - textFieldValue = PrimaryTypography.bodyLarge.copy( - fontSize = 17.sp, - ), - textFieldBirthday = SecondaryTypography.headlineMedium.copy(), - ) -} +val LocalExtendedTypography = + staticCompositionLocalOf { + ExtendedTypography( + chipIndex = + PrimaryTypography.bodyLarge.copy( + fontSize = 10.sp, + baselineShift = BaselineShift.Superscript, + fontWeight = FontWeight.Bold + ), + listItem = + PrimaryTypography.bodyLarge.copy( + fontSize = 24.sp + ), + zecBalance = + TextStyle( + fontFamily = Zboto, + fontWeight = FontWeight.Normal, + fontSize = 30.sp + ), + aboutText = + PrimaryTypography.bodyLarge.copy( + fontSize = 14.sp, + lineHeight = 20.sp + ), + buttonText = + PrimaryTypography.bodySmall.copy( + fontSize = 14.sp + ), + checkboxText = + PrimaryTypography.bodyMedium.copy( + fontSize = 14.sp + ), + securityWarningText = + PrimaryTypography.bodySmall.copy( + lineHeight = 22.32.sp + ), + textFieldHint = + PrimaryTypography.bodySmall.copy( + fontSize = 13.sp, + lineHeight = 15.73.sp, + fontWeight = FontWeight.Normal + ), + textFieldValue = + PrimaryTypography.bodyLarge.copy( + fontSize = 17.sp, + ), + textFieldBirthday = SecondaryTypography.headlineMedium.copy(), + ) + } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/ScreenHeight.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/ScreenHeight.kt index 090af9d7..c611600f 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/ScreenHeight.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/util/ScreenHeight.kt @@ -20,33 +20,32 @@ import kotlin.math.roundToInt * @return Wrapper object of the calculated heights in density pixels. */ @Composable -fun screenHeight( - cacheKey: Any = true -): ScreenHeight { +fun screenHeight(cacheKey: Any = true): ScreenHeight { val density = LocalDensity.current val configuration = LocalConfiguration.current val statusBars = WindowInsets.statusBars val navigationBars = WindowInsets.navigationBars - val cachedResult = remember(cacheKey) { - val contentHeightPx = with(density) { configuration.screenHeightDp.dp.roundToPx() } - Twig.debug { "Screen content height in pixels: $contentHeightPx" } + val cachedResult = + remember(cacheKey) { + val contentHeightPx = with(density) { configuration.screenHeightDp.dp.roundToPx() } + Twig.debug { "Screen content height in pixels: $contentHeightPx" } - val statusBarHeight = statusBars.getTop(density).dp - Twig.debug { "Status bar height: $statusBarHeight" } + val statusBarHeight = statusBars.getTop(density).dp + Twig.debug { "Status bar height: $statusBarHeight" } - val navigationBarHeight = navigationBars.getBottom(density).dp - Twig.debug { "Navigation bar height: $navigationBarHeight" } + val navigationBarHeight = navigationBars.getBottom(density).dp + Twig.debug { "Navigation bar height: $navigationBarHeight" } - val contentHeight = (contentHeightPx / density.density.roundToInt()).dp - Twig.debug { "Screen content height in dps: $contentHeight" } + val contentHeight = (contentHeightPx / density.density.roundToInt()).dp + Twig.debug { "Screen content height in dps: $contentHeight" } - ScreenHeight( - contentHeight = contentHeight, - systemStatusBarHeight = statusBarHeight, - systemNavigationBarHeight = navigationBarHeight, - ) - } + ScreenHeight( + contentHeight = contentHeight, + systemStatusBarHeight = statusBarHeight, + systemNavigationBarHeight = navigationBarHeight, + ) + } Twig.debug { "Screen total height: $cachedResult" } return cachedResult @@ -58,5 +57,6 @@ data class ScreenHeight( val systemNavigationBarHeight: Dp ) { fun overallScreenHeight() = contentHeight + systemBarsHeight() + fun systemBarsHeight() = systemStatusBarHeight + systemNavigationBarHeight } diff --git a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt index d411a7b2..d1665cc9 100644 --- a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt +++ b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/common/Global.kt @@ -11,11 +11,14 @@ import androidx.test.uiautomator.UiSelector import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds -fun getStringResource(@StringRes resId: Int) = - ApplicationProvider.getApplicationContext().getString(resId) +fun getStringResource( + @StringRes resId: Int +) = ApplicationProvider.getApplicationContext().getString(resId) -fun getStringResourceWithArgs(@StringRes resId: Int, vararg formatArgs: String) = - ApplicationProvider.getApplicationContext().getString(resId, *formatArgs) +fun getStringResourceWithArgs( + @StringRes resId: Int, + vararg formatArgs: String +) = ApplicationProvider.getApplicationContext().getString(resId, *formatArgs) // We're using indexes to find the right button, as it seems to be the best available way to test a click // action on a permission button. These indexes remain the same for LTR as well as RTL layout direction. diff --git a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt index 73e29f58..95598f67 100644 --- a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt +++ b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewIntegrationTest.kt @@ -17,7 +17,6 @@ import org.junit.Rule import org.junit.Test class ScanViewIntegrationTest : UiTestPrerequisites() { - @get:Rule val composeTestRule = createAndroidComposeRule() diff --git a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt index b341ac88..4e26e496 100644 --- a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt +++ b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/scan/view/ScanViewTest.kt @@ -26,7 +26,6 @@ import org.junit.Test import kotlin.time.Duration.Companion.milliseconds class ScanViewTest : UiTestPrerequisites() { - @get:Rule val composeTestRule = createAndroidComposeRule() @@ -34,9 +33,10 @@ class ScanViewTest : UiTestPrerequisites() { @Before fun prepareTestSetup() { - testSetup = ScanViewTestSetup(composeTestRule).apply { - setDefaultContent() - } + testSetup = + ScanViewTestSetup(composeTestRule).apply { + setDefaultContent() + } } @Test diff --git a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/AppUpdateCheckerMock.kt b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/AppUpdateCheckerMock.kt index 742f3ae7..4731cf46 100644 --- a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/AppUpdateCheckerMock.kt +++ b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/AppUpdateCheckerMock.kt @@ -21,50 +21,53 @@ import kotlinx.coroutines.flow.flow import kotlin.time.Duration.Companion.milliseconds class AppUpdateCheckerMock private constructor() : AppUpdateChecker { - companion object { private const val DEFAULT_STALENESS_DAYS = 3 fun new() = AppUpdateCheckerMock() // used mostly for tests - val resultUpdateInfo = UpdateInfoFixture.new( - appUpdateInfo = null, - state = UpdateState.Prepared, - priority = AppUpdateChecker.Priority.HIGH, - force = true - ) + val resultUpdateInfo = + UpdateInfoFixture.new( + appUpdateInfo = null, + state = UpdateState.Prepared, + priority = AppUpdateChecker.Priority.HIGH, + force = true + ) } override val stalenessDays = DEFAULT_STALENESS_DAYS - override fun newCheckForUpdateAvailabilityFlow( - context: Context - ): Flow = callbackFlow { - val fakeAppUpdateManager = FakeAppUpdateManager(context.applicationContext).also { - it.setClientVersionStalenessDays(stalenessDays) - it.setUpdateAvailable( - context.packageManager.getPackageInfoCompat(context.packageName, 0L).versionCodeCompat.toInt(), - AppUpdateType.IMMEDIATE - ) - it.setUpdatePriority(resultUpdateInfo.priority.priorityUpperBorder()) + override fun newCheckForUpdateAvailabilityFlow(context: Context): Flow = + callbackFlow { + val fakeAppUpdateManager = + FakeAppUpdateManager(context.applicationContext).also { + it.setClientVersionStalenessDays(stalenessDays) + it.setUpdateAvailable( + context.packageManager.getPackageInfoCompat(context.packageName, 0L).versionCodeCompat.toInt(), + AppUpdateType.IMMEDIATE + ) + it.setUpdatePriority(resultUpdateInfo.priority.priorityUpperBorder()) + } + + val appUpdateInfoTask = fakeAppUpdateManager.appUpdateInfo + + // to simulate a real-world situation + delay(100.milliseconds) + + appUpdateInfoTask.addOnCompleteListener { infoTask -> + emitResult(this, infoTask.result) + } + + awaitClose { + // No resources to release + } } - val appUpdateInfoTask = fakeAppUpdateManager.appUpdateInfo - - // to simulate a real-world situation - delay(100.milliseconds) - - appUpdateInfoTask.addOnCompleteListener { infoTask -> - emitResult(this, infoTask.result) - } - - awaitClose { - // No resources to release - } - } - - private fun emitResult(producerScope: ProducerScope, info: AppUpdateInfo) { + private fun emitResult( + producerScope: ProducerScope, + info: AppUpdateInfo + ) { producerScope.trySend( UpdateInfoFixture.new( getPriority(info.updatePriority()), @@ -78,9 +81,10 @@ class AppUpdateCheckerMock private constructor() : AppUpdateChecker { override fun newStartUpdateFlow( activity: ComponentActivity, appUpdateInfo: AppUpdateInfo - ): Flow = flow { - // to simulate a real-world situation - delay(100.milliseconds) - emit(Activity.RESULT_OK) - } + ): Flow = + flow { + // to simulate a real-world situation + delay(100.milliseconds) + emit(Activity.RESULT_OK) + } } diff --git a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/UpdateViewModelTest.kt b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/UpdateViewModelTest.kt index be1796b3..b0f6c248 100644 --- a/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/UpdateViewModelTest.kt +++ b/ui-integration-test/src/main/java/co/electriccoin/zcash/ui/integration/test/screen/update/viewmodel/UpdateViewModelTest.kt @@ -36,18 +36,20 @@ class UpdateViewModelTest : UiTestPrerequisites() { fun setup() { checker = AppUpdateCheckerMock.new() - initialUpdateInfo = UpdateInfoFixture.new( - appUpdateInfo = null, - state = UpdateState.Prepared, - priority = AppUpdateChecker.Priority.LOW, - force = false - ) + initialUpdateInfo = + UpdateInfoFixture.new( + appUpdateInfo = null, + state = UpdateState.Prepared, + priority = AppUpdateChecker.Priority.LOW, + force = false + ) - viewModel = UpdateViewModel( - composeTestRule.activity.application, - initialUpdateInfo, - checker - ) + viewModel = + UpdateViewModel( + composeTestRule.activity.application, + initialUpdateInfo, + checker + ) } @After @@ -57,51 +59,52 @@ class UpdateViewModelTest : UiTestPrerequisites() { @Test @MediumTest - fun validate_result_of_update_methods_calls() = runTest { - viewModel.checkForAppUpdate() + fun validate_result_of_update_methods_calls() = + runTest { + viewModel.checkForAppUpdate() - // Although this test does not copy the real world situation, as the initial and result objects - // should be mostly the same, we test VM proper functionality. VM emits the initial object - // defined in this class, then we expect the result object from the AppUpdateCheckerMock class - // and a newly acquired AppUpdateInfo object. - viewModel.updateInfo.take(4).collectIndexed { index, incomingInfo -> - when (index) { - 0 -> { - // checkForAppUpdate initial callback - incomingInfo.also { - assertNull(it.appUpdateInfo) + // Although this test does not copy the real world situation, as the initial and result objects + // should be mostly the same, we test VM proper functionality. VM emits the initial object + // defined in this class, then we expect the result object from the AppUpdateCheckerMock class + // and a newly acquired AppUpdateInfo object. + viewModel.updateInfo.take(4).collectIndexed { index, incomingInfo -> + when (index) { + 0 -> { + // checkForAppUpdate initial callback + incomingInfo.also { + assertNull(it.appUpdateInfo) - assertEquals(initialUpdateInfo.state, it.state) - assertEquals(initialUpdateInfo.appUpdateInfo, it.appUpdateInfo) - assertEquals(initialUpdateInfo.priority, it.priority) - assertEquals(initialUpdateInfo.state, it.state) - assertEquals(initialUpdateInfo.isForce, it.isForce) + assertEquals(initialUpdateInfo.state, it.state) + assertEquals(initialUpdateInfo.appUpdateInfo, it.appUpdateInfo) + assertEquals(initialUpdateInfo.priority, it.priority) + assertEquals(initialUpdateInfo.state, it.state) + assertEquals(initialUpdateInfo.isForce, it.isForce) + } } - } - 1 -> { - // checkForAppUpdate result callback - incomingInfo.also { - assertNotNull(it.appUpdateInfo) + 1 -> { + // checkForAppUpdate result callback + incomingInfo.also { + assertNotNull(it.appUpdateInfo) - assertEquals(AppUpdateCheckerMock.resultUpdateInfo.state, it.state) - assertEquals(AppUpdateCheckerMock.resultUpdateInfo.priority, it.priority) - assertEquals(AppUpdateCheckerMock.resultUpdateInfo.isForce, it.isForce) + assertEquals(AppUpdateCheckerMock.resultUpdateInfo.state, it.state) + assertEquals(AppUpdateCheckerMock.resultUpdateInfo.priority, it.priority) + assertEquals(AppUpdateCheckerMock.resultUpdateInfo.isForce, it.isForce) + } + + // now we can start the update + viewModel.goForUpdate(composeTestRule.activity, incomingInfo.appUpdateInfo!!) + } + 2 -> { + // goForUpdate initial callback + assertNotNull(incomingInfo.appUpdateInfo) + assertEquals(UpdateState.Running, incomingInfo.state) + } + 3 -> { + // goForUpdate result callback + assertNotNull(incomingInfo.appUpdateInfo) + assertEquals(UpdateState.Done, incomingInfo.state) } - - // now we can start the update - viewModel.goForUpdate(composeTestRule.activity, incomingInfo.appUpdateInfo!!) - } - 2 -> { - // goForUpdate initial callback - assertNotNull(incomingInfo.appUpdateInfo) - assertEquals(UpdateState.Running, incomingInfo.state) - } - 3 -> { - // goForUpdate result callback - assertNotNull(incomingInfo.appUpdateInfo) - assertEquals(UpdateState.Done, incomingInfo.state) } } } - } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/MainActivityCompanionTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/MainActivityCompanionTest.kt index ec6dc40d..daf96216 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/MainActivityCompanionTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/MainActivityCompanionTest.kt @@ -6,7 +6,6 @@ import org.junit.Test import kotlin.time.Duration class MainActivityCompanionTest { - @Test @SmallTest fun splashScreenDelayDisabled() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/FlowExtTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/FlowExtTest.kt index 5752acc2..ca329b88 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/FlowExtTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/FlowExtTest.kt @@ -15,52 +15,55 @@ import kotlin.time.TimeMark import kotlin.time.TimeSource class FlowExtTest { - @OptIn(ExperimentalTime::class) @Test @SmallTest - fun throttle_one_sec() = runTest { - val timer = TimeSource.Monotonic.markNow() - val flow = flow { - while (timer.elapsedNow() <= 5.seconds) { - emit(1) - } - }.throttle(1.seconds) + fun throttle_one_sec() = + runTest { + val timer = TimeSource.Monotonic.markNow() + val flow = + flow { + while (timer.elapsedNow() <= 5.seconds) { + emit(1) + } + }.throttle(1.seconds) - var timeMark: TimeMark? = null - flow.collect { - if (timeMark == null) { - timeMark = TimeSource.Monotonic.markNow() - } else { - assert(timeMark!!.elapsedNow() >= 1.seconds) - timeMark = TimeSource.Monotonic.markNow() + var timeMark: TimeMark? = null + flow.collect { + if (timeMark == null) { + timeMark = TimeSource.Monotonic.markNow() + } else { + assert(timeMark!!.elapsedNow() >= 1.seconds) + timeMark = TimeSource.Monotonic.markNow() + } } } - } @OptIn(ExperimentalTime::class) - private fun raceConditionTest(duration: Duration): Boolean = runBlocking { - val flow = (0..1000).asFlow().throttle(duration) + private fun raceConditionTest(duration: Duration): Boolean = + runBlocking { + val flow = (0..1000).asFlow().throttle(duration) - val values = mutableListOf() - flow.collect { - values.add(it) + val values = mutableListOf() + flow.collect { + values.add(it) + } + + return@runBlocking values.zipWithNext().all { it.first <= it.second } } - return@runBlocking values.zipWithNext().all { it.first <= it.second } - } - @FlakyTest @Test - fun stressTest() = runBlocking { - repeat(10) { - assertTrue { raceConditionTest(0.001.seconds) } + fun stressTest() = + runBlocking { + repeat(10) { + assertTrue { raceConditionTest(0.001.seconds) } + } + repeat(10) { + assertTrue { raceConditionTest(0.0001.seconds) } + } + repeat(10) { + assertTrue { raceConditionTest(0.00001.seconds) } + } } - repeat(10) { - assertTrue { raceConditionTest(0.0001.seconds) } - } - repeat(10) { - assertTrue { raceConditionTest(0.00001.seconds) } - } - } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenBrightnessTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenBrightnessTest.kt index 371cfd89..ba330cf5 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenBrightnessTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenBrightnessTest.kt @@ -24,18 +24,18 @@ class ScreenBrightnessTest : UiTestPrerequisites() { @Test @MediumTest - fun acquireAndReleaseScreenBrightness() = runTest { - val testSetup = TestSetup(composeTestRule) + fun acquireAndReleaseScreenBrightness() = + runTest { + val testSetup = TestSetup(composeTestRule) - assertEquals(1, testSetup.getSecureBrightnessCount()) + assertEquals(1, testSetup.getSecureBrightnessCount()) - testSetup.mutableScreenBrightnessFlag.update { false } - composeTestRule.awaitIdle() - assertEquals(0, testSetup.getSecureBrightnessCount()) - } + testSetup.mutableScreenBrightnessFlag.update { false } + composeTestRule.awaitIdle() + assertEquals(0, testSetup.getSecureBrightnessCount()) + } private class TestSetup(composeTestRule: ComposeContentTestRule) { - val mutableScreenBrightnessFlag = MutableStateFlow(true) private val screenBrightness = ScreenBrightness() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenSecurityTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenSecurityTest.kt index d3217ee0..235e5969 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenSecurityTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenSecurityTest.kt @@ -42,18 +42,18 @@ class ScreenSecurityTest : UiTestPrerequisites() { @Test @MediumTest - fun acquireAndReleaseScreenSecurity() = runTest { - val testSetup = TestSetup(composeTestRule) + fun acquireAndReleaseScreenSecurity() = + runTest { + val testSetup = TestSetup(composeTestRule) - assertEquals(1, testSetup.getSecureScreenCount()) + assertEquals(1, testSetup.getSecureScreenCount()) - testSetup.mutableSecureScreenFlag.update { false } - composeTestRule.awaitIdle() - assertEquals(0, testSetup.getSecureScreenCount()) - } + testSetup.mutableSecureScreenFlag.update { false } + composeTestRule.awaitIdle() + assertEquals(0, testSetup.getSecureScreenCount()) + } private class TestSetup(composeTestRule: ComposeContentTestRule) { - val mutableSecureScreenFlag = MutableStateFlow(true) private val screenSecurity = ScreenSecurity() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenTimeoutTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenTimeoutTest.kt index b1c53a31..79cb5843 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenTimeoutTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/ScreenTimeoutTest.kt @@ -24,18 +24,18 @@ class ScreenTimeoutTest : UiTestPrerequisites() { @Test @MediumTest - fun acquireAndReleaseScreenTimeout() = runTest { - val testSetup = TestSetup(composeTestRule) + fun acquireAndReleaseScreenTimeout() = + runTest { + val testSetup = TestSetup(composeTestRule) - assertEquals(1, testSetup.getScreenTimeoutCount()) + assertEquals(1, testSetup.getScreenTimeoutCount()) - testSetup.mutableScreenTimeoutFlag.update { false } - composeTestRule.awaitIdle() - assertEquals(0, testSetup.getScreenTimeoutCount()) - } + testSetup.mutableScreenTimeoutFlag.update { false } + composeTestRule.awaitIdle() + assertEquals(0, testSetup.getScreenTimeoutCount()) + } private class TestSetup(composeTestRule: ComposeContentTestRule) { - val mutableScreenTimeoutFlag = MutableStateFlow(true) private val screenTimeout = ScreenTimeout() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/model/VersionInfoTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/model/VersionInfoTest.kt index 005a02a3..5e4e0e38 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/model/VersionInfoTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/common/model/VersionInfoTest.kt @@ -8,7 +8,6 @@ import kotlin.test.assertNotEquals import kotlin.test.assertTrue class VersionInfoTest { - @Test @SmallTest fun sanity_check_version_info_in_testing() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/MockSynchronizer.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/MockSynchronizer.kt index fe20152d..6600152a 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/MockSynchronizer.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/MockSynchronizer.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.flow.StateFlow */ @Suppress("TooManyFunctions", "UNUSED_PARAMETER") internal class MockSynchronizer : CloseableSynchronizer { - override val latestBirthdayHeight: BlockHeight get() = error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") @@ -123,7 +122,10 @@ internal class MockSynchronizer : CloseableSynchronizer { error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") } - override suspend fun refreshUtxos(account: Account, since: BlockHeight): Int? { + override suspend fun refreshUtxos( + account: Account, + since: BlockHeight + ): Int? { error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") } @@ -140,7 +142,10 @@ internal class MockSynchronizer : CloseableSynchronizer { return 1 } - override suspend fun shieldFunds(usk: UnifiedSpendingKey, memo: String): Long { + override suspend fun shieldFunds( + usk: UnifiedSpendingKey, + memo: String + ): Long { error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") } @@ -152,7 +157,11 @@ internal class MockSynchronizer : CloseableSynchronizer { error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") } - override suspend fun getExistingDataDbFilePath(context: Context, network: ZcashNetwork, alias: String): String { + override suspend fun getExistingDataDbFilePath( + context: Context, + network: ZcashNetwork, + alias: String + ): String { error("Intentionally not implemented in ${MockSynchronizer::class.simpleName} implementation.") } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/SendArgumentsWrapperFixture.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/SendArgumentsWrapperFixture.kt index f489e150..45bae368 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/SendArgumentsWrapperFixture.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/fixture/SendArgumentsWrapperFixture.kt @@ -12,6 +12,7 @@ internal object SendArgumentsWrapperFixture { val RECIPIENT_ADDRESS = WalletFixture.Alice.getAddresses(ZcashNetwork.Testnet).unified val MEMO = MemoFixture.new("Thanks for lunch").value val AMOUNT = ZatoshiFixture.new(1) + fun amountToFixtureZecString(amount: Zatoshi?) = amount?.toZecString() fun new( diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTest.kt index 5cbd928a..c9147422 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTest.kt @@ -88,9 +88,10 @@ class AboutViewTest { } } - private fun newTestSetup(isDebuggable: Boolean = false) = AboutViewTestSetup( - composeTestRule, - VersionInfoFixture.new(isDebuggable = isDebuggable), - ConfigInfoFixture.new() - ) + private fun newTestSetup(isDebuggable: Boolean = false) = + AboutViewTestSetup( + composeTestRule, + VersionInfoFixture.new(isDebuggable = isDebuggable), + ConfigInfoFixture.new() + ) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTestSetup.kt index ef6ae027..e1eecb96 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/about/view/AboutViewTestSetup.kt @@ -11,7 +11,6 @@ class AboutViewTestSetup( versionInfo: VersionInfo, configInfo: ConfigInfo ) { - private val onBackCount = AtomicInteger(0) fun getOnBackCount(): Int { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt index bb497b5e..3880a57f 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressViewTest.kt @@ -26,128 +26,133 @@ class WalletAddressViewTest : UiTestPrerequisites() { @Test @MediumTest - fun initial_screen_setup() = runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) + fun initial_screen_setup() = + runTest { + val walletAddresses = WalletAddressesFixture.new() + newTestSetup(walletAddresses) - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also { - it.assertExists() - } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_sapling)).also { - it.assertExists() - } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also { - it.assertExists() - } + composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also { + it.assertExists() + } + composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_sapling)).also { + it.assertExists() + } + composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also { + it.assertExists() + } - composeTestRule.onNodeWithText(walletAddresses.unified.address).also { - it.assertExists() - } + composeTestRule.onNodeWithText(walletAddresses.unified.address).also { + it.assertExists() + } - composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { - it.assertDoesNotExist() + composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { + it.assertDoesNotExist() + } + composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { + it.assertDoesNotExist() + } } - composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { - it.assertDoesNotExist() - } - } @Test @MediumTest - fun unified_collapses() = runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) + fun unified_collapses() = + runTest { + val walletAddresses = WalletAddressesFixture.new() + newTestSetup(walletAddresses) - composeTestRule.onNodeWithText(walletAddresses.unified.address).also { - it.assertExists() - } + composeTestRule.onNodeWithText(walletAddresses.unified.address).also { + it.assertExists() + } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also { - it.assertExists() - it.performClick() - } + composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_unified)).also { + it.assertExists() + it.performClick() + } - composeTestRule.onNodeWithText(walletAddresses.unified.address).also { - it.assertDoesNotExist() + composeTestRule.onNodeWithText(walletAddresses.unified.address).also { + it.assertDoesNotExist() + } } - } @Test @MediumTest - fun sapling_expands() = runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) + fun sapling_expands() = + runTest { + val walletAddresses = WalletAddressesFixture.new() + newTestSetup(walletAddresses) - composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { - it.assertDoesNotExist() - } + composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { + it.assertDoesNotExist() + } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_sapling)).also { - it.assertExists() - it.performClick() - } + composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_sapling)).also { + it.assertExists() + it.performClick() + } - composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { - it.assertExists() + composeTestRule.onNodeWithText(walletAddresses.sapling.address).also { + it.assertExists() + } } - } @Test @MediumTest - fun transparent_expands() = runTest { - val walletAddresses = WalletAddressesFixture.new() - newTestSetup(walletAddresses) + fun transparent_expands() = + runTest { + val walletAddresses = WalletAddressesFixture.new() + newTestSetup(walletAddresses) - composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { - it.assertDoesNotExist() - } + composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { + it.assertDoesNotExist() + } - composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also { - it.assertExists() - it.performClick() - } + composeTestRule.onNodeWithText(getStringResource(R.string.wallet_address_transparent)).also { + it.assertExists() + it.performClick() + } - composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { - it.assertExists() + composeTestRule.onNodeWithText(walletAddresses.transparent.address).also { + it.assertExists() + } } - } @Test @MediumTest - fun back_clicked() = runTest { - val testSetup = newTestSetup(WalletAddressesFixture.new()) + fun back_clicked() = + runTest { + val testSetup = newTestSetup(WalletAddressesFixture.new()) - assertEquals(0, testSetup.getOnBackCount()) + assertEquals(0, testSetup.getOnBackCount()) - composeTestRule.onNodeWithContentDescription( - getStringResource(R.string.wallet_address_back_content_description) - ).also { - it.performClick() + composeTestRule.onNodeWithContentDescription( + getStringResource(R.string.wallet_address_back_content_description) + ).also { + it.performClick() + } + + assertEquals(1, testSetup.getOnBackCount()) } - assertEquals(1, testSetup.getOnBackCount()) - } - @Test @MediumTest - fun copy_to_clipboard_clicked() = runTest { - val testSetup = newTestSetup(WalletAddressesFixture.new()) + fun copy_to_clipboard_clicked() = + runTest { + val testSetup = newTestSetup(WalletAddressesFixture.new()) - assertEquals(0, testSetup.getOnCopyToClipboardCount()) + assertEquals(0, testSetup.getOnCopyToClipboardCount()) - composeTestRule.onNodeWithTag( - WalletAddressesTag.WALLET_ADDRESS - ).also { - it.performClick() + composeTestRule.onNodeWithTag( + WalletAddressesTag.WALLET_ADDRESS + ).also { + it.performClick() + } + + assertEquals(1, testSetup.getOnCopyToClipboardCount()) } - assertEquals(1, testSetup.getOnCopyToClipboardCount()) - } - private fun newTestSetup(initialState: WalletAddresses) = TestSetup(composeTestRule, initialState) private class TestSetup(private val composeTestRule: ComposeContentTestRule, initialState: WalletAddresses) { - private val onBackCount = AtomicInteger(0) private val onCopyToClipboardCount = AtomicInteger(0) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt index e547b239..1decdd84 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtilTest.kt @@ -11,22 +11,23 @@ import kotlin.test.Ignore import kotlin.test.assertContains class FileShareUtilTest { - // TODO [#1034]: Finish disabled FileShareUtilTest // TODO [#1034]: https://github.com/Electric-Coin-Company/zashi-android/issues/1034 @Ignore("Temporary file permission is not correctly set") @Test @SmallTest fun check_intent_for_private_data_file_sharing() { - val tempFilePath = kotlin.io.path.createTempFile( - directory = getAppContext().cacheDir.toPath(), - suffix = ".sqlite3" - ) - val intent = FileShareUtil.newShareContentIntent( - context = getAppContext(), - dataFilePath = tempFilePath.pathString, - versionInfo = VersionInfoFixture.new() - ) + val tempFilePath = + kotlin.io.path.createTempFile( + directory = getAppContext().cacheDir.toPath(), + suffix = ".sqlite3" + ) + val intent = + FileShareUtil.newShareContentIntent( + context = getAppContext(), + dataFilePath = tempFilePath.pathString, + versionInfo = VersionInfoFixture.new() + ) assertEquals(intent.action, Intent.ACTION_VIEW) assertEquals( FileShareUtil.SHARE_OUTSIDE_THE_APP_FLAGS or FileShareUtil.SHARE_CONTENT_PERMISSION_FLAGS, diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTest.kt index 77105d30..e7a3f3eb 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTest.kt @@ -106,9 +106,10 @@ class ExportPrivateDataViewTest : UiTestPrerequisites() { assertEquals(true, testSetup.getOnAgree()) } - private fun newTestSetup() = ExportPrivateDataViewTestSetup(composeTestRule).apply { - setDefaultContent() - } + private fun newTestSetup() = + ExportPrivateDataViewTestSetup(composeTestRule).apply { + setDefaultContent() + } } private fun ComposeContentTestRule.clickBack() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTestSetup.kt index 55663b02..e8113322 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataViewTestSetup.kt @@ -8,7 +8,6 @@ import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger class ExportPrivateDataViewTestSetup(private val composeTestRule: ComposeContentTestRule) { - private val onBackCount = AtomicInteger(0) private val onAgree = AtomicBoolean(false) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/HistoryTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/HistoryTestSetup.kt index 39013272..88f52427 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/HistoryTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/HistoryTestSetup.kt @@ -10,7 +10,6 @@ class HistoryTestSetup( private val composeTestRule: ComposeContentTestRule, initialHistorySyncState: TransactionHistorySyncState ) { - private val onBackCount = AtomicInteger(0) fun getOnBackCount(): Int { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/fixture/TransactionHistorySyncStateFixture.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/fixture/TransactionHistorySyncStateFixture.kt index 566026c4..15fc339f 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/fixture/TransactionHistorySyncStateFixture.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/fixture/TransactionHistorySyncStateFixture.kt @@ -7,11 +7,12 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf internal object TransactionHistorySyncStateFixture { - val TRANSACTIONS = persistentListOf( - TransactionOverviewFixture.new(), - TransactionOverviewFixture.new(), - TransactionOverviewFixture.new() - ) + val TRANSACTIONS = + persistentListOf( + TransactionOverviewFixture.new(), + TransactionOverviewFixture.new(), + TransactionOverviewFixture.new() + ) val STATE = TransactionHistorySyncState.Syncing(TRANSACTIONS) fun new( diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/view/HistoryViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/view/HistoryViewTest.kt index 19abdbd3..e8d013d2 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/view/HistoryViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/history/view/HistoryViewTest.kt @@ -121,8 +121,10 @@ class HistoryViewTest { private fun newTestSetup( transactionHistorySyncState: TransactionHistorySyncState = TransactionHistorySyncStateFixture.new() - ) = HistoryTestSetup( - composeTestRule = composeTestRule, - initialHistorySyncState = transactionHistorySyncState - ) + ): HistoryTestSetup { + return HistoryTestSetup( + composeTestRule = composeTestRule, + initialHistorySyncState = transactionHistorySyncState + ) + } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/integration/HomeViewIntegrationTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/integration/HomeViewIntegrationTest.kt index 9193bbd6..c85b9ca4 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/integration/HomeViewIntegrationTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/integration/HomeViewIntegrationTest.kt @@ -23,21 +23,23 @@ class HomeViewIntegrationTest : UiTestPrerequisites() { @get:Rule val composeTestRule = createComposeRule() - private fun newTestSetup(walletSnapshot: WalletSnapshot) = HomeTestSetup( - composeTestRule, - walletSnapshot, - isShowFiatConversion = false - ) + private fun newTestSetup(walletSnapshot: WalletSnapshot) = + HomeTestSetup( + composeTestRule, + walletSnapshot, + isShowFiatConversion = false + ) // This is just basic sanity check that we still have UI set up as expected after the state restore @Test @MediumTest fun wallet_snapshot_restoration() { val restorationTester = StateRestorationTester(composeTestRule) - val walletSnapshot = WalletSnapshotFixture.new( - status = Synchronizer.Status.SYNCING, - progress = PercentDecimal(0.5f) - ) + val walletSnapshot = + WalletSnapshotFixture.new( + status = Synchronizer.Status.SYNCING, + progress = PercentDecimal(0.5f) + ) val testSetup = newTestSetup(walletSnapshot) restorationTester.setContent { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValuesTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValuesTest.kt index f0b1479f..387d3990 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValuesTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValuesTest.kt @@ -16,22 +16,23 @@ import kotlin.test.assertNotNull import kotlin.test.assertTrue class WalletDisplayValuesTest { - @Test @SmallTest fun download_running_test() { - val walletSnapshot = WalletSnapshotFixture.new( - progress = PercentDecimal.ONE_HUNDRED_PERCENT, - status = Synchronizer.Status.SYNCING, - orchardBalance = WalletSnapshotFixture.ORCHARD_BALANCE, - saplingBalance = WalletSnapshotFixture.SAPLING_BALANCE, - transparentBalance = WalletSnapshotFixture.TRANSPARENT_BALANCE - ) - val values = WalletDisplayValues.getNextValues( - getAppContext(), - walletSnapshot, - false - ) + val walletSnapshot = + WalletSnapshotFixture.new( + progress = PercentDecimal.ONE_HUNDRED_PERCENT, + status = Synchronizer.Status.SYNCING, + orchardBalance = WalletSnapshotFixture.ORCHARD_BALANCE, + saplingBalance = WalletSnapshotFixture.SAPLING_BALANCE, + transparentBalance = WalletSnapshotFixture.TRANSPARENT_BALANCE + ) + val values = + WalletDisplayValues.getNextValues( + getAppContext(), + walletSnapshot, + false + ) assertNotNull(values) assertEquals(1f, values.progress.decimal) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryTestSetup.kt index 341f02be..8019dcdd 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryTestSetup.kt @@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicInteger class NewWalletRecoveryTestSetup( private val composeTestRule: ComposeContentTestRule, ) { - private val onSeedCopyCount = AtomicInteger(0) private val onBirthdayCopyCount = AtomicInteger(0) @@ -20,6 +19,7 @@ class NewWalletRecoveryTestSetup( composeTestRule.waitForIdle() return onSeedCopyCount.get() } + fun getOnBirthdayCopyCount(): Int { composeTestRule.waitForIdle() return onBirthdayCopyCount.get() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryViewsSecuredScreenTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryViewsSecuredScreenTest.kt index e705700c..55ed410d 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryViewsSecuredScreenTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryViewsSecuredScreenTest.kt @@ -25,11 +25,12 @@ class NewWalletRecoveryViewsSecuredScreenTest : UiTestPrerequisites() { @Test @MediumTest - fun acquireScreenSecurity() = runTest { - val testSetup = newTestSetup() + fun acquireScreenSecurity() = + runTest { + val testSetup = newTestSetup() - assertEquals(1, testSetup.getSecureScreenCount()) - } + assertEquals(1, testSetup.getSecureScreenCount()) + } private class TestSetup(private val composeTestRule: ComposeContentTestRule) { private val screenSecurity = ScreenSecurity() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/OnboardingTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/OnboardingTestSetup.kt index b0336452..2a5019dc 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/OnboardingTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/OnboardingTestSetup.kt @@ -27,8 +27,10 @@ class OnboardingTestSetup( fun DefaultContent() { ZcashTheme { ShortOnboarding( - showWelcomeAnim = false, // It's fine to test the screen UI after the welcome animation - isDebugMenuEnabled = false, // Debug only UI state does not need to be tested + // It's fine to test the screen UI after the welcome animation + showWelcomeAnim = false, + // Debug only UI state does not need to be tested + isDebugMenuEnabled = false, onImportWallet = { onImportWalletCallbackCount.incrementAndGet() }, onCreateWallet = { onCreateWalletCallbackCount.incrementAndGet() }, onFixtureWallet = {} diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingViewTest.kt index b0fb23b2..553aa770 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingViewTest.kt @@ -53,10 +53,11 @@ class OnboardingViewTest : UiTestPrerequisites() { fun click_create_wallet() { val testSetup = newTestSetup() - val newWalletButton = composeTestRule.onNodeWithText( - text = getStringResource(R.string.onboarding_create_new_wallet), - ignoreCase = true - ) + val newWalletButton = + composeTestRule.onNodeWithText( + text = getStringResource(R.string.onboarding_create_new_wallet), + ignoreCase = true + ) newWalletButton.performClick() assertEquals(1, testSetup.getOnCreateWalletCallbackCount()) @@ -68,10 +69,11 @@ class OnboardingViewTest : UiTestPrerequisites() { fun click_import_wallet() { val testSetup = newTestSetup() - val newWalletButton = composeTestRule.onNodeWithText( - text = getStringResource(R.string.onboarding_import_existing_wallet), - ignoreCase = true - ) + val newWalletButton = + composeTestRule.onNodeWithText( + text = getStringResource(R.string.onboarding_import_existing_wallet), + ignoreCase = true + ) newWalletButton.performClick() assertEquals(1, testSetup.getOnImportWalletCallbackCount()) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt index 9a5ee83c..848beafd 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenBrightnessTest.kt @@ -16,25 +16,27 @@ class ReceiveViewScreenBrightnessTest : UiTestPrerequisites() { @Test @MediumTest - fun testBrightnessDefaultState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + fun testBrightnessDefaultState() = + runTest { + val testSetup = newTestSetup(WalletAddressFixture.unified()) - assertEquals(0, testSetup.getScreenBrightnessCount()) - } + assertEquals(0, testSetup.getScreenBrightnessCount()) + } @Test @MediumTest - fun testBrightnessOnState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + fun testBrightnessOnState() = + runTest { + val testSetup = newTestSetup(WalletAddressFixture.unified()) - assertEquals(false, testSetup.getOnAdjustBrightness()) - assertEquals(0, testSetup.getScreenBrightnessCount()) + assertEquals(false, testSetup.getOnAdjustBrightness()) + assertEquals(0, testSetup.getScreenBrightnessCount()) - composeTestRule.clickAdjustBrightness() + composeTestRule.clickAdjustBrightness() - assertEquals(true, testSetup.getOnAdjustBrightness()) - assertEquals(1, testSetup.getScreenBrightnessCount()) - } + assertEquals(true, testSetup.getOnAdjustBrightness()) + assertEquals(1, testSetup.getScreenBrightnessCount()) + } private fun newTestSetup(walletAddress: WalletAddress) = ReceiveViewTestSetup(composeTestRule, walletAddress) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt index 9ad59b66..3444f482 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewScreenTimeoutTest.kt @@ -16,25 +16,27 @@ class ReceiveViewScreenTimeoutTest : UiTestPrerequisites() { @Test @MediumTest - fun testTimeoutDefaultState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + fun testTimeoutDefaultState() = + runTest { + val testSetup = newTestSetup(WalletAddressFixture.unified()) - assertEquals(0, testSetup.getScreenTimeoutCount()) - } + assertEquals(0, testSetup.getScreenTimeoutCount()) + } @Test @MediumTest - fun testTimeoutOnState() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + fun testTimeoutOnState() = + runTest { + val testSetup = newTestSetup(WalletAddressFixture.unified()) - assertEquals(false, testSetup.getOnAdjustBrightness()) - assertEquals(0, testSetup.getScreenTimeoutCount()) + assertEquals(false, testSetup.getOnAdjustBrightness()) + assertEquals(0, testSetup.getScreenTimeoutCount()) - composeTestRule.clickAdjustBrightness() + composeTestRule.clickAdjustBrightness() - assertEquals(true, testSetup.getOnAdjustBrightness()) - assertEquals(1, testSetup.getScreenTimeoutCount()) - } + assertEquals(true, testSetup.getOnAdjustBrightness()) + assertEquals(1, testSetup.getScreenTimeoutCount()) + } private fun newTestSetup(walletAddress: WalletAddress) = ReceiveViewTestSetup(composeTestRule, walletAddress) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt index d693b6e4..2bb6c0f0 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveViewTest.kt @@ -25,48 +25,51 @@ class ReceiveViewTest { @Test @MediumTest - fun setup() = runTest { - val walletAddress = WalletAddressFixture.unified() - newTestSetup(walletAddress) + fun setup() = + runTest { + val walletAddress = WalletAddressFixture.unified() + newTestSetup(walletAddress) - // Enable substring for ellipsizing - composeTestRule.onNodeWithText(walletAddress.address, substring = true).also { - it.assertExists() + // Enable substring for ellipsizing + composeTestRule.onNodeWithText(walletAddress.address, substring = true).also { + it.assertExists() + } } - } @Test @MediumTest - fun back() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + fun back() = + runTest { + val testSetup = newTestSetup(WalletAddressFixture.unified()) - assertEquals(0, testSetup.getOnBackCount()) + assertEquals(0, testSetup.getOnBackCount()) - composeTestRule.onNodeWithContentDescription( - getStringResource(R.string.receive_back_content_description) - ).also { - it.performClick() + composeTestRule.onNodeWithContentDescription( + getStringResource(R.string.receive_back_content_description) + ).also { + it.performClick() + } + + assertEquals(1, testSetup.getOnBackCount()) } - assertEquals(1, testSetup.getOnBackCount()) - } - @Test @MediumTest - fun address_details() = runTest { - val testSetup = newTestSetup(WalletAddressFixture.unified()) + fun address_details() = + runTest { + val testSetup = newTestSetup(WalletAddressFixture.unified()) - assertEquals(0, testSetup.getOnAddressDetailsCount()) + assertEquals(0, testSetup.getOnAddressDetailsCount()) - composeTestRule.onNodeWithText( - text = getStringResource(R.string.receive_see_address_details), - ignoreCase = true - ).also { - it.performClick() + composeTestRule.onNodeWithText( + text = getStringResource(R.string.receive_see_address_details), + ignoreCase = true + ).also { + it.performClick() + } + + assertEquals(1, testSetup.getOnAddressDetailsCount()) } - assertEquals(1, testSetup.getOnAddressDetailsCount()) - } - private fun newTestSetup(walletAddress: WalletAddress) = ReceiveViewTestSetup(composeTestRule, walletAddress) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/request/view/RequestViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/request/view/RequestViewTest.kt index 436f5a1c..a758e520 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/request/view/RequestViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/request/view/RequestViewTest.kt @@ -45,50 +45,52 @@ class RequestViewTest : UiTestPrerequisites() { @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun create_request_no_message() = runTest { - val testSetup = TestSetup(composeTestRule) + fun create_request_no_message() = + runTest { + val testSetup = TestSetup(composeTestRule) - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastCreateZecRequest()) + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastCreateZecRequest()) - composeTestRule.setValidAmount() + composeTestRule.setValidAmount() - composeTestRule.clickCreateAndSend() + composeTestRule.clickCreateAndSend() - assertEquals(1, testSetup.getOnCreateCount()) + assertEquals(1, testSetup.getOnCreateCount()) - testSetup.getLastCreateZecRequest().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.address) - assertEquals(Zatoshi(12345600000), it.amount) - assertTrue(it.message.value.isEmpty()) + testSetup.getLastCreateZecRequest().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.address) + assertEquals(Zatoshi(12345600000), it.amount) + assertTrue(it.message.value.isEmpty()) + } } - } @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun create_request_with_message() = runTest { - val testSetup = TestSetup(composeTestRule) + fun create_request_with_message() = + runTest { + val testSetup = TestSetup(composeTestRule) - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastCreateZecRequest()) + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastCreateZecRequest()) - composeTestRule.setValidAmount() + composeTestRule.setValidAmount() - composeTestRule.setValidMessage() + composeTestRule.setValidMessage() - composeTestRule.clickCreateAndSend() + composeTestRule.clickCreateAndSend() - assertEquals(1, testSetup.getOnCreateCount()) + assertEquals(1, testSetup.getOnCreateCount()) - testSetup.getLastCreateZecRequest().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.address) - assertEquals(Zatoshi(12345600000), it.amount) - assertEquals(ZecRequestFixture.MESSAGE.value, it.message.value) + testSetup.getLastCreateZecRequest().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.address) + assertEquals(Zatoshi(12345600000), it.amount) + assertEquals(ZecRequestFixture.MESSAGE.value, it.message.value) + } } - } @Test @MediumTest @@ -165,30 +167,31 @@ class RequestViewTest : UiTestPrerequisites() { @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun max_message_length() = runTest { - val testSetup = TestSetup(composeTestRule) + fun max_message_length() = + runTest { + val testSetup = TestSetup(composeTestRule) - composeTestRule.setValidAmount() + composeTestRule.setValidAmount() - composeTestRule.setMessage( - buildString { - repeat(ZecRequestMessage.MAX_MESSAGE_LENGTH + 1) { number -> - append("$number") + composeTestRule.setMessage( + buildString { + repeat(ZecRequestMessage.MAX_MESSAGE_LENGTH + 1) { number -> + append("$number") + } } + ) + + composeTestRule.clickCreateAndSend() + + assertEquals(1, testSetup.getOnCreateCount()) + + testSetup.getLastCreateZecRequest().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.address) + assertEquals(Zatoshi(12345600000), it.amount) + assertTrue(it.message.value.isEmpty()) } - ) - - composeTestRule.clickCreateAndSend() - - assertEquals(1, testSetup.getOnCreateCount()) - - testSetup.getLastCreateZecRequest().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.address) - assertEquals(Zatoshi(12345600000), it.amount) - assertTrue(it.message.value.isEmpty()) } - } @Test @MediumTest @@ -203,7 +206,6 @@ class RequestViewTest : UiTestPrerequisites() { } private class TestSetup(private val composeTestRule: ComposeContentTestRule) { - private val onBackCount = AtomicInteger(0) private val onCreateCount = AtomicInteger(0) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResultTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResultTest.kt index 70d1b939..362b8016 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResultTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResultTest.kt @@ -10,10 +10,11 @@ import org.junit.Test class ParseResultTest { companion object { private val SAMPLE_WORD_LIST = setOf("bar", "baz", "foo") - private val SAMPLE_WORD_LIST_EXT = buildSet { - addAll(SAMPLE_WORD_LIST) - add("bazooka") - } + private val SAMPLE_WORD_LIST_EXT = + buildSet { + addAll(SAMPLE_WORD_LIST) + add("bazooka") + } } @Test diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewAndroidTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewAndroidTest.kt index b9e36718..5aa0b549 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewAndroidTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewAndroidTest.kt @@ -120,8 +120,9 @@ class RestoreViewAndroidTest : UiTestPrerequisites() { it.performTextInput("test") } - val imm = getAppContext().getSystemService(Context.INPUT_METHOD_SERVICE) - as InputMethodManager + val imm = + getAppContext().getSystemService(Context.INPUT_METHOD_SERVICE) + as InputMethodManager // Test that the input seed text field is still expecting an input, as the inserted seed words are not complete composeTestRule.waitUntil(5.seconds.inWholeMilliseconds) { @@ -155,11 +156,15 @@ class RestoreViewAndroidTest : UiTestPrerequisites() { ) = RestoreViewTest.TestSetup(composeTestRule, initialStage, initialWordsList) } -private fun copyToClipboard(context: Context, text: String) { +private fun copyToClipboard( + context: Context, + text: String +) { val clipboardManager = context.getSystemService(ClipboardManager::class.java) - val data = ClipData.newPlainText( - context.getString(R.string.new_wallet_recovery_seed_clipboard_tag), - text - ) + val data = + ClipData.newPlainText( + context.getString(R.string.new_wallet_recovery_seed_clipboard_tag), + text + ) clipboardManager.setPrimaryClip(data) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewSecuredScreenTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewSecuredScreenTest.kt index 293a8ba9..ebd7fe95 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewSecuredScreenTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewSecuredScreenTest.kt @@ -27,11 +27,12 @@ class RestoreViewSecuredScreenTest : UiTestPrerequisites() { @Test @MediumTest - fun acquireScreenSecurity() = runTest { - val testSetup = TestSetup(composeTestRule) + fun acquireScreenSecurity() = + runTest { + val testSetup = TestSetup(composeTestRule) - assertEquals(1, testSetup.getSecureScreenCount()) - } + assertEquals(1, testSetup.getSecureScreenCount()) + } private class TestSetup(composeTestRule: ComposeContentTestRule) { private val screenSecurity = ScreenSecurity() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewTest.kt index 12cb6e57..6dc23a61 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreViewTest.kt @@ -181,10 +181,11 @@ class RestoreViewTest : UiTestPrerequisites() { @Test @MediumTest fun height_skip() { - val testSetup = newTestSetup( - initialStage = RestoreStage.Birthday, - initialWordsList = SeedPhraseFixture.new().split - ) + val testSetup = + newTestSetup( + initialStage = RestoreStage.Birthday, + initialWordsList = SeedPhraseFixture.new().split + ) composeTestRule.onNodeWithText( text = getStringResource(R.string.restore_birthday_button_restore), @@ -202,10 +203,11 @@ class RestoreViewTest : UiTestPrerequisites() { @Test @MediumTest fun height_set_valid() { - val testSetup = newTestSetup( - initialStage = RestoreStage.Birthday, - initialWordsList = SeedPhraseFixture.new().split - ) + val testSetup = + newTestSetup( + initialStage = RestoreStage.Birthday, + initialWordsList = SeedPhraseFixture.new().split + ) composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also { it.performTextInput(ZcashNetwork.Mainnet.saplingActivationHeight.value.toString()) @@ -226,10 +228,11 @@ class RestoreViewTest : UiTestPrerequisites() { @Test @MediumTest fun height_set_invalid_too_small() { - val testSetup = newTestSetup( - initialStage = RestoreStage.Birthday, - initialWordsList = SeedPhraseFixture.new().split - ) + val testSetup = + newTestSetup( + initialStage = RestoreStage.Birthday, + initialWordsList = SeedPhraseFixture.new().split + ) composeTestRule.onNodeWithText( text = getStringResource(R.string.restore_birthday_button_restore), @@ -257,10 +260,11 @@ class RestoreViewTest : UiTestPrerequisites() { @Test @MediumTest fun height_set_invalid_non_digit() { - val testSetup = newTestSetup( - initialStage = RestoreStage.Birthday, - initialWordsList = SeedPhraseFixture.new().split - ) + val testSetup = + newTestSetup( + initialStage = RestoreStage.Birthday, + initialWordsList = SeedPhraseFixture.new().split + ) composeTestRule.onNodeWithTag(RestoreTag.BIRTHDAY_TEXT_FIELD).also { it.performTextInput("1.2") @@ -281,10 +285,11 @@ class RestoreViewTest : UiTestPrerequisites() { @Test @MediumTest fun complete_click_take_to_wallet() { - val testSetup = newTestSetup( - initialStage = RestoreStage.Birthday, - initialWordsList = SeedPhraseFixture.new().split - ) + val testSetup = + newTestSetup( + initialStage = RestoreStage.Birthday, + initialWordsList = SeedPhraseFixture.new().split + ) assertEquals(0, testSetup.getOnFinishedCount()) @@ -317,10 +322,11 @@ class RestoreViewTest : UiTestPrerequisites() { @Test @MediumTest fun back_from_birthday() { - val testSetup = newTestSetup( - initialStage = RestoreStage.Birthday, - initialWordsList = SeedPhraseFixture.new().split - ) + val testSetup = + newTestSetup( + initialStage = RestoreStage.Birthday, + initialWordsList = SeedPhraseFixture.new().split + ) assertEquals(0, testSetup.getOnBackCount()) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtilTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtilTest.kt index eb31d741..027d19ba 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtilTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtilTest.kt @@ -10,10 +10,10 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull class SettingsUtilTest { - companion object { - val SETTINGS_URI = SettingsUtil.SETTINGS_URI_PREFIX + - getAppContext().packageName + val SETTINGS_URI = + SettingsUtil.SETTINGS_URI_PREFIX + + getAppContext().packageName } @Test diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/view/ScanViewBasicTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/view/ScanViewBasicTest.kt index 8d5059d6..babdf276 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/view/ScanViewBasicTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/scan/view/ScanViewBasicTest.kt @@ -23,7 +23,6 @@ import org.junit.Test // Its ensured by GrantPermissionRule component. More complex UI and integration tests can be found // in the ui-integration-test-lib module. class ScanViewBasicTest : UiTestPrerequisites() { - @get:Rule val composeTestRule = createComposeRule() @@ -90,7 +89,8 @@ class ScanViewBasicTest : UiTestPrerequisites() { assertEquals(ScanState.Scanning, testSetup.getScanState()) } - private fun newTestSetup() = ScanViewBasicTestSetup(composeTestRule).apply { - setDefaultContent() - } + private fun newTestSetup() = + ScanViewBasicTestSetup(composeTestRule).apply { + setDefaultContent() + } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtilTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtilTest.kt index 833ac2f9..3858059e 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtilTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtilTest.kt @@ -7,7 +7,6 @@ import org.junit.Test import kotlin.test.assertContains class WebBrowserUtilTest { - @Test @SmallTest fun check_intent_for_web_browser() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTest.kt index c635eccf..cbdbeb99 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTest.kt @@ -96,9 +96,10 @@ class SecurityWarningViewTest : UiTestPrerequisites() { assertEquals(true, testSetup.getOnAcknowledged()) } - private fun newTestSetup() = SecurityWarningViewTestSetup(composeTestRule).apply { - setDefaultContent() - } + private fun newTestSetup() = + SecurityWarningViewTestSetup(composeTestRule).apply { + setDefaultContent() + } } private fun ComposeContentTestRule.clickBack() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTestSetup.kt index d35666a5..eee382c6 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningViewTestSetup.kt @@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger class SecurityWarningViewTestSetup(private val composeTestRule: ComposeContentTestRule) { - private val onBackCount = AtomicInteger(0) private val onAcknowledged = AtomicBoolean(false) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryTestSetup.kt index 94f86cf6..063d33de 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryTestSetup.kt @@ -9,7 +9,6 @@ import java.util.concurrent.atomic.AtomicInteger class SeedRecoveryTestSetup( private val composeTestRule: ComposeContentTestRule, ) { - private val onSeedCopyCount = AtomicInteger(0) private val onBirthdayCopyCount = AtomicInteger(0) @@ -22,6 +21,7 @@ class SeedRecoveryTestSetup( composeTestRule.waitForIdle() return onSeedCopyCount.get() } + fun getOnBirthdayCopyCount(): Int { composeTestRule.waitForIdle() return onBirthdayCopyCount.get() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryViewsSecuredScreenTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryViewsSecuredScreenTest.kt index 260fa5bd..4f5dce88 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryViewsSecuredScreenTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryViewsSecuredScreenTest.kt @@ -25,11 +25,12 @@ class SeedRecoveryViewsSecuredScreenTest : UiTestPrerequisites() { @Test @MediumTest - fun acquireScreenSecurity() = runTest { - val testSetup = newTestSetup() + fun acquireScreenSecurity() = + runTest { + val testSetup = newTestSetup() - assertEquals(1, testSetup.getSecureScreenCount()) - } + assertEquals(1, testSetup.getSecureScreenCount()) + } private class TestSetup(private val composeTestRule: ComposeContentTestRule) { private val screenSecurity = ScreenSecurity() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/MemoExtTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/MemoExtTest.kt index 5cd30d19..0a8ade9e 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/MemoExtTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/MemoExtTest.kt @@ -8,7 +8,6 @@ import org.junit.Test import kotlin.test.assertEquals class MemoExtTest { - @Test @SmallTest fun value_or_empty_char_test_empty_input() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/WalletAddressExtTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/WalletAddressExtTest.kt index 30e45816..00f4497a 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/WalletAddressExtTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/WalletAddressExtTest.kt @@ -9,30 +9,31 @@ import org.junit.Test import kotlin.test.assertEquals class WalletAddressExtTest { + @Test + @SmallTest + @OptIn(ExperimentalCoroutinesApi::class) + fun testAbbreviatedSaplingAddress() = + runTest { + val actual = WalletAddressFixture.sapling().abbreviated(getAppContext()) + + // TODO [#248]: The expected value should probably be reversed if the locale is RTL + // TODO [#248]: https://github.com/Electric-Coin-Company/zashi-android/issues/248 + val expected = "zs1hf…skt4u" + + assertEquals(expected, actual) + } @Test @SmallTest @OptIn(ExperimentalCoroutinesApi::class) - fun testAbbreviatedSaplingAddress() = runTest { - val actual = WalletAddressFixture.sapling().abbreviated(getAppContext()) + fun testAbbreviatedTransparentAddress() = + runTest { + val actual = WalletAddressFixture.transparent().abbreviated(getAppContext()) - // TODO [#248]: The expected value should probably be reversed if the locale is RTL - // TODO [#248]: https://github.com/Electric-Coin-Company/zashi-android/issues/248 - val expected = "zs1hf…skt4u" + // TODO [#248]: The expected value should probably be reversed if the locale is RTL + // TODO [#248]: https://github.com/Electric-Coin-Company/zashi-android/issues/248 + val expected = "t1QZM…3CSPK" - assertEquals(expected, actual) - } - - @Test - @SmallTest - @OptIn(ExperimentalCoroutinesApi::class) - fun testAbbreviatedTransparentAddress() = runTest { - val actual = WalletAddressFixture.transparent().abbreviated(getAppContext()) - - // TODO [#248]: The expected value should probably be reversed if the locale is RTL - // TODO [#248]: https://github.com/Electric-Coin-Company/zashi-android/issues/248 - val expected = "t1QZM…3CSPK" - - assertEquals(expected, actual) - } + assertEquals(expected, actual) + } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExtTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExtTest.kt index 979f1ed9..99a081f9 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExtTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExtTest.kt @@ -10,23 +10,24 @@ import kotlin.test.Test import kotlin.test.assertEquals class ZecSendExtTest { - @Test @SmallTest @OptIn(ExperimentalCoroutinesApi::class) - fun round_trip() = runTest { - val original = ZecSendFixture.new() - val saved = with(ZecSend.Saver) { - val allowingScope = SaverScope { true } + fun round_trip() = + runTest { + val original = ZecSendFixture.new() + val saved = + with(ZecSend.Saver) { + val allowingScope = SaverScope { true } - allowingScope.save(original) + allowingScope.save(original) + } + + val restored = ZecSend.Saver.restore(saved!!) + + assertEquals(original, restored) } - val restored = ZecSend.Saver.restore(saved!!) - - assertEquals(original, restored) - } - @Test @SmallTest fun restore_empty() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/integration/SendViewIntegrationTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/integration/SendViewIntegrationTest.kt index 129d1926..76464c3b 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/integration/SendViewIntegrationTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/integration/SendViewIntegrationTest.kt @@ -25,18 +25,18 @@ import org.junit.Rule import org.junit.Test class SendViewIntegrationTest { - @get:Rule val composeTestRule = createComposeRule() private val wallet = WalletFixture.Alice private val network = ZcashNetwork.Testnet - private val spendingKey = runBlocking { - WalletFixture.Alice.getUnifiedSpendingKey( - seed = wallet.seedPhrase, - network = network - ) - } + private val spendingKey = + runBlocking { + WalletFixture.Alice.getUnifiedSpendingKey( + seed = wallet.seedPhrase, + network = network + ) + } private val synchronizer = MockSynchronizer.new() private val balance = ZatoshiFixture.new() diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewAndroidTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewAndroidTest.kt index 15c8e007..5c2243f0 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewAndroidTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewAndroidTest.kt @@ -41,10 +41,11 @@ class SendViewAndroidTest : UiTestPrerequisites() { @Test @MediumTest fun back_on_sending_with_system_navigation_disabled_check() { - val testSetup = newTestSetup( - SendStage.Confirmation, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.Confirmation, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) @@ -62,10 +63,11 @@ class SendViewAndroidTest : UiTestPrerequisites() { @Test @MediumTest fun back_on_send_successful_with_system_navigation() { - val testSetup = newTestSetup( - SendStage.SendSuccessful, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.SendSuccessful, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) @@ -79,10 +81,11 @@ class SendViewAndroidTest : UiTestPrerequisites() { @Test @MediumTest fun back_on_send_failure_with_system_navigation() { - val testSetup = newTestSetup( - SendStage.SendFailure, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.SendFailure, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt index e26f2a0a..869ec535 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt @@ -83,208 +83,214 @@ class SendViewTest : UiTestPrerequisites() { @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun create_request_no_memo() = runTest { - val testSetup = newTestSetup() + fun create_request_no_memo() = + runTest { + val testSetup = newTestSetup() - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastZecSend()) + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastZecSend()) - composeTestRule.setValidAmount() - composeTestRule.setValidAddress() - composeTestRule.clickCreateAndSend() - composeTestRule.assertOnConfirmation() - composeTestRule.clickConfirmation() + composeTestRule.setValidAmount() + composeTestRule.setValidAddress() + composeTestRule.clickCreateAndSend() + composeTestRule.assertOnConfirmation() + composeTestRule.clickConfirmation() - launch { - testSetup.mutableActionExecuted.collectWith(this) { - if (!it) return@collectWith + launch { + testSetup.mutableActionExecuted.collectWith(this) { + if (!it) return@collectWith - assertEquals(1, testSetup.getOnCreateCount()) + assertEquals(1, testSetup.getOnCreateCount()) - launch { - testSetup.getLastZecSend().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.destination) - assertEquals(Zatoshi(12345678900000), it.amount) - assertEquals(ZecRequestFixture.MESSAGE.value, it.memo.value) + launch { + testSetup.getLastZecSend().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.destination) + assertEquals(Zatoshi(12345678900000), it.amount) + assertEquals(ZecRequestFixture.MESSAGE.value, it.memo.value) + } } + this.cancel() } - this.cancel() } } - } @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun create_request_with_memo() = runTest { - val testSetup = newTestSetup() + fun create_request_with_memo() = + runTest { + val testSetup = newTestSetup() - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastZecSend()) + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastZecSend()) - composeTestRule.setValidAmount() - composeTestRule.setValidAddress() - composeTestRule.setValidMemo() + composeTestRule.setValidAmount() + composeTestRule.setValidAddress() + composeTestRule.setValidMemo() - composeTestRule.clickCreateAndSend() - composeTestRule.assertOnConfirmation() - composeTestRule.clickConfirmation() + composeTestRule.clickCreateAndSend() + composeTestRule.assertOnConfirmation() + composeTestRule.clickConfirmation() - launch { - testSetup.mutableActionExecuted.collectWith(this) { - if (!it) return@collectWith + launch { + testSetup.mutableActionExecuted.collectWith(this) { + if (!it) return@collectWith - assertEquals(1, testSetup.getOnCreateCount()) + assertEquals(1, testSetup.getOnCreateCount()) - launch { - testSetup.getLastZecSend().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.destination) - assertEquals(Zatoshi(12345678900000), it.amount) - assertEquals(ZecRequestFixture.MESSAGE.value, it.memo.value) + launch { + testSetup.getLastZecSend().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.destination) + assertEquals(Zatoshi(12345678900000), it.amount) + assertEquals(ZecRequestFixture.MESSAGE.value, it.memo.value) + } } + this.cancel() } - this.cancel() } } - } @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun check_regex_functionality_valid_inputs() = runTest { - val testSetup = newTestSetup() - val separators = MonetarySeparators.current() + fun check_regex_functionality_valid_inputs() = + runTest { + val testSetup = newTestSetup() + val separators = MonetarySeparators.current() - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastZecSend()) - composeTestRule.assertSendDisabled() + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastZecSend()) + composeTestRule.assertSendDisabled() - composeTestRule.setValidAmount() - composeTestRule.setValidAddress() - composeTestRule.setValidMemo() - composeTestRule.assertSendEnabled() + composeTestRule.setValidAmount() + composeTestRule.setValidAddress() + composeTestRule.setValidMemo() + composeTestRule.assertSendEnabled() - composeTestRule.setAmount("123") - composeTestRule.assertSendEnabled() + composeTestRule.setAmount("123") + composeTestRule.assertSendEnabled() - // e.g. 123, - composeTestRule.setAmount("123${separators.grouping}") - composeTestRule.assertSendEnabled() + // e.g. 123, + composeTestRule.setAmount("123${separators.grouping}") + composeTestRule.assertSendEnabled() - // e.g. 123. - composeTestRule.setAmount("123${separators.decimal}") - composeTestRule.assertSendEnabled() + // e.g. 123. + composeTestRule.setAmount("123${separators.decimal}") + composeTestRule.assertSendEnabled() - // e.g. 123,456. - composeTestRule.setAmount("123${separators.grouping}456${separators.decimal}") - composeTestRule.assertSendEnabled() + // e.g. 123,456. + composeTestRule.setAmount("123${separators.grouping}456${separators.decimal}") + composeTestRule.assertSendEnabled() - // e.g. 123,456.789 - composeTestRule.setAmount("123${separators.grouping}456${separators.decimal}789") - composeTestRule.assertSendEnabled() + // e.g. 123,456.789 + composeTestRule.setAmount("123${separators.grouping}456${separators.decimal}789") + composeTestRule.assertSendEnabled() - composeTestRule.clickCreateAndSend() - composeTestRule.assertOnConfirmation() - composeTestRule.clickConfirmation() + composeTestRule.clickCreateAndSend() + composeTestRule.assertOnConfirmation() + composeTestRule.clickConfirmation() - launch { - testSetup.mutableActionExecuted.collectWith(this) { - if (!it) return@collectWith + launch { + testSetup.mutableActionExecuted.collectWith(this) { + if (!it) return@collectWith - assertEquals(1, testSetup.getOnCreateCount()) + assertEquals(1, testSetup.getOnCreateCount()) - launch { - testSetup.getLastZecSend().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.destination) - assertEquals(Zatoshi(12345678900000), it.amount) - assertEquals(ZecRequestFixture.MESSAGE.value, it.memo.value) + launch { + testSetup.getLastZecSend().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.destination) + assertEquals(Zatoshi(12345678900000), it.amount) + assertEquals(ZecRequestFixture.MESSAGE.value, it.memo.value) + } } + this.cancel() } - this.cancel() } } - } @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun check_regex_functionality_invalid_inputs() = runTest { - val testSetup = newTestSetup() - val separators = MonetarySeparators.current() + fun check_regex_functionality_invalid_inputs() = + runTest { + val testSetup = newTestSetup() + val separators = MonetarySeparators.current() - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastZecSend()) - composeTestRule.assertSendDisabled() + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastZecSend()) + composeTestRule.assertSendDisabled() - composeTestRule.setAmount("aaa") - composeTestRule.assertSendDisabled() + composeTestRule.setAmount("aaa") + composeTestRule.assertSendDisabled() - composeTestRule.setAmount("123aaa") - composeTestRule.assertSendDisabled() + composeTestRule.setAmount("123aaa") + composeTestRule.assertSendDisabled() - // e.g. ,. - composeTestRule.setAmount("${separators.grouping}${separators.decimal}") - composeTestRule.assertSendDisabled() + // e.g. ,. + composeTestRule.setAmount("${separators.grouping}${separators.decimal}") + composeTestRule.assertSendDisabled() - // e.g. 123,. - composeTestRule.setAmount("123${separators.grouping}${separators.decimal}") - composeTestRule.assertSendDisabled() + // e.g. 123,. + composeTestRule.setAmount("123${separators.grouping}${separators.decimal}") + composeTestRule.assertSendDisabled() - // e.g. 1,2,3 - composeTestRule.setAmount("1${separators.grouping}2${separators.grouping}3") - composeTestRule.assertSendDisabled() + // e.g. 1,2,3 + composeTestRule.setAmount("1${separators.grouping}2${separators.grouping}3") + composeTestRule.assertSendDisabled() - // e.g. 1.2.3 - composeTestRule.setAmount("1${separators.decimal}2${separators.decimal}3") - composeTestRule.assertSendDisabled() + // e.g. 1.2.3 + composeTestRule.setAmount("1${separators.decimal}2${separators.decimal}3") + composeTestRule.assertSendDisabled() - assertEquals(0, testSetup.getOnCreateCount()) - assertEquals(null, testSetup.getLastZecSend()) - composeTestRule.assertSendDisabled() - } + assertEquals(0, testSetup.getOnCreateCount()) + assertEquals(null, testSetup.getLastZecSend()) + composeTestRule.assertSendDisabled() + } @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun max_memo_length() = runTest { - val testSetup = newTestSetup() + fun max_memo_length() = + runTest { + val testSetup = newTestSetup() - composeTestRule.setValidAmount() - composeTestRule.setValidAddress() + composeTestRule.setValidAmount() + composeTestRule.setValidAddress() - val input = buildString { - while (Memo.isWithinMaxLength(toString())) { - append("a") - } - } - - composeTestRule.setMemo(input) - - composeTestRule.clickCreateAndSend() - composeTestRule.assertOnConfirmation() - composeTestRule.clickConfirmation() - - launch { - testSetup.mutableActionExecuted.collectWith(this) { - if (!it) return@collectWith - - assertEquals(1, testSetup.getOnCreateCount()) - - launch { - testSetup.getLastZecSend().also { - assertNotNull(it) - assertEquals(WalletAddressFixture.unified(), it.destination) - assertEquals(Zatoshi(12345600000), it.amount) - assertTrue(it.memo.value.isEmpty()) + val input = + buildString { + while (Memo.isWithinMaxLength(toString())) { + append("a") } } - this.cancel() + + composeTestRule.setMemo(input) + + composeTestRule.clickCreateAndSend() + composeTestRule.assertOnConfirmation() + composeTestRule.clickConfirmation() + + launch { + testSetup.mutableActionExecuted.collectWith(this) { + if (!it) return@collectWith + + assertEquals(1, testSetup.getOnCreateCount()) + + launch { + testSetup.getLastZecSend().also { + assertNotNull(it) + assertEquals(WalletAddressFixture.unified(), it.destination) + assertEquals(Zatoshi(12345600000), it.amount) + assertTrue(it.memo.value.isEmpty()) + } + } + this.cancel() + } } } - } @Test @MediumTest @@ -335,10 +341,11 @@ class SendViewTest : UiTestPrerequisites() { @Test @MediumTest fun back_on_send_successful() { - val testSetup = newTestSetup( - SendStage.SendSuccessful, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.SendSuccessful, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) @@ -351,10 +358,11 @@ class SendViewTest : UiTestPrerequisites() { @Test @MediumTest fun close_on_send_successful() { - val testSetup = newTestSetup( - SendStage.SendSuccessful, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.SendSuccessful, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) @@ -370,10 +378,11 @@ class SendViewTest : UiTestPrerequisites() { @Test @MediumTest fun back_on_send_failure() { - val testSetup = newTestSetup( - SendStage.SendFailure, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.SendFailure, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) @@ -387,10 +396,11 @@ class SendViewTest : UiTestPrerequisites() { @Test @MediumTest fun close_on_send_failure() { - val testSetup = newTestSetup( - SendStage.SendFailure, - runBlocking { ZecSendFixture.new() } - ) + val testSetup = + newTestSetup( + SendStage.SendFailure, + runBlocking { ZecSendFixture.new() } + ) assertEquals(0, testSetup.getOnBackCount()) @@ -421,11 +431,12 @@ class SendViewTest : UiTestPrerequisites() { fun input_arguments_to_form() { newTestSetup( sendStage = SendStage.Form, - sendArgumentsWrapper = SendArgumentsWrapperFixture.new( - recipientAddress = SendArgumentsWrapperFixture.RECIPIENT_ADDRESS, - amount = SendArgumentsWrapperFixture.AMOUNT, - memo = SendArgumentsWrapperFixture.MEMO - ), + sendArgumentsWrapper = + SendArgumentsWrapperFixture.new( + recipientAddress = SendArgumentsWrapperFixture.RECIPIENT_ADDRESS, + amount = SendArgumentsWrapperFixture.AMOUNT, + memo = SendArgumentsWrapperFixture.MEMO + ), zecSend = null ) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsViewTest.kt index ef9cd055..552a0262 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsViewTest.kt @@ -163,13 +163,14 @@ class SettingsViewTest : UiTestPrerequisites() { @Test @MediumTest fun troubleshooting_rescan_test() { - val testSetup = SettingsViewTestSetup( - composeTestRule, - TroubleshootingParametersFixture.new( - isEnabled = true, - isRescanEnabled = true + val testSetup = + SettingsViewTestSetup( + composeTestRule, + TroubleshootingParametersFixture.new( + isEnabled = true, + isRescanEnabled = true + ) ) - ) assertEquals(0, testSetup.getRescanCount()) @@ -185,13 +186,14 @@ class SettingsViewTest : UiTestPrerequisites() { @Test @MediumTest fun troubleshooting_background_sync_test() { - val testSetup = SettingsViewTestSetup( - composeTestRule, - TroubleshootingParametersFixture.new( - isEnabled = true, - isBackgroundSyncEnabled = true + val testSetup = + SettingsViewTestSetup( + composeTestRule, + TroubleshootingParametersFixture.new( + isEnabled = true, + isBackgroundSyncEnabled = true + ) ) - ) assertEquals(0, testSetup.getBackgroundSyncCount()) @@ -209,13 +211,14 @@ class SettingsViewTest : UiTestPrerequisites() { @Test @MediumTest fun troubleshooting_keep_screen_on_during_sync_test() { - val testSetup = SettingsViewTestSetup( - composeTestRule, - TroubleshootingParametersFixture.new( - isEnabled = true, - isKeepScreenOnDuringSyncEnabled = true + val testSetup = + SettingsViewTestSetup( + composeTestRule, + TroubleshootingParametersFixture.new( + isEnabled = true, + isKeepScreenOnDuringSyncEnabled = true + ) ) - ) assertEquals(0, testSetup.getKeepScreenOnSyncCount()) @@ -233,13 +236,14 @@ class SettingsViewTest : UiTestPrerequisites() { @Test @MediumTest fun troubleshooting_analytics_test() { - val testSetup = SettingsViewTestSetup( - composeTestRule, - TroubleshootingParametersFixture.new( - isEnabled = true, - isAnalyticsEnabled = true + val testSetup = + SettingsViewTestSetup( + composeTestRule, + TroubleshootingParametersFixture.new( + isEnabled = true, + isAnalyticsEnabled = true + ) ) - ) assertEquals(0, testSetup.getAnalyticsCount()) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfoTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfoTest.kt index dceb5e93..6dc0ea33 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfoTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfoTest.kt @@ -10,99 +10,106 @@ import kotlin.test.assertTrue class SupportInfoTest { @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_time() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_time() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.timeInfo.toSupportString() + val individualExpected = supportInfo.timeInfo.toSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Time)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Time)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_app() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_app() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.appInfo.toSupportString() + val individualExpected = supportInfo.appInfo.toSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.App)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.App)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_os() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_os() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.operatingSystemInfo.toSupportString() + val individualExpected = supportInfo.operatingSystemInfo.toSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Os)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Os)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_device() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_device() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.deviceInfo.toSupportString() + val individualExpected = supportInfo.deviceInfo.toSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Device)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Device)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_crash() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_crash() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.crashInfo.toCrashSupportString() + val individualExpected = supportInfo.crashInfo.toCrashSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Crash)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Crash)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_environment() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_environment() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.environmentInfo.toSupportString() + val individualExpected = supportInfo.environmentInfo.toSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Environment)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Environment)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } @OptIn(ExperimentalCoroutinesApi::class) @Test - fun filter_permission() = runTest { - val supportInfo = SupportInfo.new(getAppContext()) + fun filter_permission() = + runTest { + val supportInfo = SupportInfo.new(getAppContext()) - val individualExpected = supportInfo.permissionInfo.toPermissionSupportString() + val individualExpected = supportInfo.permissionInfo.toPermissionSupportString() - val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Permission)) - assertTrue(actualIncluded.contains(individualExpected)) + val actualIncluded = supportInfo.toSupportString(setOf(SupportInfoType.Permission)) + assertTrue(actualIncluded.contains(individualExpected)) - val actualExcluded = supportInfo.toSupportString(emptySet()) - assertFalse(actualExcluded.contains(individualExpected)) - } + val actualExcluded = supportInfo.toSupportString(emptySet()) + assertFalse(actualExcluded.contains(individualExpected)) + } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewIntegrationTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewIntegrationTest.kt index de548ed0..0cc43b99 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewIntegrationTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewIntegrationTest.kt @@ -14,7 +14,6 @@ import org.junit.Rule import org.junit.Test class SupportViewIntegrationTest : UiTestPrerequisites() { - @get:Rule val composeTestRule = createComposeRule() @@ -67,10 +66,11 @@ class SupportViewIntegrationTest : UiTestPrerequisites() { restorationTester.emulateSavedInstanceStateRestore() - val dialogContent = getStringResourceWithArgs( - R.string.support_confirmation_explanation, - getStringResource(R.string.app_name) - ) + val dialogContent = + getStringResourceWithArgs( + R.string.support_confirmation_explanation, + getStringResource(R.string.app_name) + ) composeTestRule.onNodeWithText(dialogContent).also { it.assertExists() } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTest.kt index 20bd2563..cd215c76 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTest.kt @@ -48,10 +48,11 @@ class SupportViewTest : UiTestPrerequisites() { assertEquals(0, testSetup.getOnSendCount()) - val dialogContent = getStringResourceWithArgs( - R.string.support_confirmation_explanation, - getStringResource(R.string.app_name) - ) + val dialogContent = + getStringResourceWithArgs( + R.string.support_confirmation_explanation, + getStringResource(R.string.app_name) + ) composeTestRule.onNodeWithText(dialogContent).also { it.assertExists() } @@ -91,10 +92,11 @@ class SupportViewTest : UiTestPrerequisites() { it.performClick() } - val dialogContent = getStringResourceWithArgs( - R.string.support_confirmation_explanation, - getStringResource(R.string.app_name) - ) + val dialogContent = + getStringResourceWithArgs( + R.string.support_confirmation_explanation, + getStringResource(R.string.app_name) + ) composeTestRule.onNodeWithText(dialogContent).also { it.assertDoesNotExist() } @@ -103,9 +105,10 @@ class SupportViewTest : UiTestPrerequisites() { assertEquals(0, testSetup.getOnBackCount()) } - private fun newTestSetup() = SupportViewTestSetup(composeTestRule).apply { - setDefaultContent() - } + private fun newTestSetup() = + SupportViewTestSetup(composeTestRule).apply { + setDefaultContent() + } } private fun ComposeContentTestRule.clickBack() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTestSetup.kt index 26b55a31..503c985e 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/support/view/SupportViewTestSetup.kt @@ -8,7 +8,6 @@ import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicReference class SupportViewTestSetup(private val composeTestRule: ComposeContentTestRule) { - private val onBackCount = AtomicInteger(0) private val onSendCount = AtomicInteger(0) diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/fixture/UpdateInfoFixtureTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/fixture/UpdateInfoFixtureTest.kt index a2b16686..b7b20253 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/fixture/UpdateInfoFixtureTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/fixture/UpdateInfoFixtureTest.kt @@ -6,7 +6,6 @@ import org.junit.Test import kotlin.test.assertEquals class UpdateInfoFixtureTest { - companion object { val updateInfo = UpdateInfoFixture.new(appUpdateInfo = null) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/integration/UpdateViewIntegrationTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/integration/UpdateViewIntegrationTest.kt index a06ea6d5..bffe248f 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/integration/UpdateViewIntegrationTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/integration/UpdateViewIntegrationTest.kt @@ -18,7 +18,6 @@ import kotlin.test.assertEquals import kotlin.test.assertNotEquals class UpdateViewIntegrationTest { - @get:Rule val composeTestRule = createComposeRule() @@ -26,14 +25,15 @@ class UpdateViewIntegrationTest { @MediumTest fun update_info_state_restoration() { val restorationTester = StateRestorationTester(composeTestRule) - val testSetup = newTestSetup( - UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.HIGH, - force = true, - appUpdateInfo = null, - state = UpdateState.Prepared + val testSetup = + newTestSetup( + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.HIGH, + force = true, + appUpdateInfo = null, + state = UpdateState.Prepared + ) ) - ) restorationTester.setContent { testSetup.DefaultContent() @@ -55,8 +55,9 @@ class UpdateViewIntegrationTest { assertNotEquals(testSetup.getUpdateState(), UpdateState.Prepared) } - private fun newTestSetup(updateInfo: UpdateInfo) = UpdateViewTestSetup( - composeTestRule, - updateInfo - ) + private fun newTestSetup(updateInfo: UpdateInfo) = + UpdateViewTestSetup( + composeTestRule, + updateInfo + ) } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/AppUpdateCheckerImpTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/AppUpdateCheckerImpTest.kt index 278d0061..d16b12a4 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/AppUpdateCheckerImpTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/AppUpdateCheckerImpTest.kt @@ -21,7 +21,6 @@ import kotlin.test.assertNull import kotlin.test.assertTrue class AppUpdateCheckerImpTest { - @get:Rule val composeTestRule = createAndroidComposeRule() @@ -40,46 +39,48 @@ class AppUpdateCheckerImpTest { @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun check_for_update_availability_test() = runTest { - assertNotNull(updateChecker) + fun check_for_update_availability_test() = + runTest { + assertNotNull(updateChecker) - getAppUpdateInfoFlow().onFirst { updateInfo -> - assertTrue( - listOf( - UpdateState.Failed, - UpdateState.Prepared, - UpdateState.Done - ).contains(updateInfo.state) - ) + getAppUpdateInfoFlow().onFirst { updateInfo -> + assertTrue( + listOf( + UpdateState.Failed, + UpdateState.Prepared, + UpdateState.Done + ).contains(updateInfo.state) + ) + } } - } @Test @MediumTest @OptIn(ExperimentalCoroutinesApi::class) - fun start_update_availability_test() = runTest { - getAppUpdateInfoFlow().onFirst { updateInfo -> - // In case we get result with FAILED state, e.g. app is still not released in the Google - // Play store, there is no way to continue with the test. - if (updateInfo.state == UpdateState.Failed) { - assertNull(updateInfo.appUpdateInfo) - return@onFirst - } + fun start_update_availability_test() = + runTest { + getAppUpdateInfoFlow().onFirst { updateInfo -> + // In case we get result with FAILED state, e.g. app is still not released in the Google + // Play store, there is no way to continue with the test. + if (updateInfo.state == UpdateState.Failed) { + assertNull(updateInfo.appUpdateInfo) + return@onFirst + } - assertNotNull(updateInfo.appUpdateInfo) + assertNotNull(updateInfo.appUpdateInfo) - updateChecker.newStartUpdateFlow( - composeTestRule.activity, - updateInfo.appUpdateInfo!! - ).onFirst { result -> - assertTrue { - listOf( - Activity.RESULT_OK, - Activity.RESULT_CANCELED, - ActivityResult.RESULT_IN_APP_UPDATE_FAILED - ).contains(result) + updateChecker.newStartUpdateFlow( + composeTestRule.activity, + updateInfo.appUpdateInfo!! + ).onFirst { result -> + assertTrue { + listOf( + Activity.RESULT_OK, + Activity.RESULT_CANCELED, + ActivityResult.RESULT_IN_APP_UPDATE_FAILED + ).contains(result) + } } } } - } } diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtilTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtilTest.kt index be3f0369..18718be6 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtilTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtilTest.kt @@ -9,10 +9,10 @@ import org.junit.Test import kotlin.test.assertContains class PlayStoreUtilTest { - companion object { - val PLAY_STORE_URI = PlayStoreUtil.PLAY_STORE_APP_URI + - getAppContext().packageName + val PLAY_STORE_URI = + PlayStoreUtil.PLAY_STORE_APP_URI + + getAppContext().packageName } @Test diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewAndroidTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewAndroidTest.kt index d2925dd8..1f7bc22a 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewAndroidTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewAndroidTest.kt @@ -18,26 +18,27 @@ import org.junit.Test // Non-multiplatform tests that require interacting with the Android system (e.g. system back navigation) // These don't have persistent state, so they are still unit tests. class UpdateViewAndroidTest : UiTestPrerequisites() { - @get:Rule val composeTestRule = createAndroidComposeRule() - private fun newTestSetup(updateInfo: UpdateInfo) = UpdateViewAndroidTestSetup( - composeTestRule, - updateInfo - ).apply { - setDefaultContent() - } + private fun newTestSetup(updateInfo: UpdateInfo) = + UpdateViewAndroidTestSetup( + composeTestRule, + updateInfo + ).apply { + setDefaultContent() + } @Test @MediumTest fun postpone_optional_update_test() { - val updateInfo = UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.LOW, - force = false, - appUpdateInfo = null, - state = UpdateState.Prepared - ) + val updateInfo = + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.LOW, + force = false, + appUpdateInfo = null, + state = UpdateState.Prepared + ) newTestSetup(updateInfo) composeTestRule.onNodeWithText(getStringResource(R.string.update_header)).also { @@ -54,12 +55,13 @@ class UpdateViewAndroidTest : UiTestPrerequisites() { @Test @MediumTest fun postpone_force_update_test() { - val updateInfo = UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.HIGH, - force = true, - appUpdateInfo = null, - state = UpdateState.Prepared - ) + val updateInfo = + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.HIGH, + force = true, + appUpdateInfo = null, + state = UpdateState.Prepared + ) newTestSetup(updateInfo) composeTestRule.onNodeWithText(getStringResource(R.string.update_critical_header)).also { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTest.kt index d8890b25..2727185a 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTest.kt @@ -21,19 +21,19 @@ import org.junit.Rule import org.junit.Test class UpdateViewTest : UiTestPrerequisites() { - @get:Rule val composeTestRule = createComposeRule() @Test @MediumTest fun texts_force_update_test() { - val updateInfo = UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.HIGH, - force = true, - appUpdateInfo = null, - state = UpdateState.Prepared - ) + val updateInfo = + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.HIGH, + force = true, + appUpdateInfo = null, + state = UpdateState.Prepared + ) newTestSetup(updateInfo) @@ -53,12 +53,13 @@ class UpdateViewTest : UiTestPrerequisites() { @Test @MediumTest fun later_btn_not_force_update_test() { - val updateInfo = UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.LOW, - force = false, - appUpdateInfo = null, - state = UpdateState.Prepared - ) + val updateInfo = + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.LOW, + force = false, + appUpdateInfo = null, + state = UpdateState.Prepared + ) val testSetup = newTestSetup(updateInfo) assertEquals(0, testSetup.getOnLaterCount()) @@ -71,12 +72,13 @@ class UpdateViewTest : UiTestPrerequisites() { @Test @MediumTest fun texts_not_force_update_test() { - val updateInfo = UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.MEDIUM, - force = false, - appUpdateInfo = null, - state = UpdateState.Prepared - ) + val updateInfo = + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.MEDIUM, + force = false, + appUpdateInfo = null, + state = UpdateState.Prepared + ) newTestSetup(updateInfo) @@ -94,12 +96,13 @@ class UpdateViewTest : UiTestPrerequisites() { @Test @MediumTest fun later_btn_force_update_test() { - val updateInfo = UpdateInfoFixture.new( - priority = AppUpdateChecker.Priority.HIGH, - force = true, - appUpdateInfo = null, - state = UpdateState.Prepared - ) + val updateInfo = + UpdateInfoFixture.new( + priority = AppUpdateChecker.Priority.HIGH, + force = true, + appUpdateInfo = null, + state = UpdateState.Prepared + ) val testSetup = newTestSetup(updateInfo) assertEquals(0, testSetup.getOnLaterCount()) @@ -145,12 +148,13 @@ class UpdateViewTest : UiTestPrerequisites() { assertEquals(1, testSetup.getOnReferenceCount()) } - private fun newTestSetup(updateInfo: UpdateInfo) = UpdateViewTestSetup( - composeTestRule, - updateInfo - ).apply { - setDefaultContent() - } + private fun newTestSetup(updateInfo: UpdateInfo) = + UpdateViewTestSetup( + composeTestRule, + updateInfo + ).apply { + setDefaultContent() + } } private fun ComposeContentTestRule.clickLater() { diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTestSetup.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTestSetup.kt index 721bb0e6..e31cb4cc 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTestSetup.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/update/view/UpdateViewTestSetup.kt @@ -13,7 +13,6 @@ class UpdateViewTestSetup( private val composeTestRule: ComposeContentTestRule, private val updateInfo: UpdateInfo ) { - private val onDownloadCount = AtomicInteger(0) private val onLaterCount = AtomicInteger(0) private val onReferenceCount = AtomicInteger(0) @@ -38,6 +37,7 @@ class UpdateViewTestSetup( composeTestRule.waitForIdle() return updateState.get() } + fun getUpdateInfo(): UpdateInfo { composeTestRule.waitForIdle() return updateInfo diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/test/Global.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/test/Global.kt index 2471106e..cc2d3cb6 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/test/Global.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/test/Global.kt @@ -6,7 +6,11 @@ import androidx.test.core.app.ApplicationProvider fun getAppContext(): Context = ApplicationProvider.getApplicationContext() -fun getStringResource(@StringRes resId: Int) = getAppContext().getString(resId) +fun getStringResource( + @StringRes resId: Int +) = getAppContext().getString(resId) -fun getStringResourceWithArgs(@StringRes resId: Int, vararg formatArgs: String) = - getAppContext().getString(resId, *formatArgs) +fun getStringResourceWithArgs( + @StringRes resId: Int, + vararg formatArgs: String +) = getAppContext().getString(resId, *formatArgs) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/global/StorageChecker.kt b/ui-lib/src/main/java/co/electriccoin/zcash/global/StorageChecker.kt index 4a0c7f47..3a949e8e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/global/StorageChecker.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/global/StorageChecker.kt @@ -12,11 +12,13 @@ object StorageChecker { @SuppressLint("UsableSpace") @Suppress("MagicNumber") - suspend fun checkAvailableStorageMegabytes(): Int = withContext(Dispatchers.IO) { - return@withContext (Environment.getDataDirectory().usableSpace / (1024 * 1024)).toInt() - } + suspend fun checkAvailableStorageMegabytes(): Int = + withContext(Dispatchers.IO) { + return@withContext (Environment.getDataDirectory().usableSpace / (1024 * 1024)).toInt() + } - suspend fun spaceRequiredToContinueMegabytes() = withContext(Dispatchers.IO) { - return@withContext REQUIRED_FREE_SPACE_MEGABYTES - checkAvailableStorageMegabytes() - } + suspend fun spaceRequiredToContinueMegabytes() = + withContext(Dispatchers.IO) { + return@withContext REQUIRED_FREE_SPACE_MEGABYTES - checkAvailableStorageMegabytes() + } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/global/WalletCoordinator.kt b/ui-lib/src/main/java/co/electriccoin/zcash/global/WalletCoordinator.kt index 576e3185..6182c6c2 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/global/WalletCoordinator.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/global/WalletCoordinator.kt @@ -8,22 +8,24 @@ import co.electriccoin.zcash.ui.preference.EncryptedPreferenceSingleton import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.flow -private val lazy = LazyWithArgument { - /** - * A flow of the user's stored wallet. Null indicates that no wallet has been stored. - */ - val persistableWallet = flow { - // EncryptedPreferenceSingleton.getInstance() is a suspending function, which is why we need - // the flow builder to provide a coroutine context. - val encryptedPreferenceProvider = EncryptedPreferenceSingleton.getInstance(it) +private val lazy = + LazyWithArgument { + /** + * A flow of the user's stored wallet. Null indicates that no wallet has been stored. + */ + val persistableWallet = + flow { + // EncryptedPreferenceSingleton.getInstance() is a suspending function, which is why we need + // the flow builder to provide a coroutine context. + val encryptedPreferenceProvider = EncryptedPreferenceSingleton.getInstance(it) - emitAll(EncryptedPreferenceKeys.PERSISTABLE_WALLET.observe(encryptedPreferenceProvider)) + emitAll(EncryptedPreferenceKeys.PERSISTABLE_WALLET.observe(encryptedPreferenceProvider)) + } + + WalletCoordinator( + context = it, + persistableWallet = persistableWallet + ) } - WalletCoordinator( - context = it, - persistableWallet = persistableWallet - ) -} - fun WalletCoordinator.Companion.getInstance(context: Context) = lazy.getInstance(context) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt index 51b5940d..59d13263 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt @@ -53,7 +53,6 @@ import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds class MainActivity : ComponentActivity() { - val homeViewModel by viewModels() val walletViewModel by viewModels() @@ -82,11 +81,12 @@ class MainActivity : ComponentActivity() { */ @SuppressLint("SourceLockedOrientationActivity") private fun setAllowedScreenOrientation() { - requestedOrientation = if (BuildConfig.IS_SCREEN_ROTATION_ENABLED) { - ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - } else { - ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } + requestedOrientation = + if (BuildConfig.IS_SCREEN_ROTATION_ENABLED) { + ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + } else { + ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } } private fun setupSplashScreen() { @@ -195,15 +195,16 @@ class MainActivity : ComponentActivity() { } private fun monitorForBackgroundSync() { - val isEnableBackgroundSyncFlow = run { - val homeViewModel by viewModels() - val isSecretReadyFlow = walletViewModel.secretState.map { it is SecretState.Ready } - val isBackgroundSyncEnabledFlow = homeViewModel.isBackgroundSyncEnabled.filterNotNull() + val isEnableBackgroundSyncFlow = + run { + val homeViewModel by viewModels() + val isSecretReadyFlow = walletViewModel.secretState.map { it is SecretState.Ready } + val isBackgroundSyncEnabledFlow = homeViewModel.isBackgroundSyncEnabled.filterNotNull() - isSecretReadyFlow.combine(isBackgroundSyncEnabledFlow) { isSecretReady, isBackgroundSyncEnabled -> - isSecretReady && isBackgroundSyncEnabled + isSecretReadyFlow.combine(isBackgroundSyncEnabledFlow) { isSecretReady, isBackgroundSyncEnabled -> + isSecretReady && isBackgroundSyncEnabled + } } - } lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.RESUMED) { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt index 9ec57af1..1d645b5b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt @@ -46,11 +46,12 @@ import co.electriccoin.zcash.ui.screen.update.WrapCheckForUpdate @Suppress("LongMethod") internal fun MainActivity.Navigation() { val context = LocalContext.current - val navController = rememberNavController().also { - // This suppress is necessary, as this is how we set up the nav controller for tests. - @SuppressLint("RestrictedApi") - navControllerForTesting = it - } + val navController = + rememberNavController().also { + // This suppress is necessary, as this is how we set up the nav controller for tests. + @SuppressLint("RestrictedApi") + navControllerForTesting = it + } NavHost(navController = navController, startDestination = HOME) { composable(HOME) { @@ -127,11 +128,12 @@ internal fun MainActivity.Navigation() { navController.navigateJustOnce(SCAN) }, goBack = { navController.popBackStackJustOnce(SEND) }, - sendArgumentsWrapper = SendArgumentsWrapper( - recipientAddress = backStackEntry.savedStateHandle[SEND_RECIPIENT_ADDRESS], - amount = backStackEntry.savedStateHandle[SEND_AMOUNT], - memo = backStackEntry.savedStateHandle[SEND_MEMO] - ) + sendArgumentsWrapper = + SendArgumentsWrapper( + recipientAddress = backStackEntry.savedStateHandle[SEND_RECIPIENT_ADDRESS], + amount = backStackEntry.savedStateHandle[SEND_AMOUNT], + memo = backStackEntry.savedStateHandle[SEND_MEMO] + ) ) backStackEntry.savedStateHandle.remove(SEND_RECIPIENT_ADDRESS) backStackEntry.savedStateHandle.remove(SEND_AMOUNT) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/CompositionLocalBinder.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/CompositionLocalBinder.kt index e8dfeb5a..ebecd012 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/CompositionLocalBinder.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/CompositionLocalBinder.kt @@ -30,8 +30,9 @@ fun ComponentActivity.BindCompLocalProvider(content: @Composable () -> Unit) { private fun ComponentActivity.observeScreenSecurityFlag(screenSecurity: ScreenSecurity) { screenSecurity.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { isSecure -> - val isTest = FirebaseTestLabUtil.isFirebaseTestLab(applicationContext) || - EmulatorWtfUtil.isEmulatorWtf(applicationContext) + val isTest = + FirebaseTestLabUtil.isFirebaseTestLab(applicationContext) || + EmulatorWtfUtil.isEmulatorWtf(applicationContext) if (isSecure && !isTest) { window.setFlags( @@ -47,13 +48,15 @@ private fun ComponentActivity.observeScreenSecurityFlag(screenSecurity: ScreenSe private fun ComponentActivity.observeScreenBrightnessFlag(screenBrightness: ScreenBrightness) { screenBrightness.referenceCount.map { it > 0 }.collectWith(lifecycleScope) { maxBrightness -> if (maxBrightness) { - window.attributes = window.attributes.apply { - this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL - } + window.attributes = + window.attributes.apply { + this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL + } } else { - window.attributes = window.attributes.apply { - this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE - } + window.attributes = + window.attributes.apply { + this.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE + } } } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/FlowExt.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/FlowExt.kt index 518e17a3..5ea39557 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/FlowExt.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/FlowExt.kt @@ -18,41 +18,43 @@ import kotlin.time.TimeSource fun Flow.throttle( duration: Duration, timeSource: TimeSource = TimeSource.Monotonic -): Flow = flow { - coroutineScope { - val context = coroutineContext - val mutex = Mutex() +): Flow = + flow { + coroutineScope { + val context = coroutineContext + val mutex = Mutex() - var timeMark = timeSource.markNow() - var delayEmit: Deferred? = null - var firstValue = true - var valueToEmit: T - collect { value -> - if (firstValue) { - firstValue = false - emit(value) - timeMark = timeSource.markNow() - return@collect - } - delayEmit?.cancel() - valueToEmit = value - - if (timeMark.elapsedNow() >= duration) { - mutex.withLock { - emit(valueToEmit) + var timeMark = timeSource.markNow() + var delayEmit: Deferred? = null + var firstValue = true + var valueToEmit: T + collect { value -> + if (firstValue) { + firstValue = false + emit(value) timeMark = timeSource.markNow() + return@collect } - } else { - delayEmit = async(Dispatchers.Default) { + delayEmit?.cancel() + valueToEmit = value + + if (timeMark.elapsedNow() >= duration) { mutex.withLock { - delay(duration) - withContext(context) { - emit(valueToEmit) - } + emit(valueToEmit) timeMark = timeSource.markNow() } + } else { + delayEmit = + async(Dispatchers.Default) { + mutex.withLock { + delay(duration) + withContext(context) { + emit(valueToEmit) + } + timeMark = timeSource.markNow() + } + } } } } } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ListExt.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ListExt.kt index 70ad6935..3254d060 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ListExt.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ListExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.common diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/LocaleExt.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/LocaleExt.kt index 68cb7a5c..fa6f3b08 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/LocaleExt.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/LocaleExt.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.common diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ScreenSecurity.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ScreenSecurity.kt index 481562bc..d013394b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ScreenSecurity.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/ScreenSecurity.kt @@ -50,7 +50,9 @@ val isRunningTest: Boolean by lazy { val shouldSecureScreen: Boolean by lazy { if (isRunningTest) { true - } else BuildConfig.IS_SECURE_SCREEN_ENABLED + } else { + BuildConfig.IS_SECURE_SCREEN_ENABLED + } } @Composable diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/VersionInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/VersionInfo.kt index fe1dff24..332673ac 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/VersionInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/VersionInfo.kt @@ -22,13 +22,15 @@ data class VersionInfo( val applicationInfo = context.applicationInfo return VersionInfo( - versionName = packageInfo.versionName ?: "null", // Should only be null during tests - versionCode = packageInfo.versionCodeCompat, // Should only be 0 during tests + // Should only be null during tests + versionName = packageInfo.versionName ?: "null", + // Should only be 0 during tests + versionCode = packageInfo.versionCodeCompat, isDebuggable = ( (0 != applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) && !FirebaseTestLabUtil.isFirebaseTestLab(context.applicationContext) && !EmulatorWtfUtil.isEmulatorWtf(context.applicationContext) - ), + ), gitSha = gitSha, gitCommitCount = gitCommitCount.toLong() ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletSnapshot.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletSnapshot.kt index 0a4d8fc7..1f7980a0 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletSnapshot.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/model/WalletSnapshot.kt @@ -20,8 +20,9 @@ data class WalletSnapshot( val synchronizerError: SynchronizerError? ) { // Note: the wallet is effectively empty if it cannot cover the miner's fee - val hasFunds = saplingBalance.available.value > - (ZcashSdk.MINERS_FEE.value.toDouble() / Zatoshi.ZATOSHI_PER_ZEC) // 0.00001 + val hasFunds = + saplingBalance.available.value > + (ZcashSdk.MINERS_FEE.value.toDouble() / Zatoshi.ZATOSHI_PER_ZEC) // 0.00001 val hasSaplingBalance = saplingBalance.total.value > 0 val isSendEnabled: Boolean get() = status == Synchronizer.Status.SYNCED && hasFunds diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/CheckUpdateViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/CheckUpdateViewModel.kt index dce92a70..c3bfc8a0 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/CheckUpdateViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/CheckUpdateViewModel.kt @@ -15,7 +15,6 @@ class CheckUpdateViewModel( application: Application, private val appUpdateChecker: AppUpdateChecker ) : AndroidViewModel(application) { - val updateInfo: MutableStateFlow = MutableStateFlow(null) fun checkForAppUpdate() { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/HomeViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/HomeViewModel.kt index 0f6f341b..b12d373e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/HomeViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/HomeViewModel.kt @@ -15,14 +15,14 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.stateIn class HomeViewModel(application: Application) : AndroidViewModel(application) { - /** * A flow of whether background sync is enabled */ - val isBackgroundSyncEnabled: StateFlow = flow { - val preferenceProvider = StandardPreferenceSingleton.getInstance(application) - emitAll(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED.observe(preferenceProvider)) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT.inWholeMilliseconds), null) + val isBackgroundSyncEnabled: StateFlow = + flow { + val preferenceProvider = StandardPreferenceSingleton.getInstance(application) + emitAll(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED.observe(preferenceProvider)) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT.inWholeMilliseconds), null) val configurationFlow: StateFlow = AndroidConfigurationFactory.getInstance(application).getConfigurationFlow() diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/WalletViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/WalletViewModel.kt index 0a1b0ad6..1eeaa802 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/WalletViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/WalletViewModel.kt @@ -75,140 +75,151 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) /** * Synchronizer that is retained long enough to survive configuration changes. */ - val synchronizer = walletCoordinator.synchronizer.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - null - ) + val synchronizer = + walletCoordinator.synchronizer.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + null + ) /** * A flow of the user's preferred fiat currency. */ - val preferredFiatCurrency: StateFlow = flow { - val preferenceProvider = StandardPreferenceSingleton.getInstance(application) - emitAll(StandardPreferenceKeys.PREFERRED_FIAT_CURRENCY.observe(preferenceProvider)) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - null - ) + val preferredFiatCurrency: StateFlow = + flow { + val preferenceProvider = StandardPreferenceSingleton.getInstance(application) + emitAll(StandardPreferenceKeys.PREFERRED_FIAT_CURRENCY.observe(preferenceProvider)) + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + null + ) /** * A flow of the wallet onboarding state. */ - private val onboardingState = flow { - val preferenceProvider = StandardPreferenceSingleton.getInstance(application) - emitAll( - StandardPreferenceKeys.ONBOARDING_STATE.observe(preferenceProvider).map { persistedNumber -> - OnboardingState.fromNumber(persistedNumber) - } - ) - } - - val secretState: StateFlow = combine( - walletCoordinator.persistableWallet, - onboardingState - ) { persistableWallet: PersistableWallet?, onboardingState: OnboardingState -> - when { - onboardingState == OnboardingState.NONE -> SecretState.None - onboardingState == OnboardingState.NEEDS_WARN -> SecretState.NeedsWarning - onboardingState == OnboardingState.NEEDS_BACKUP && persistableWallet != null -> { - SecretState.NeedsBackup(persistableWallet) - } - onboardingState == OnboardingState.READY && persistableWallet != null -> { - SecretState.Ready(persistableWallet) - } - else -> SecretState.None + private val onboardingState = + flow { + val preferenceProvider = StandardPreferenceSingleton.getInstance(application) + emitAll( + StandardPreferenceKeys.ONBOARDING_STATE.observe(preferenceProvider).map { persistedNumber -> + OnboardingState.fromNumber(persistedNumber) + } + ) } - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - SecretState.Loading - ) + + val secretState: StateFlow = + combine( + walletCoordinator.persistableWallet, + onboardingState + ) { persistableWallet: PersistableWallet?, onboardingState: OnboardingState -> + when { + onboardingState == OnboardingState.NONE -> SecretState.None + onboardingState == OnboardingState.NEEDS_WARN -> SecretState.NeedsWarning + onboardingState == OnboardingState.NEEDS_BACKUP && persistableWallet != null -> { + SecretState.NeedsBackup(persistableWallet) + } + onboardingState == OnboardingState.READY && persistableWallet != null -> { + SecretState.Ready(persistableWallet) + } + else -> SecretState.None + } + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + SecretState.Loading + ) // This needs to be refactored once we support pin lock - val spendingKey = secretState - .filterIsInstance() - .map { it.persistableWallet } - .map { - val bip39Seed = withContext(Dispatchers.IO) { - Mnemonics.MnemonicCode(it.seedPhrase.joinToString()).toSeed() - } - DerivationTool.getInstance().deriveUnifiedSpendingKey( - seed = bip39Seed, - network = it.network, - account = Account.DEFAULT + val spendingKey = + secretState + .filterIsInstance() + .map { it.persistableWallet } + .map { + val bip39Seed = + withContext(Dispatchers.IO) { + Mnemonics.MnemonicCode(it.seedPhrase.joinToString()).toSeed() + } + DerivationTool.getInstance().deriveUnifiedSpendingKey( + seed = bip39Seed, + network = it.network, + account = Account.DEFAULT + ) + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + null ) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - null - ) @OptIn(ExperimentalCoroutinesApi::class, ExperimentalTime::class) - val walletSnapshot: StateFlow = synchronizer - .flatMapLatest { - if (null == it) { - flowOf(null) - } else { - it.toWalletSnapshot() + val walletSnapshot: StateFlow = + synchronizer + .flatMapLatest { + if (null == it) { + flowOf(null) + } else { + it.toWalletSnapshot() + } } - } - .throttle(1.seconds) - .stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - null - ) + .throttle(1.seconds) + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + null + ) - val addresses: StateFlow = synchronizer - .filterNotNull() - .map { - WalletAddresses.new(it) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - null - ) + val addresses: StateFlow = + synchronizer + .filterNotNull() + .map { + WalletAddresses.new(it) + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + null + ) @OptIn(ExperimentalCoroutinesApi::class) - val transactionHistoryState = synchronizer - .filterNotNull() - .flatMapLatest { - it.transactions - .combine(it.status) { transactions: List, status: Synchronizer.Status -> - if (status.isSyncing()) { - TransactionHistorySyncState.Syncing(transactions.toPersistentList()) - } else { - TransactionHistorySyncState.Done(transactions.toPersistentList()) + val transactionHistoryState = + synchronizer + .filterNotNull() + .flatMapLatest { + it.transactions + .combine(it.status) { transactions: List, status: Synchronizer.Status -> + if (status.isSyncing()) { + TransactionHistorySyncState.Syncing(transactions.toPersistentList()) + } else { + TransactionHistorySyncState.Done(transactions.toPersistentList()) + } } - } - } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - initialValue = TransactionHistorySyncState.Loading - ) + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + initialValue = TransactionHistorySyncState.Loading + ) /** * Creates a wallet asynchronously and then persists it. Clients observe * [secretState] to see the side effects. This would be used for a user creating a new wallet. */ - /* - * Although waiting for the wallet to be written and then read back is slower, it is probably - * safer because it 1. guarantees the wallet is written to disk and 2. has a single source of truth. - */ fun persistNewWallet() { + /* + * Although waiting for the wallet to be written and then read back is slower, it is probably + * safer because it 1. guarantees the wallet is written to disk and 2. has a single source of truth. + */ + val application = getApplication() viewModelScope.launch { val zcashNetwork = ZcashNetwork.fromResources(application) - val newWallet = PersistableWallet.new( - application = application, - zcashNetwork = zcashNetwork, - endpoint = LightWalletEndpoint.defaultForNetwork(zcashNetwork), - walletInitMode = WalletInitMode.NewWallet - ) + val newWallet = + PersistableWallet.new( + application = application, + zcashNetwork = zcashNetwork, + endpoint = LightWalletEndpoint.defaultForNetwork(zcashNetwork), + walletInitMode = WalletInitMode.NewWallet + ) persistWallet(newWallet) } } @@ -280,17 +291,22 @@ class WalletViewModel(application: Application) : AndroidViewModel(application) */ sealed class SecretState { object Loading : SecretState() + object None : SecretState() + object NeedsWarning : SecretState() + class NeedsBackup(val persistableWallet: PersistableWallet) : SecretState() + class Ready(val persistableWallet: PersistableWallet) : SecretState() } +// TODO [#529]: Localize Synchronizer Errors +// TODO [#529]: https://github.com/Electric-Coin-Company/zashi-android/issues/529 + /** * Represents all kind of Synchronizer errors */ -// TODO [#529]: Localize Synchronizer Errors -// TODO [#529]: https://github.com/Electric-Coin-Company/zashi-android/issues/529 sealed class SynchronizerError { abstract fun getCauseMessage(): String? @@ -315,51 +331,59 @@ sealed class SynchronizerError { } } -private fun Synchronizer.toCommonError(): Flow = callbackFlow { - // just for initial default value emit - trySend(null) +private fun Synchronizer.toCommonError(): Flow = + callbackFlow { + // just for initial default value emit + trySend(null) - onCriticalErrorHandler = { - Twig.error { "WALLET - Error Critical: $it" } - trySend(SynchronizerError.Critical(it)) - false - } - onProcessorErrorHandler = { - Twig.error { "WALLET - Error Processor: $it" } - trySend(SynchronizerError.Processor(it)) - false - } - onSubmissionErrorHandler = { - Twig.error { "WALLET - Error Submission: $it" } - trySend(SynchronizerError.Submission(it)) - false - } - onSetupErrorHandler = { - Twig.error { "WALLET - Error Setup: $it" } - trySend(SynchronizerError.Setup(it)) - false - } - onChainErrorHandler = { x, y -> - Twig.error { "WALLET - Error Chain: $x, $y" } - trySend(SynchronizerError.Chain(x, y)) - } + onCriticalErrorHandler = { + Twig.error { "WALLET - Error Critical: $it" } + trySend(SynchronizerError.Critical(it)) + false + } + onProcessorErrorHandler = { + Twig.error { "WALLET - Error Processor: $it" } + trySend(SynchronizerError.Processor(it)) + false + } + onSubmissionErrorHandler = { + Twig.error { "WALLET - Error Submission: $it" } + trySend(SynchronizerError.Submission(it)) + false + } + onSetupErrorHandler = { + Twig.error { "WALLET - Error Setup: $it" } + trySend(SynchronizerError.Setup(it)) + false + } + onChainErrorHandler = { x, y -> + Twig.error { "WALLET - Error Chain: $x, $y" } + trySend(SynchronizerError.Chain(x, y)) + } - awaitClose { - // nothing to close here + awaitClose { + // nothing to close here + } } -} // No good way around needing magic numbers for the indices @Suppress("MagicNumber") private fun Synchronizer.toWalletSnapshot() = combine( - status, // 0 - processorInfo, // 1 - orchardBalances, // 2 - saplingBalances, // 3 - transparentBalances, // 4 - progress, // 5 - toCommonError() // 6 + // 0 + status, + // 1 + processorInfo, + // 2 + orchardBalances, + // 3 + saplingBalances, + // 4 + transparentBalances, + // 5 + progress, + // 6 + toCommonError() ) { flows -> val orchardBalance = flows[2] as WalletBalance? val saplingBalance = flows[3] as WalletBalance? diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/ConfigInfoFixture.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/ConfigInfoFixture.kt index 90c52e3e..0ea7f34d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/ConfigInfoFixture.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/ConfigInfoFixture.kt @@ -4,12 +4,8 @@ import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo import kotlinx.datetime.Instant import kotlinx.datetime.toInstant -// Magic Number doesn't matter here for hard-coded fixture values -@Suppress("MagicNumber") object ConfigInfoFixture { val UPDATED_AT = "2023-01-15T08:38:45.415Z".toInstant() - fun new( - updatedAt: Instant? = UPDATED_AT, - ) = ConfigInfo(updatedAt) + fun new(updatedAt: Instant? = UPDATED_AT) = ConfigInfo(updatedAt) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/UpdateInfoFixture.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/UpdateInfoFixture.kt index 4e0ee980..a6da414d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/UpdateInfoFixture.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/UpdateInfoFixture.kt @@ -6,7 +6,6 @@ import co.electriccoin.zcash.ui.screen.update.model.UpdateState import com.google.android.play.core.appupdate.AppUpdateInfo object UpdateInfoFixture { - val INITIAL_PRIORITY = AppUpdateChecker.Priority.LOW val INITIAL_STATE = UpdateState.Prepared const val INITIAL_FORCE = false diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/WalletSnapshotFixture.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/WalletSnapshotFixture.kt index 674b1807..aed049e6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/WalletSnapshotFixture.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/fixture/WalletSnapshotFixture.kt @@ -10,7 +10,6 @@ import co.electriccoin.zcash.ui.common.viewmodel.SynchronizerError @Suppress("MagicNumber") object WalletSnapshotFixture { - val STATUS = Synchronizer.Status.SYNCED val PROGRESS = PercentDecimal.ZERO_PERCENT val TRANSPARENT_BALANCE: WalletBalance = WalletBalance(Zatoshi(8), Zatoshi(1)) @@ -21,11 +20,12 @@ object WalletSnapshotFixture { @Suppress("LongParameterList") fun new( status: Synchronizer.Status = STATUS, - processorInfo: CompactBlockProcessor.ProcessorInfo = CompactBlockProcessor.ProcessorInfo( - null, - null, - null - ), + processorInfo: CompactBlockProcessor.ProcessorInfo = + CompactBlockProcessor.ProcessorInfo( + null, + null, + null + ), orchardBalance: WalletBalance = ORCHARD_BALANCE, saplingBalance: WalletBalance = SAPLING_BALANCE, transparentBalance: WalletBalance = TRANSPARENT_BALANCE, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceKeys.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceKeys.kt index 0bb09a0e..3eae26db 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceKeys.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceKeys.kt @@ -3,6 +3,5 @@ package co.electriccoin.zcash.ui.preference import co.electriccoin.zcash.preference.model.entry.PreferenceKey object EncryptedPreferenceKeys { - val PERSISTABLE_WALLET = PersistableWalletPreferenceDefault(PreferenceKey("persistable_wallet")) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceSingleton.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceSingleton.kt index daa75e19..2c58b20a 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceSingleton.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/EncryptedPreferenceSingleton.kt @@ -6,12 +6,12 @@ import co.electriccoin.zcash.preference.api.PreferenceProvider import co.electriccoin.zcash.spackle.SuspendingLazy object EncryptedPreferenceSingleton { - private const val PREF_FILENAME = "co.electriccoin.zcash.encrypted" - private val lazy = SuspendingLazy { - AndroidPreferenceProvider.newEncrypted(it, PREF_FILENAME) - } + private val lazy = + SuspendingLazy { + AndroidPreferenceProvider.newEncrypted(it, PREF_FILENAME) + } suspend fun getInstance(context: Context) = lazy.getInstance(context) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/FiatCurrencyPreferenceDefault.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/FiatCurrencyPreferenceDefault.kt index 8e1aec19..b35f8594 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/FiatCurrencyPreferenceDefault.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/FiatCurrencyPreferenceDefault.kt @@ -8,7 +8,6 @@ import co.electriccoin.zcash.preference.model.entry.PreferenceKey data class FiatCurrencyPreferenceDefault( override val key: PreferenceKey ) : PreferenceDefault { - override suspend fun getValue(preferenceProvider: PreferenceProvider) = preferenceProvider.getString(key)?.let { FiatCurrency(it) } ?: FiatCurrency("USD") diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/PersistableWalletPreferenceDefault.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/PersistableWalletPreferenceDefault.kt index debba0a2..7850bfcd 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/PersistableWalletPreferenceDefault.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/PersistableWalletPreferenceDefault.kt @@ -9,7 +9,6 @@ import org.json.JSONObject data class PersistableWalletPreferenceDefault( override val key: PreferenceKey ) : PreferenceDefault { - override suspend fun getValue(preferenceProvider: PreferenceProvider) = preferenceProvider.getString(key)?.let { PersistableWallet.from(JSONObject(it)) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceKeys.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceKeys.kt index c23b0cc0..e5ddd7f1 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceKeys.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceKeys.kt @@ -6,14 +6,14 @@ import co.electriccoin.zcash.preference.model.entry.PreferenceKey import co.electriccoin.zcash.ui.common.model.OnboardingState object StandardPreferenceKeys { - /** * State defining whether the user has completed any of the onboarding wallet states. */ - val ONBOARDING_STATE = IntegerPreferenceDefault( - PreferenceKey("onboarding_state"), - OnboardingState.NONE.toNumber() - ) + val ONBOARDING_STATE = + IntegerPreferenceDefault( + PreferenceKey("onboarding_state"), + OnboardingState.NONE.toNumber() + ) // Default to true until https://github.com/Electric-Coin-Company/zashi-android/issues/304 val IS_ANALYTICS_ENABLED = BooleanPreferenceDefault(PreferenceKey("is_analytics_enabled"), true) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceSingleton.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceSingleton.kt index da5f87d8..774388e3 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceSingleton.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/preference/StandardPreferenceSingleton.kt @@ -6,12 +6,12 @@ import co.electriccoin.zcash.preference.api.PreferenceProvider import co.electriccoin.zcash.spackle.SuspendingLazy object StandardPreferenceSingleton { - private const val PREF_FILENAME = "co.electriccoin.zcash" - private val lazy = SuspendingLazy { - AndroidPreferenceProvider.newStandard(it, PREF_FILENAME) - } + private val lazy = + SuspendingLazy { + AndroidPreferenceProvider.newStandard(it, PREF_FILENAME) + } suspend fun getInstance(context: Context) = lazy.getInstance(context) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/AndroidAboutView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/AndroidAboutView.kt index f61e8a42..162613fa 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/AndroidAboutView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/AndroidAboutView.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.about @@ -12,9 +12,7 @@ import co.electriccoin.zcash.ui.screen.about.view.About import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo @Composable -internal fun MainActivity.WrapAbout( - goBack: () -> Unit -) { +internal fun MainActivity.WrapAbout(goBack: () -> Unit) { WrapAbout(this, goBack) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt index eb15b30c..1ae04a29 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/about/view/AboutView.kt @@ -69,17 +69,18 @@ fun About( }) { paddingValues -> AboutMainContent( versionInfo = versionInfo, - modifier = Modifier - .fillMaxHeight() - .verticalScroll( - rememberScrollState() - ) - .padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .fillMaxHeight() + .verticalScroll( + rememberScrollState() + ) + .padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingDefault, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @@ -149,9 +150,10 @@ fun AboutMainContent( val logoContentDescription = stringResource(R.string.zcash_logo_content_description) Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.semantics(mergeDescendants = true) { - contentDescription = logoContentDescription - } + modifier = + Modifier.semantics(mergeDescendants = true) { + contentDescription = logoContentDescription + } ) { Image( painter = painterResource(id = co.electriccoin.zcash.ui.design.R.drawable.zashi_logo_without_text), @@ -171,10 +173,11 @@ fun AboutMainContent( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) Text( - text = stringResource( - R.string.about_version_format, - versionInfo.versionName - ), + text = + stringResource( + R.string.about_version_format, + versionInfo.versionName + ), style = ZcashTheme.typography.primary.titleSmall ) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/AndroidAccount.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/AndroidAccount.kt index e3c47471..e940da69 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/AndroidAccount.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/AndroidAccount.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.account @@ -49,9 +49,10 @@ internal fun WrapAccount( AppUpdateCheckerImp.new() ) } - val updateAvailable = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let { - it?.appUpdateInfo != null && it.state == UpdateState.Prepared - } + val updateAvailable = + checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let { + it?.appUpdateInfo != null && it.state == UpdateState.Prepared + } val walletViewModel by activity.viewModels() val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/model/WalletDisplayValues.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/model/WalletDisplayValues.kt index 47ab691b..7f96bccc 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/model/WalletDisplayValues.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/model/WalletDisplayValues.kt @@ -35,11 +35,12 @@ data class WalletDisplayValues( // TODO [#578]: Provide Zatoshi -> USD fiat currency formatting // TODO [#578]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/578 // We'll ideally provide a "fresh" currencyConversion object here - val fiatCurrencyAmountState = walletSnapshot.spendableBalance().toFiatCurrencyState( - null, - Locale.current.toKotlinLocale(), - MonetarySeparators.current() - ) + val fiatCurrencyAmountState = + walletSnapshot.spendableBalance().toFiatCurrencyState( + null, + Locale.current.toKotlinLocale(), + MonetarySeparators.current() + ) var fiatCurrencyAmountText = getFiatCurrencyRateValue(context, fiatCurrencyAmountState) when (walletSnapshot.status) { @@ -47,28 +48,32 @@ data class WalletDisplayValues( progress = walletSnapshot.progress // we add "so far" to the amount if (fiatCurrencyAmountState != FiatCurrencyConversionRateState.Unavailable) { - fiatCurrencyAmountText = context.getString( - R.string.account_status_syncing_amount_suffix, - fiatCurrencyAmountText - ) + fiatCurrencyAmountText = + context.getString( + R.string.account_status_syncing_amount_suffix, + fiatCurrencyAmountText + ) } - statusText = context.getString( - R.string.account_status_syncing_format, - walletSnapshot.progress.toPercentageWithDecimal() - ) + statusText = + context.getString( + R.string.account_status_syncing_format, + walletSnapshot.progress.toPercentageWithDecimal() + ) } Synchronizer.Status.SYNCED -> { - statusText = if (updateAvailable) { - context.getString(R.string.account_status_update) - } else { - context.getString(R.string.account_status_up_to_date) - } + statusText = + if (updateAvailable) { + context.getString(R.string.account_status_update) + } else { + context.getString(R.string.account_status_up_to_date) + } } Synchronizer.Status.DISCONNECTED -> { - statusText = context.getString( - R.string.account_status_error, - context.getString(R.string.account_status_error_connection) - ) + statusText = + context.getString( + R.string.account_status_error, + context.getString(R.string.account_status_error_connection) + ) } Synchronizer.Status.STOPPED -> { statusText = context.getString(R.string.account_status_stopped) @@ -77,11 +82,12 @@ data class WalletDisplayValues( // more detailed error message walletSnapshot.synchronizerError?.let { - statusText = context.getString( - R.string.account_status_error, - walletSnapshot.synchronizerError.getCauseMessage() - ?: context.getString(R.string.account_status_error_unknown) - ) + statusText = + context.getString( + R.string.account_status_error, + walletSnapshot.synchronizerError.getCauseMessage() + ?: context.getString(R.string.account_status_error_unknown) + ) } return WalletDisplayValues( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/view/AccountView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/view/AccountView.kt index 93b46058..819a6d92 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/view/AccountView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/account/view/AccountView.kt @@ -79,20 +79,19 @@ fun Account( goReceive = goReceive, goSend = goSend, goHistory = goHistory, - modifier = Modifier.padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingHuge, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingHuge, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @Composable -private fun AccountTopAppBar( - onSettings: () -> Unit -) { +private fun AccountTopAppBar(onSettings: () -> Unit) { SmallTopAppBar( showTitleLogo = true, hamburgerMenuActions = { @@ -135,9 +134,10 @@ private fun AccountMainContent( Status(walletSnapshot, isUpdateAvailable, isFiatConversionEnabled) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) @@ -171,16 +171,18 @@ private fun Status( updateAvailable: Boolean, isFiatConversionEnabled: Boolean ) { - val walletDisplayValues = WalletDisplayValues.getNextValues( - LocalContext.current, - walletSnapshot, - updateAvailable - ) + val walletDisplayValues = + WalletDisplayValues.getNextValues( + LocalContext.current, + walletSnapshot, + updateAvailable + ) Column( - modifier = Modifier - .fillMaxWidth() - .testTag(AccountTag.STATUS_VIEWS), + modifier = + Modifier + .fillMaxWidth() + .testTag(AccountTag.STATUS_VIEWS), horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt index d6e4c5e7..0be8ea2c 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/AndroidWalletAddresses.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.address @@ -13,9 +13,7 @@ import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.screen.address.view.WalletAddresses @Composable -internal fun MainActivity.WrapWalletAddresses( - goBack: () -> Unit -) { +internal fun MainActivity.WrapWalletAddresses(goBack: () -> Unit) { WrapWalletAddresses(this, goBack) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt index d76c3285..38218a97 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/address/view/WalletAddressView.kt @@ -80,9 +80,10 @@ fun WalletAddresses( WalletDetailAddresses( walletAddresses = walletAddresses, onCopyToClipboard = onCopyToClipboard, - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) + modifier = + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) ) } } @@ -127,9 +128,10 @@ private fun WalletDetailAddresses( Image( painter = ColorPainter(ZcashTheme.colors.highlight), contentDescription = "", - modifier = Modifier - .fillMaxHeight() - .width(BIG_INDICATOR_WIDTH) + modifier = + Modifier + .fillMaxHeight() + .width(BIG_INDICATOR_WIDTH) ) Column(Modifier.fillMaxWidth()) { @@ -151,17 +153,19 @@ private fun WalletDetailAddresses( SaplingAddress( saplingAddress = walletAddresses.sapling.address, onCopyToClipboard = onCopyToClipboard, - modifier = Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) + modifier = + Modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) ) TransparentAddress( transparentAddress = walletAddresses.transparent.address, onCopyToClipboard = onCopyToClipboard, - modifier = Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) + modifier = + Modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) ) } } @@ -219,32 +223,35 @@ private fun ExpandableRow( Column { Row( verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .defaultMinSize(minHeight = 48.dp) - .clickable { expandedState = !expandedState } - .padding( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingTiny - ) + modifier = + Modifier + .defaultMinSize(minHeight = 48.dp) + .clickable { expandedState = !expandedState } + .padding( + horizontal = ZcashTheme.dimens.spacingDefault, + vertical = ZcashTheme.dimens.spacingTiny + ) ) { ListItem(text = title) Spacer( - modifier = Modifier - .fillMaxWidth() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxWidth() + .weight(MINIMAL_WEIGHT) ) ExpandableArrow(expandedState) } if (expandedState) { Body( content, - modifier = Modifier - .clickable { onCopyToClipboard(content) } - .padding( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingTiny - ) - .testTag(WalletAddressesTag.WALLET_ADDRESS) + modifier = + Modifier + .clickable { onCopyToClipboard(content) } + .padding( + horizontal = ZcashTheme.dimens.spacingDefault, + vertical = ZcashTheme.dimens.spacingTiny + ) + .testTag(WalletAddressesTag.WALLET_ADDRESS) ) } } @@ -255,10 +262,11 @@ private fun SmallIndicator(color: Color) { // TODO [#160]: Border is not the right implementation here, as it causes double thickness for the middle item // TODO [#160]: https://github.com/Electric-Coin-Company/zashi-android/issues/160 Image( - modifier = Modifier - .fillMaxHeight() - .width(SMALL_INDICATOR_WIDTH) - .border(1.dp, ZcashTheme.colors.addressHighlightBorder), + modifier = + Modifier + .fillMaxHeight() + .width(SMALL_INDICATOR_WIDTH) + .border(1.dp, ZcashTheme.colors.addressHighlightBorder), painter = ColorPainter(color), contentDescription = "" ) @@ -270,16 +278,18 @@ private const val NINETY_DEGREES = 90f private fun ExpandableArrow(isExpanded: Boolean) { Icon( imageVector = Icons.Filled.ArrowDropDownCircle, - contentDescription = if (isExpanded) { - stringResource(id = R.string.wallet_address_hide) - } else { - stringResource(id = R.string.wallet_address_show) - }, - modifier = if (isExpanded) { - Modifier - } else { - Modifier.rotate(NINETY_DEGREES) - }, + contentDescription = + if (isExpanded) { + stringResource(id = R.string.wallet_address_hide) + } else { + stringResource(id = R.string.wallet_address_show) + }, + modifier = + if (isExpanded) { + Modifier + } else { + Modifier.rotate(NINETY_DEGREES) + }, tint = MaterialTheme.colorScheme.onBackground ) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt index 2aeaad1b..d21663be 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/AndroidExportPrivateData.kt @@ -77,27 +77,30 @@ fun shareData( context: Context, synchronizer: Synchronizer, snackbarHostState: SnackbarHostState, -): Flow = callbackFlow { - val shareIntent = FileShareUtil.newShareContentIntent( - context = context, - // Example of the expected db file absolute path: - // /data/user/0/co.electriccoin.zcash.debug/no_backup/co.electricoin.zcash/zcash_sdk_mainnet_data.sqlite3 - dataFilePath = (synchronizer as SdkSynchronizer).getExistingDataDbFilePath( - context = context, - network = ZcashNetwork.fromResources(context) - ), - versionInfo = VersionInfo.new(context.applicationContext) - ) - runCatching { - context.startActivity(shareIntent) - trySend(true) - }.onFailure { - snackbarHostState.showSnackbar( - message = context.getString(R.string.export_data_unable_to_share) - ) - trySend(false) +): Flow = + callbackFlow { + val shareIntent = + FileShareUtil.newShareContentIntent( + context = context, + // Example of the expected db file absolute path: + // /data/user/0/co.electriccoin.zcash/no_backup/co.electricoin.zcash/zcash_sdk_mainnet_data.sqlite3 + dataFilePath = + (synchronizer as SdkSynchronizer).getExistingDataDbFilePath( + context = context, + network = ZcashNetwork.fromResources(context) + ), + versionInfo = VersionInfo.new(context.applicationContext) + ) + runCatching { + context.startActivity(shareIntent) + trySend(true) + }.onFailure { + snackbarHostState.showSnackbar( + message = context.getString(R.string.export_data_unable_to_share) + ) + trySend(false) + } + awaitClose { + // No resources to release + } } - awaitClose { - // No resources to release - } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt index f84876f6..b053ed85 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/util/FileShareUtil.kt @@ -8,7 +8,6 @@ import co.electriccoin.zcash.ui.common.model.VersionInfo import java.io.File object FileShareUtil { - const val SHARE_OUTSIDE_THE_APP_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK const val SHARE_CONTENT_PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION @@ -30,31 +29,34 @@ object FileShareUtil { dataFilePath: String, versionInfo: VersionInfo ): Intent { - val fileUri = FileProvider.getUriForFile( - context, - if (versionInfo.isDebuggable) { - ZASHI_INTERNAL_DATA_AUTHORITY_DEBUG - } else { - ZASHI_INTERNAL_DATA_AUTHORITY - }, - File(dataFilePath) - ) - - val dataIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, fileUri) - type = ZASHI_INTERNAL_DATA_MIME_TYPE - } - - val shareDataIntent = Intent.createChooser( - dataIntent, - context.getString(R.string.export_data_export_data_chooser_title) - ).apply { - addFlags( - SHARE_CONTENT_PERMISSION_FLAGS or - SHARE_OUTSIDE_THE_APP_FLAGS + val fileUri = + FileProvider.getUriForFile( + context, + if (versionInfo.isDebuggable) { + ZASHI_INTERNAL_DATA_AUTHORITY_DEBUG + } else { + ZASHI_INTERNAL_DATA_AUTHORITY + }, + File(dataFilePath) ) - } + + val dataIntent: Intent = + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, fileUri) + type = ZASHI_INTERNAL_DATA_MIME_TYPE + } + + val shareDataIntent = + Intent.createChooser( + dataIntent, + context.getString(R.string.export_data_export_data_chooser_title) + ).apply { + addFlags( + SHARE_CONTENT_PERMISSION_FLAGS or + SHARE_OUTSIDE_THE_APP_FLAGS + ) + } return shareDataIntent } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataView.kt index 9ae7051d..016fc47f 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/exportdata/view/ExportPrivateDataView.kt @@ -64,23 +64,22 @@ fun ExportPrivateData( ExportPrivateDataContent( onAgree = onAgree, onConfirm = onConfirm, - modifier = Modifier - .fillMaxSize() - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) - .verticalScroll(rememberScrollState()) + modifier = + Modifier + .fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) + .verticalScroll(rememberScrollState()) ) } } @Composable -private fun ExportPrivateDataTopAppBar( - onBack: () -> Unit, -) { +private fun ExportPrivateDataTopAppBar(onBack: () -> Unit) { SmallTopAppBar( backText = stringResource(R.string.export_data_back).uppercase(), backContentDescriptionText = stringResource(R.string.export_data_back_content_description), @@ -122,9 +121,10 @@ private fun ExportPrivateDataContent( val checkedState = rememberSaveable { mutableStateOf(false) } CheckBox( - modifier = Modifier - .align(Alignment.Start) - .fillMaxWidth(), + modifier = + Modifier + .align(Alignment.Start) + .fillMaxWidth(), checked = checkedState.value, onCheckedChange = { checkedState.value = it @@ -135,9 +135,10 @@ private fun ExportPrivateDataContent( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/AndroidHistory.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/AndroidHistory.kt index 92319ddd..21b1467e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/AndroidHistory.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/AndroidHistory.kt @@ -10,9 +10,7 @@ import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel import co.electriccoin.zcash.ui.screen.history.view.History @Composable -internal fun MainActivity.WrapHistory( - goBack: () -> Unit -) { +internal fun MainActivity.WrapHistory(goBack: () -> Unit) { WrapHistory( activity = this, goBack = goBack diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/state/TransactionHistorySyncState.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/state/TransactionHistorySyncState.kt index 50ab0856..d4f0f462 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/state/TransactionHistorySyncState.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/state/TransactionHistorySyncState.kt @@ -4,15 +4,16 @@ import cash.z.ecc.android.sdk.model.TransactionOverview import kotlinx.collections.immutable.ImmutableList sealed class TransactionHistorySyncState { - object Loading : TransactionHistorySyncState() { override fun toString() = "Loading" // NON-NLS } + data class Syncing(val transactions: ImmutableList) : TransactionHistorySyncState() { fun hasNoTransactions(): Boolean { return transactions.isEmpty() } } + data class Done(val transactions: ImmutableList) : TransactionHistorySyncState() { fun hasNoTransactions(): Boolean { return transactions.isEmpty() diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/view/HistoryView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/view/HistoryView.kt index b6c62cce..2b1abea8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/view/HistoryView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/history/view/HistoryView.kt @@ -77,14 +77,15 @@ private fun ComposableHistoryListPreview() { ZcashTheme(forceDarkMode = false) { GradientSurface { History( - transactionState = TransactionHistorySyncState.Syncing( - @Suppress("MagicNumber") - persistentListOf( - TransactionOverviewFixture.new(netValue = Zatoshi(100000000)), - TransactionOverviewFixture.new(netValue = Zatoshi(200000000)), - TransactionOverviewFixture.new(netValue = Zatoshi(300000000)), - ) - ), + transactionState = + TransactionHistorySyncState.Syncing( + @Suppress("MagicNumber") + persistentListOf( + TransactionOverviewFixture.new(netValue = Zatoshi(100000000)), + TransactionOverviewFixture.new(netValue = Zatoshi(200000000)), + TransactionOverviewFixture.new(netValue = Zatoshi(300000000)), + ) + ), goBack = {} ) } @@ -109,12 +110,13 @@ fun History( }) { paddingValues -> HistoryMainContent( transactionState = transactionState, - modifier = Modifier - .fillMaxHeight() - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding() - ) + modifier = + Modifier + .fillMaxHeight() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding() + ) ) } } @@ -146,9 +148,10 @@ private fun HistoryMainContent( when (transactionState) { is TransactionHistorySyncState.Loading -> { CircularProgressIndicator( - modifier = Modifier - .align(alignment = Center) - .testTag(HistoryTag.PROGRESS) + modifier = + Modifier + .align(alignment = Center) + .testTag(HistoryTag.PROGRESS) ) } is TransactionHistorySyncState.Syncing -> { @@ -157,23 +160,24 @@ private fun HistoryMainContent( ) { Body( text = stringResource(id = R.string.history_syncing), - modifier = Modifier - .padding( - top = ZcashTheme.dimens.spacingSmall, - bottom = ZcashTheme.dimens.spacingSmall, - start = ZcashTheme.dimens.spacingDefault, - end = ZcashTheme.dimens.spacingDefault - ) + modifier = + Modifier + .padding( + top = ZcashTheme.dimens.spacingSmall, + bottom = ZcashTheme.dimens.spacingSmall, + start = ZcashTheme.dimens.spacingDefault, + end = ZcashTheme.dimens.spacingDefault + ) ) HistoryList(transactions = transactionState.transactions) } // Add progress indicator only in the state of empty transaction if (transactionState.hasNoTransactions()) { CircularProgressIndicator( - modifier = Modifier - .align(alignment = Center) - .testTag(HistoryTag.PROGRESS) - + modifier = + Modifier + .align(alignment = Center) + .testTag(HistoryTag.PROGRESS) ) } } @@ -181,9 +185,10 @@ private fun HistoryMainContent( if (transactionState.hasNoTransactions()) { Body( text = stringResource(id = R.string.history_empty), - modifier = Modifier - .padding(all = ZcashTheme.dimens.spacingDefault) - .align(alignment = Center) + modifier = + Modifier + .padding(all = ZcashTheme.dimens.spacingDefault) + .align(alignment = Center) ) } else { HistoryList(transactions = transactionState.transactions) @@ -216,9 +221,10 @@ fun HistoryItem( currency: ZcashCurrency ) { Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = ZcashTheme.dimens.spacingSmall), + modifier = + Modifier + .fillMaxWidth() + .padding(vertical = ZcashTheme.dimens.spacingSmall), verticalAlignment = Alignment.CenterVertically ) { val transactionText: String @@ -262,13 +268,14 @@ fun HistoryItem( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingTiny)) - val dateString = transaction.minedHeight?.let { - transaction.blockTimeEpochSeconds?.let { blockTimeEpochSeconds -> - // * 1000 to covert to millis - @Suppress("MagicNumber") - dateFormat.format(blockTimeEpochSeconds.times(1000L)) + val dateString = + transaction.minedHeight?.let { + transaction.blockTimeEpochSeconds?.let { blockTimeEpochSeconds -> + // * 1000 to covert to millis + @Suppress("MagicNumber") + dateFormat.format(blockTimeEpochSeconds.times(1000L)) + } ?: stringResource(id = R.string.history_item_date_not_available) } ?: stringResource(id = R.string.history_item_date_not_available) - } ?: stringResource(id = R.string.history_item_date_not_available) // For now, use the same label for the above missing transaction date Body( @@ -280,11 +287,12 @@ fun HistoryItem( Column { Row(modifier = Modifier.align(alignment = Alignment.End)) { - val zecString = if (transaction.isSentTransaction) { - "-${transaction.netValue.toZecString()}" - } else { - transaction.netValue.toZecString() - } + val zecString = + if (transaction.isSentTransaction) { + "-${transaction.netValue.toZecString()}" + } else { + transaction.netValue.toZecString() + } Body(text = zecString) Spacer(modifier = Modifier.width(ZcashTheme.dimens.spacingTiny)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt index 751a5680..331765b7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/AndroidHome.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.home @@ -49,9 +49,10 @@ internal fun WrapHome( AppUpdateCheckerImp.new() ) } - val updateAvailable = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let { - it?.appUpdateInfo != null && it.state == UpdateState.Prepared - } + val updateAvailable = + checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value.let { + it?.appUpdateInfo != null && it.state == UpdateState.Prepared + } val walletViewModel by activity.viewModels() val walletSnapshot = walletViewModel.walletSnapshot.collectAsStateWithLifecycle().value diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValues.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValues.kt index 881affc8..174ff1ab 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValues.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/model/WalletDisplayValues.kt @@ -35,11 +35,12 @@ data class WalletDisplayValues( // TODO [#578]: Provide Zatoshi -> USD fiat currency formatting // TODO [#578]: https://github.com/Electric-Coin-Company/zcash-android-wallet-sdk/issues/578 // We'll ideally provide a "fresh" currencyConversion object here - val fiatCurrencyAmountState = walletSnapshot.spendableBalance().toFiatCurrencyState( - null, - Locale.current.toKotlinLocale(), - MonetarySeparators.current() - ) + val fiatCurrencyAmountState = + walletSnapshot.spendableBalance().toFiatCurrencyState( + null, + Locale.current.toKotlinLocale(), + MonetarySeparators.current() + ) var fiatCurrencyAmountText = getFiatCurrencyRateValue(context, fiatCurrencyAmountState) when (walletSnapshot.status) { @@ -47,28 +48,32 @@ data class WalletDisplayValues( progress = walletSnapshot.progress // we add "so far" to the amount if (fiatCurrencyAmountState != FiatCurrencyConversionRateState.Unavailable) { - fiatCurrencyAmountText = context.getString( - R.string.home_status_syncing_amount_suffix, - fiatCurrencyAmountText - ) + fiatCurrencyAmountText = + context.getString( + R.string.home_status_syncing_amount_suffix, + fiatCurrencyAmountText + ) } - statusText = context.getString( - R.string.home_status_syncing_format, - walletSnapshot.progress.toPercentageWithDecimal() - ) + statusText = + context.getString( + R.string.home_status_syncing_format, + walletSnapshot.progress.toPercentageWithDecimal() + ) } Synchronizer.Status.SYNCED -> { - statusText = if (updateAvailable) { - context.getString(R.string.home_status_update) - } else { - context.getString(R.string.home_status_up_to_date) - } + statusText = + if (updateAvailable) { + context.getString(R.string.home_status_update) + } else { + context.getString(R.string.home_status_up_to_date) + } } Synchronizer.Status.DISCONNECTED -> { - statusText = context.getString( - R.string.home_status_error, - context.getString(R.string.home_status_error_connection) - ) + statusText = + context.getString( + R.string.home_status_error, + context.getString(R.string.home_status_error_connection) + ) } Synchronizer.Status.STOPPED -> { statusText = context.getString(R.string.home_status_stopped) @@ -77,11 +82,12 @@ data class WalletDisplayValues( // more detailed error message walletSnapshot.synchronizerError?.let { - statusText = context.getString( - R.string.home_status_error, - walletSnapshot.synchronizerError.getCauseMessage() - ?: context.getString(R.string.home_status_error_unknown) - ) + statusText = + context.getString( + R.string.home_status_error, + walletSnapshot.synchronizerError.getCauseMessage() + ?: context.getString(R.string.home_status_error_unknown) + ) } return WalletDisplayValues( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt index e6c44fc2..747b31be 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt @@ -81,20 +81,19 @@ fun Home( goReceive = goReceive, goSend = goSend, goHistory = goHistory, - modifier = Modifier.padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingHuge, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding() + ZcashTheme.dimens.spacingHuge, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @Composable -private fun HomeTopAppBar( - onSettings: () -> Unit -) { +private fun HomeTopAppBar(onSettings: () -> Unit) { SmallTopAppBar( showTitleLogo = true, hamburgerMenuActions = { @@ -137,9 +136,10 @@ private fun HomeMainContent( Status(walletSnapshot, isUpdateAvailable, isFiatConversionEnabled) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) @@ -173,16 +173,18 @@ private fun Status( updateAvailable: Boolean, isFiatConversionEnabled: Boolean ) { - val walletDisplayValues = WalletDisplayValues.getNextValues( - LocalContext.current, - walletSnapshot, - updateAvailable - ) + val walletDisplayValues = + WalletDisplayValues.getNextValues( + LocalContext.current, + walletSnapshot, + updateAvailable + ) Column( - modifier = Modifier - .fillMaxWidth() - .testTag(HomeTag.STATUS_VIEWS), + modifier = + Modifier + .fillMaxWidth() + .testTag(HomeTag.STATUS_VIEWS), horizontalAlignment = Alignment.CenterHorizontally ) { Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryView.kt index 29d3c7d2..a4d7b8f2 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/newwalletrecovery/view/NewWalletRecoveryView.kt @@ -84,10 +84,11 @@ fun NewWalletRecovery( onBirthdayCopy = onBirthdayCopy, // Horizontal paddings will be part of each UI element to minimize a possible truncation on very // small screens - modifier = Modifier.padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding() - ) + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding() + ) ) } } @@ -116,9 +117,10 @@ private fun NewWalletRecoveryCopyToBufferMenuItem( text = stringResource(id = R.string.new_wallet_recovery_copy), onClick = onCopyToClipboard, textAlign = TextAlign.Center, - modifier = modifier.then( - Modifier.padding(all = ZcashTheme.dimens.spacingDefault) - ) + modifier = + modifier.then( + Modifier.padding(all = ZcashTheme.dimens.spacingDefault) + ) ) } @@ -160,9 +162,10 @@ private fun NewWalletRecoveryMainContent( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) @@ -170,12 +173,13 @@ private fun NewWalletRecoveryMainContent( PrimaryButton( onClick = onComplete, text = stringResource(R.string.new_wallet_recovery_button_finished), - modifier = Modifier - .padding( - bottom = ZcashTheme.dimens.spacingHuge, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .padding( + bottom = ZcashTheme.dimens.spacingHuge, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @@ -208,16 +212,18 @@ private fun NewWalletRecoverySeedPhrase( ) { BodySmall( text = stringResource(R.string.new_wallet_recovery_birthday_height, it.value), - modifier = Modifier - .testTag(WALLET_BIRTHDAY) - .padding(horizontal = ZcashTheme.dimens.spacingDefault) - .basicMarquee() - // Apply click callback to the text only as the wrapping layout can be much wider - .clickable( - interactionSource = interactionSource, - indication = null, // Disable ripple - onClick = onBirthdayCopy - ) + modifier = + Modifier + .testTag(WALLET_BIRTHDAY) + .padding(horizontal = ZcashTheme.dimens.spacingDefault) + .basicMarquee() + // Apply click callback to the text only as the wrapping layout can be much wider + .clickable( + interactionSource = interactionSource, + // Disable ripple + indication = null, + onClick = onBirthdayCopy + ) ) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt index 6a80ed9d..fe134b5d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/AndroidOnboarding.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.onboarding @@ -32,9 +32,7 @@ internal fun MainActivity.WrapOnboarding() { @Suppress("LongMethod") @Composable -internal fun WrapOnboarding( - activity: ComponentActivity -) { +internal fun WrapOnboarding(activity: ComponentActivity) { val walletViewModel by activity.viewModels() val onboardingViewModel by activity.viewModels() @@ -110,12 +108,13 @@ internal fun persistExistingWalletWithSeedPhrase( walletViewModel.persistOnboardingState(OnboardingState.READY) val network = ZcashNetwork.fromResources(context) - val restoredWallet = PersistableWallet( - network = network, - birthday = birthday, - endpoint = LightWalletEndpoint.defaultForNetwork(network), - seedPhrase = seedPhrase, - walletInitMode = WalletInitMode.RestoreWallet - ) + val restoredWallet = + PersistableWallet( + network = network, + birthday = birthday, + endpoint = LightWalletEndpoint.defaultForNetwork(network), + seedPhrase = seedPhrase, + walletInitMode = WalletInitMode.RestoreWallet + ) walletViewModel.persistExistingWallet(restoredWallet) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt index bb92a38c..dc8d8321 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/view/OnboardingView.kt @@ -90,9 +90,10 @@ fun ShortOnboarding( ) { Scaffold { paddingValues -> val screenHeight = screenHeight() - val (welcomeAnimVisibility, setWelcomeAnimVisibility) = rememberSaveable { - mutableStateOf(showWelcomeAnim) - } + val (welcomeAnimVisibility, setWelcomeAnimVisibility) = + rememberSaveable { + mutableStateOf(showWelcomeAnim) + } Column( modifier = Modifier.verticalScroll(rememberScrollState()) @@ -109,14 +110,15 @@ fun ShortOnboarding( onImportWallet = onImportWallet, onCreateWallet = onCreateWallet, onFixtureWallet = onFixtureWallet, - modifier = Modifier - .padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingHuge, - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) - .height(screenHeight.contentHeight - paddingValues.calculateBottomPadding()) + modifier = + Modifier + .padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingHuge, + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) + .height(screenHeight.contentHeight - paddingValues.calculateBottomPadding()) ) } } @@ -124,9 +126,7 @@ fun ShortOnboarding( } @Composable -private fun DebugMenu( - onFixtureWallet: (String) -> Unit -) { +private fun DebugMenu(onFixtureWallet: (String) -> Unit) { Column { var expanded by rememberSaveable { mutableStateOf(false) } IconButton(onClick = { expanded = true }) { @@ -191,9 +191,10 @@ private fun OnboardingMainContent( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) PrimaryButton( @@ -223,10 +224,11 @@ fun AnimatedImage( AnimatedVisibility( visible = welcomeAnimVisibility, - exit = slideOutVertically( - targetOffsetY = { -it }, - animationSpec = tween(AnimationConstants.ANIMATION_DURATION) - ), + exit = + slideOutVertically( + targetOffsetY = { -it }, + animationSpec = tween(AnimationConstants.ANIMATION_DURATION) + ), modifier = modifier ) { Box(modifier = Modifier.fillMaxSize()) { @@ -234,9 +236,10 @@ fun AnimatedImage( Image( painter = ColorPainter(ZcashTheme.colors.welcomeAnimationColor), contentScale = ContentScale.FillBounds, - modifier = Modifier - .fillMaxHeight() - .height(screenHeight.overallScreenHeight()), + modifier = + Modifier + .fillMaxHeight() + .height(screenHeight.overallScreenHeight()), contentDescription = null ) Image( @@ -249,10 +252,11 @@ fun AnimatedImage( Image( painter = painterResource(id = R.drawable.logo_with_hi), contentDescription = stringResource(R.string.zcash_logo_with_hi_text_content_description), - modifier = Modifier - .align(Alignment.TopCenter) - .fillMaxWidth() - .padding(top = screenHeight.systemStatusBarHeight + ZcashTheme.dimens.spacingHuge) + modifier = + Modifier + .align(Alignment.TopCenter) + .fillMaxWidth() + .padding(top = screenHeight.systemStatusBarHeight + ZcashTheme.dimens.spacingHuge) ) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/viewmodel/OnboardingViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/viewmodel/OnboardingViewModel.kt index ae2346fc..da748f03 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/viewmodel/OnboardingViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/onboarding/viewmodel/OnboardingViewModel.kt @@ -12,7 +12,6 @@ class OnboardingViewModel( application: Application, private val savedStateHandle: SavedStateHandle ) : AndroidViewModel(application) { - // This is a bit weird being placed here, but onboarding currently is considered complete when // the user has a persisted wallet. Also import allows the user to go back to onboarding, while // creating a new wallet does not. @@ -23,6 +22,7 @@ class OnboardingViewModel( } val showWelcomeAnimation = savedStateHandle.getStateFlow(KEY_SHOW_WELCOME_ANIMATION, true) + fun setShowWelcomeAnimation(setShowWelcomeAnimation: Boolean) { savedStateHandle[KEY_SHOW_WELCOME_ANIMATION] = setShowWelcomeAnimation } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt index 93a9daa0..fd94c7f7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/AndroidReceive.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.receive diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/AndroidQrCodeImageGenerator.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/AndroidQrCodeImageGenerator.kt index 7e6aef81..3f1b60e4 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/AndroidQrCodeImageGenerator.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/AndroidQrCodeImageGenerator.kt @@ -6,7 +6,10 @@ import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap object AndroidQrCodeImageGenerator : QrCodeImageGenerator { - override fun generate(bitArray: BooleanArray, sizePixels: Int): ImageBitmap { + override fun generate( + bitArray: BooleanArray, + sizePixels: Int + ): ImageBitmap { val colorArray = bitArray.toBlackAndWhiteColorArray() return Bitmap.createBitmap(colorArray, sizePixels, sizePixels, Bitmap.Config.ARGB_8888) @@ -14,10 +17,11 @@ object AndroidQrCodeImageGenerator : QrCodeImageGenerator { } } -private fun BooleanArray.toBlackAndWhiteColorArray() = IntArray(size) { - if (this[it]) { - Color.BLACK - } else { - Color.WHITE +private fun BooleanArray.toBlackAndWhiteColorArray() = + IntArray(size) { + if (this[it]) { + Color.BLACK + } else { + Color.WHITE + } } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt index 8664fa9b..4efc3d50 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/JvmQrCodeGenerator.kt @@ -4,10 +4,14 @@ import com.google.zxing.BarcodeFormat import com.google.zxing.qrcode.QRCodeWriter object JvmQrCodeGenerator : QrCodeGenerator { - override fun generate(data: String, sizePixels: Int): BooleanArray { - val bitMatrix = QRCodeWriter().let { - it.encode(data, BarcodeFormat.QR_CODE, sizePixels, sizePixels) - } + override fun generate( + data: String, + sizePixels: Int + ): BooleanArray { + val bitMatrix = + QRCodeWriter().let { + it.encode(data, BarcodeFormat.QR_CODE, sizePixels, sizePixels) + } return BooleanArray(sizePixels * sizePixels).apply { var booleanArrayPosition = 0 diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeGenerator.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeGenerator.kt index 229e0797..b6e4d39d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeGenerator.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeGenerator.kt @@ -6,5 +6,8 @@ interface QrCodeGenerator { * @param sizePixels Size in pixels of the QR code. * @return A QR code pixel matrix, represented as an array of booleans where false is white and true is black. */ - fun generate(data: String, sizePixels: Int): BooleanArray + fun generate( + data: String, + sizePixels: Int + ): BooleanArray } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeImageGenerator.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeImageGenerator.kt index fbb99e45..292eeced 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeImageGenerator.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/util/QrCodeImageGenerator.kt @@ -3,5 +3,8 @@ package co.electriccoin.zcash.ui.screen.receive.util import androidx.compose.ui.graphics.ImageBitmap interface QrCodeImageGenerator { - fun generate(bitArray: BooleanArray, sizePixels: Int): ImageBitmap + fun generate( + bitArray: BooleanArray, + sizePixels: Int + ): ImageBitmap } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt index 8f928209..9d9983b3 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/receive/view/ReceiveView.kt @@ -81,14 +81,15 @@ fun Receive( walletAddress = walletAddress, onAddressDetails = onAddressDetails, adjustBrightness = brightness, - modifier = Modifier - .fillMaxHeight() - .verticalScroll(rememberScrollState()) - .padding( - top = ZcashTheme.dimens.spacingDefault, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .fillMaxHeight() + .verticalScroll(rememberScrollState()) + .padding( + top = ZcashTheme.dimens.spacingDefault, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @@ -109,11 +110,12 @@ private fun ReceiveTopAppBar( onClick = onBrightness ) { Icon( - imageVector = if (adjustBrightness) { - Icons.Default.BrightnessLow - } else { - Icons.Default.BrightnessHigh - }, + imageVector = + if (adjustBrightness) { + Icons.Default.BrightnessLow + } else { + Icons.Default.BrightnessHigh + }, contentDescription = stringResource(R.string.receive_brightness_content_description) ) } @@ -162,9 +164,10 @@ private fun ReceiveContents( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/AndroidRequest.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/AndroidRequest.kt index 37214ac1..534b26dc 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/AndroidRequest.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/AndroidRequest.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.request @@ -16,9 +16,7 @@ import co.electriccoin.zcash.ui.screen.request.view.Request import kotlinx.coroutines.runBlocking @Composable -internal fun MainActivity.WrapRequest( - goBack: () -> Unit -) { +internal fun MainActivity.WrapRequest(goBack: () -> Unit) { WrapRequest(this, goBack) } @@ -37,10 +35,11 @@ private fun WrapRequest( walletAddresses.unified, goBack = goBack, onCreateAndSend = { - val chooserIntent = Intent.createChooser( - it.newShareIntent(activity.applicationContext), - null - ) + val chooserIntent = + Intent.createChooser( + it.newShareIntent(activity.applicationContext), + null + ) activity.startActivity(chooserIntent) @@ -50,10 +49,11 @@ private fun WrapRequest( } } -private fun ZecRequest.newShareIntent(context: Context) = runBlocking { - Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, context.getString(R.string.request_template_format, toUri())) - type = "text/plain" +private fun ZecRequest.newShareIntent(context: Context) = + runBlocking { + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, context.getString(R.string.request_template_format, toUri())) + type = "text/plain" + } } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt index a2061d92..7f73f081 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/request/view/RequestView.kt @@ -73,12 +73,13 @@ fun Request( RequestMainContent( myAddress = myAddress, onCreateAndSend = onCreateAndSend, - modifier = Modifier.padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateTopPadding(), - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateTopPadding(), + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @@ -143,9 +144,10 @@ private fun RequestMainContent( }, label = { Text(stringResource(id = R.string.request_message)) }) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResult.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResult.kt index 8ddd7c52..8286e742 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResult.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/model/ParseResult.kt @@ -26,7 +26,10 @@ internal sealed class ParseResult { companion object { @Suppress("ReturnCount") - fun new(completeWordList: Set, rawInput: String): ParseResult { + fun new( + completeWordList: Set, + rawInput: String + ): ParseResult { // Note: This assumes the word list is English words val trimmed = rawInput.lowercase(Locale.US).trim() @@ -45,9 +48,10 @@ internal sealed class ParseResult { return Autocomplete(autocomplete) } - val multiple = trimmed.split(SeedPhrase.DEFAULT_DELIMITER) - .filter { completeWordList.contains(it) } - .first(SeedPhrase.SEED_PHRASE_SIZE) + val multiple = + trimmed.split(SeedPhrase.DEFAULT_DELIMITER) + .filter { completeWordList.contains(it) } + .first(SeedPhrase.SEED_PHRASE_SIZE) if (multiple.isNotEmpty()) { return Add(multiple) } @@ -61,7 +65,10 @@ internal sealed class ParseResult { } } -internal fun findSuggestions(input: String, completeWordList: Set): List { +internal fun findSuggestions( + input: String, + completeWordList: Set +): List { return if (input.isBlank()) { emptyList() } else { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/RestoreState.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/RestoreState.kt index 39e2ee72..384496ff 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/RestoreState.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/RestoreState.kt @@ -10,7 +10,6 @@ import kotlinx.coroutines.flow.StateFlow * layout preview. The default constructor argument is generally fine for other platforms. */ class RestoreState(initialState: RestoreStage = RestoreStage.values().first()) { - private val mutableState = MutableStateFlow(initialState) val current: StateFlow = mutableState diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/WordList.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/WordList.kt index cb4d8bfb..305e2d6e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/WordList.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/state/WordList.kt @@ -19,19 +19,21 @@ class WordList(initial: List = emptyList()) { } fun append(words: List) { - val newList = (current.value + words) - .first(SeedPhrase.SEED_PHRASE_SIZE) // Prevent pasting too many words - .toPersistentList() + val newList = + (current.value + words) + .first(SeedPhrase.SEED_PHRASE_SIZE) // Prevent pasting too many words + .toPersistentList() mutableState.value = newList } fun removeLast() { - val newList = if (mutableState.value.isNotEmpty()) { - current.value.subList(0, current.value.size - 1) - } else { - current.value - }.toPersistentList() + val newList = + if (mutableState.value.isNotEmpty()) { + current.value.subList(0, current.value.size - 1) + } else { + current.value + }.toPersistentList() mutableState.value = newList } @@ -40,5 +42,6 @@ class WordList(initial: List = emptyList()) { override fun toString() = "WordList" } -fun WordList.wordValidation() = current - .map { SeedPhraseValidation.new(it) } +fun WordList.wordValidation() = + current + .map { SeedPhraseValidation.new(it) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt index e06d694b..ec06fb6d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/view/RestoreView.kt @@ -95,18 +95,19 @@ private fun PreviewRestoreSeed() { RestoreWallet( ZcashNetwork.Mainnet, restoreState = RestoreState(RestoreStage.Seed), - completeWordList = persistentHashSetOf( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "rib", - "ribbon" - ), + completeWordList = + persistentHashSetOf( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "rib", + "ribbon" + ), userWordList = WordList(listOf("abandon", "absorb")), restoreHeight = null, setRestoreHeight = {}, @@ -125,18 +126,19 @@ private fun PreviewRestoreBirthday() { RestoreWallet( ZcashNetwork.Mainnet, restoreState = RestoreState(RestoreStage.Birthday), - completeWordList = persistentHashSetOf( - "abandon", - "ability", - "able", - "about", - "above", - "absent", - "absorb", - "abstract", - "rib", - "ribbon" - ), + completeWordList = + persistentHashSetOf( + "abandon", + "ability", + "able", + "about", + "above", + "absent", + "absorb", + "abstract", + "rib", + "ribbon" + ), userWordList = WordList(listOf("abandon", "absorb")), restoreHeight = null, setRestoreHeight = {}, @@ -215,11 +217,12 @@ fun RestoreWallet( isSeedValid = isSeedValid, parseResult = parseResult, setText = { text = it }, - modifier = Modifier - .imePadding() - .navigationBarsPadding() - .animateContentSize() - .fillMaxWidth() + modifier = + Modifier + .imePadding() + .navigationBarsPadding() + .animateContentSize() + .fillMaxWidth() ) } RestoreStage.Birthday -> { @@ -228,13 +231,14 @@ fun RestoreWallet( } }, content = { paddingValues -> - val commonModifier = Modifier - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + val commonModifier = + Modifier + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) when (currentStage) { RestoreStage.Seed -> { @@ -258,9 +262,10 @@ fun RestoreWallet( initialRestoreHeight = restoreHeight, setRestoreHeight = setRestoreHeight, onDone = onFinished, - modifier = commonModifier - .imePadding() - .navigationBarsPadding() + modifier = + commonModifier + .imePadding() + .navigationBarsPadding() ) } } @@ -277,9 +282,10 @@ private fun ClearSeedMenuItem( text = stringResource(id = R.string.restore_button_clear), onClick = onSeedClear, textAlign = TextAlign.Center, - modifier = modifier.then( - Modifier.padding(all = ZcashTheme.dimens.spacingDefault) - ) + modifier = + modifier.then( + Modifier.padding(all = ZcashTheme.dimens.spacingDefault) + ) ) } @@ -351,10 +357,11 @@ private fun RestoreSeedMainContent( ) { // Used to calculate necessary scroll to have the seed TextFiled visible Column( - modifier = Modifier.onSizeChanged { size -> - textFieldScrollToHeight.intValue = size.height - Twig.debug { "TextField scroll height: ${textFieldScrollToHeight.intValue}" } - } + modifier = + Modifier.onSizeChanged { size -> + textFieldScrollToHeight.intValue = size.height + Twig.debug { "TextField scroll height: ${textFieldScrollToHeight.intValue}" } + } ) { TopScreenLogoTitle( title = stringResource(R.string.restore_title), @@ -380,9 +387,10 @@ private fun RestoreSeedMainContent( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(Modifier.height(ZcashTheme.dimens.spacingLarge)) @@ -434,12 +442,13 @@ private fun RestoreSeedBottomBar( ) { Warn( parseResult = parseResult, - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = ZcashTheme.dimens.spacingDefault, - vertical = ZcashTheme.dimens.spacingSmall - ) + modifier = + Modifier + .fillMaxWidth() + .padding( + horizontal = ZcashTheme.dimens.spacingDefault, + vertical = ZcashTheme.dimens.spacingSmall + ) ) Autocomplete(parseResult = parseResult, { setText("") @@ -461,26 +470,29 @@ private fun SeedGridWithText( ) { val currentUserWordList = userWordList.current.collectAsStateWithLifecycle().value - val currentSeedText = currentUserWordList.run { - if (isEmpty()) { - text - } else { - joinToString(separator = " ", postfix = " ").plus(text) + val currentSeedText = + currentUserWordList.run { + if (isEmpty()) { + text + } else { + joinToString(separator = " ", postfix = " ").plus(text) + } } - } Column( - modifier = Modifier - .border( - border = BorderStroke( - width = ZcashTheme.dimens.layoutStroke, - color = ZcashTheme.colors.layoutStroke + modifier = + Modifier + .border( + border = + BorderStroke( + width = ZcashTheme.dimens.layoutStroke, + color = ZcashTheme.colors.layoutStroke + ) ) - ) - .fillMaxWidth() - .defaultMinSize(minHeight = ZcashTheme.dimens.textFieldDefaultHeight) - .then(modifier) - .testTag(RestoreTag.CHIP_LAYOUT) + .fillMaxWidth() + .defaultMinSize(minHeight = ZcashTheme.dimens.textFieldDefaultHeight) + .then(modifier) + .testTag(RestoreTag.CHIP_LAYOUT) ) { /* * Treat the user input as a password for more secure input, but disable the transformation @@ -488,15 +500,17 @@ private fun SeedGridWithText( */ TextField( textStyle = ZcashTheme.extendedTypography.textFieldValue, - modifier = Modifier - .fillMaxWidth() - .padding(ZcashTheme.dimens.spacingTiny) - .testTag(RestoreTag.SEED_WORD_TEXT_FIELD) - .focusRequester(focusRequester), - value = TextFieldValue( - text = currentSeedText, - selection = TextRange(index = currentSeedText.length) - ), + modifier = + Modifier + .fillMaxWidth() + .padding(ZcashTheme.dimens.spacingTiny) + .testTag(RestoreTag.SEED_WORD_TEXT_FIELD) + .focusRequester(focusRequester), + value = + TextFieldValue( + text = currentSeedText, + selection = TextRange(index = currentSeedText.length) + ), placeholder = { Text( text = stringResource(id = R.string.restore_seed_hint), @@ -512,23 +526,25 @@ private fun SeedGridWithText( setText = setText ) }, - keyboardOptions = KeyboardOptions( - KeyboardCapitalization.None, - autoCorrect = false, - imeAction = ImeAction.Done, - keyboardType = KeyboardType.Password - ), + keyboardOptions = + KeyboardOptions( + KeyboardCapitalization.None, + autoCorrect = false, + imeAction = ImeAction.Done, + keyboardType = KeyboardType.Password + ), keyboardActions = KeyboardActions(onAny = {}), isError = parseResult is ParseResult.Warn, - colors = TextFieldDefaults.colors( - focusedContainerColor = Color.Transparent, - unfocusedContainerColor = Color.Transparent, - disabledContainerColor = Color.Transparent, - errorContainerColor = Color.Transparent, - focusedIndicatorColor = Color.Transparent, - unfocusedIndicatorColor = Color.Transparent, - disabledIndicatorColor = Color.Transparent - ) + colors = + TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + disabledContainerColor = Color.Transparent, + errorContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent + ) ) } } @@ -554,11 +570,12 @@ fun processTextInput( userWordList: WordList, setText: (String) -> Unit ) { - val textDifference = if (updateSeedText.length > currentSeedText.length) { - updateSeedText.substring(currentSeedText.length) - } else { - "" - } + val textDifference = + if (updateSeedText.length > currentSeedText.length) { + updateSeedText.substring(currentSeedText.length) + } else { + "" + } Twig.debug { "Text difference: $textDifference" } if (whiteSpaceRegex.matches(textDifference)) { @@ -588,31 +605,34 @@ private fun Autocomplete( // TODO [#1061]: Restore screen input validation refactoring and adding tests // TODO [#1061]: https://github.com/Electric-Coin-Company/zashi-android/issues/1061 // Note that we currently do not use the highlighting of the suggestion bar - val (isHighlight, suggestions) = when (parseResult) { - is ParseResult.Autocomplete -> { - Pair(false, parseResult.suggestions) + val (isHighlight, suggestions) = + when (parseResult) { + is ParseResult.Autocomplete -> { + Pair(false, parseResult.suggestions) + } + is ParseResult.Warn -> { + return + } + else -> { + Pair(false, null) + } } - is ParseResult.Warn -> { - return - } - else -> { - Pair(false, null) - } - } suggestions?.let { LazyRow( - modifier = modifier - .testTag(RestoreTag.AUTOCOMPLETE_LAYOUT) - .fillMaxWidth(), + modifier = + modifier + .testTag(RestoreTag.AUTOCOMPLETE_LAYOUT) + .fillMaxWidth(), contentPadding = PaddingValues(all = ZcashTheme.dimens.spacingSmall), horizontalArrangement = Arrangement.Absolute.Center ) { items(it) { ChipOnSurface( text = it, - modifier = Modifier - .testTag(RestoreTag.AUTOCOMPLETE_ITEM) - .clickable { onSuggestionSelected(it) } + modifier = + Modifier + .testTag(RestoreTag.AUTOCOMPLETE_ITEM) + .clickable { onSuggestionSelected(it) } ) } } @@ -626,28 +646,32 @@ private fun Warn( ) { if (parseResult is ParseResult.Warn) { Surface( - modifier = modifier.then( - Modifier.border( - border = BorderStroke( - width = ZcashTheme.dimens.chipStroke, - color = ZcashTheme.colors.layoutStroke + modifier = + modifier.then( + Modifier.border( + border = + BorderStroke( + width = ZcashTheme.dimens.chipStroke, + color = ZcashTheme.colors.layoutStroke + ) ) - ) - ), + ), shape = RectangleShape, color = MaterialTheme.colorScheme.secondary, shadowElevation = ZcashTheme.dimens.chipShadowElevation ) { Text( - modifier = Modifier - .fillMaxWidth() - .padding(ZcashTheme.dimens.spacingSmall), + modifier = + Modifier + .fillMaxWidth() + .padding(ZcashTheme.dimens.spacingSmall), textAlign = TextAlign.Center, - text = if (parseResult.suggestions.isEmpty()) { - stringResource(id = R.string.restore_seed_warning_no_suggestions) - } else { - stringResource(id = R.string.restore_seed_warning_suggestions) - } + text = + if (parseResult.suggestions.isEmpty()) { + stringResource(id = R.string.restore_seed_warning_no_suggestions) + } else { + stringResource(id = R.string.restore_seed_warning_suggestions) + } ) } } @@ -665,9 +689,10 @@ private fun RestoreBirthdayMainContent( val scrollState = rememberScrollState() val focusRequester = remember { FocusRequester() } - val (height, setHeight) = rememberSaveable { - mutableStateOf(initialRestoreHeight?.value?.toString() ?: "") - } + val (height, setHeight) = + rememberSaveable { + mutableStateOf(initialRestoreHeight?.value?.toString() ?: "") + } Column( Modifier @@ -691,34 +716,38 @@ private fun RestoreBirthdayMainContent( val filteredHeightString = heightString.filter { it.isDigit() } setHeight(filteredHeightString) }, - modifier = Modifier - .fillMaxWidth() - .padding(ZcashTheme.dimens.spacingTiny) - .focusRequester(focusRequester) - .testTag(RestoreTag.BIRTHDAY_TEXT_FIELD), + modifier = + Modifier + .fillMaxWidth() + .padding(ZcashTheme.dimens.spacingTiny) + .focusRequester(focusRequester) + .testTag(RestoreTag.BIRTHDAY_TEXT_FIELD), textStyle = ZcashTheme.extendedTypography.textFieldBirthday, - keyboardOptions = KeyboardOptions( - KeyboardCapitalization.None, - autoCorrect = false, - imeAction = ImeAction.Done, - keyboardType = KeyboardType.Number - ), + keyboardOptions = + KeyboardOptions( + KeyboardCapitalization.None, + autoCorrect = false, + imeAction = ImeAction.Done, + keyboardType = KeyboardType.Number + ), keyboardActions = KeyboardActions(onAny = {}), withBorder = false, ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) // Empty birthday value is a valid birthday height too, thus run validation only in case of non-empty heights. - val isBirthdayValid = height.isEmpty() || height.toLongOrNull()?.let { - it >= zcashNetwork.saplingActivationHeight.value - } ?: false + val isBirthdayValid = + height.isEmpty() || height.toLongOrNull()?.let { + it >= zcashNetwork.saplingActivationHeight.value + } ?: false val isEmptyBirthday = height.isEmpty() diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/viewmodel/RestoreViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/viewmodel/RestoreViewModel.kt index b4d6c1d8..18e29a70 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/viewmodel/RestoreViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/restore/viewmodel/RestoreViewModel.kt @@ -25,59 +25,66 @@ import kotlinx.coroutines.withContext import java.util.Locale class RestoreViewModel(application: Application, savedStateHandle: SavedStateHandle) : AndroidViewModel(application) { + val restoreState: RestoreState = + run { + val initialValue = + if (savedStateHandle.contains(KEY_STAGE)) { + savedStateHandle.get(KEY_STAGE) + } else { + null + } - val restoreState: RestoreState = run { - val initialValue = if (savedStateHandle.contains(KEY_STAGE)) { - savedStateHandle.get(KEY_STAGE) - } else { - null + if (null == initialValue) { + RestoreState() + } else { + RestoreState(initialValue) + } } - if (null == initialValue) { - RestoreState() - } else { - RestoreState(initialValue) - } - } - /** * The complete word list that the user can choose from; useful for autocomplete */ - // This is a hack to prevent disk IO on the main thread - val completeWordList = flow { - // Using IO context because of https://github.com/Electric-Coin-Company/kotlin-bip39/issues/13 - val completeWordList = withContext(Dispatchers.IO) { - Mnemonics.getCachedWords(Locale.ENGLISH.language) + val completeWordList = + // This is a hack to prevent disk IO on the main thread + flow { + // Using IO context because of https://github.com/Electric-Coin-Company/kotlin-bip39/issues/13 + val completeWordList = + withContext(Dispatchers.IO) { + Mnemonics.getCachedWords(Locale.ENGLISH.language) + } + + emit(CompleteWordSetState.Loaded(completeWordList.toPersistentSet())) + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + CompleteWordSetState.Loading + ) + + val userWordList: WordList = + run { + val initialValue = + if (savedStateHandle.contains(KEY_WORD_LIST)) { + savedStateHandle.get>(KEY_WORD_LIST) + } else { + null + } + + if (null == initialValue) { + WordList() + } else { + WordList(initialValue) + } } - emit(CompleteWordSetState.Loaded(completeWordList.toPersistentSet())) - }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - CompleteWordSetState.Loading - ) - - val userWordList: WordList = run { - val initialValue = if (savedStateHandle.contains(KEY_WORD_LIST)) { - savedStateHandle.get>(KEY_WORD_LIST) - } else { - null + val userBirthdayHeight: MutableStateFlow = + run { + val initialValue: BlockHeight? = + savedStateHandle.get(KEY_BIRTHDAY_HEIGHT)?.let { + BlockHeight.new(ZcashNetwork.fromResources(application), it) + } + MutableStateFlow(initialValue) } - if (null == initialValue) { - WordList() - } else { - WordList(initialValue) - } - } - - val userBirthdayHeight: MutableStateFlow = run { - val initialValue: BlockHeight? = savedStateHandle.get(KEY_BIRTHDAY_HEIGHT)?.let { - BlockHeight.new(ZcashNetwork.fromResources(application), it) - } - MutableStateFlow(initialValue) - } - init { // viewModelScope is constructed with Dispatchers.Main.immediate, so this will // update the save state as soon as a change occurs. @@ -101,5 +108,6 @@ class RestoreViewModel(application: Application, savedStateHandle: SavedStateHan sealed class CompleteWordSetState { object Loading : CompleteWordSetState() + data class Loaded(val list: ImmutableSet) : CompleteWordSetState() } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/QrCodeAnalyzer.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/QrCodeAnalyzer.kt index 13871d6b..e432b498 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/QrCodeAnalyzer.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/QrCodeAnalyzer.kt @@ -15,40 +15,43 @@ import java.nio.ByteBuffer class QrCodeAnalyzer( private val onQrCodeScanned: (String) -> Unit ) : ImageAnalysis.Analyzer { - - private val supportedImageFormats = listOf( - ImageFormat.YUV_420_888, - ImageFormat.YUV_422_888, - ImageFormat.YUV_444_888 - ) + private val supportedImageFormats = + listOf( + ImageFormat.YUV_420_888, + ImageFormat.YUV_422_888, + ImageFormat.YUV_444_888 + ) override fun analyze(image: ImageProxy) { image.use { if (image.format in supportedImageFormats) { val bytes = image.planes.first().buffer.toByteArray() - val source = PlanarYUVLuminanceSource( - bytes, - image.width, - image.height, - 0, - 0, - image.width, - image.height, - false - ) + val source = + PlanarYUVLuminanceSource( + bytes, + image.width, + image.height, + 0, + 0, + image.width, + image.height, + false + ) val binaryBmp = BinaryBitmap(HybridBinarizer(source)) runCatching { - val result = MultiFormatReader().apply { - setHints( - mapOf( - DecodeHintType.POSSIBLE_FORMATS to arrayListOf( - BarcodeFormat.QR_CODE + val result = + MultiFormatReader().apply { + setHints( + mapOf( + DecodeHintType.POSSIBLE_FORMATS to + arrayListOf( + BarcodeFormat.QR_CODE + ) ) ) - ) - }.decode(binaryBmp) + }.decode(binaryBmp) onQrCodeScanned(result.text) }.onFailure { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtil.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtil.kt index 149c7209..e282c186 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtil.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/util/SettingsUtil.kt @@ -5,12 +5,12 @@ import android.net.Uri import android.provider.Settings object SettingsUtil { - internal const val SETTINGS_URI_PREFIX = "package:" - internal const val FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_NO_HISTORY or - Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + internal const val FLAGS = + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_NO_HISTORY or + Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS /** * Returns an intent to the system Settings page of the app given by packageName parameter. @@ -19,9 +19,7 @@ object SettingsUtil { * * @return Intent for launching the system Settings app */ - internal fun newSettingsIntent( - packageName: String - ): Intent { + internal fun newSettingsIntent(packageName: String): Intent { return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { addCategory(Intent.CATEGORY_DEFAULT) data = Uri.parse("$SETTINGS_URI_PREFIX$packageName") diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt index 4eaa9d06..1ed72441 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/scan/view/ScanView.kt @@ -113,15 +113,16 @@ fun Scan( onBack, onScanStateChanged, snackbarHostState, - modifier = Modifier - .fillMaxSize() - .background(Color.Black) - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.spacingNone, - end = ZcashTheme.dimens.spacingNone - ) + modifier = + Modifier + .fillMaxSize() + .background(Color.Black) + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.spacingNone, + end = ZcashTheme.dimens.spacingNone + ) ) } } @@ -141,11 +142,12 @@ fun ScanBottomItems( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) Small( - text = when (scanState) { - ScanState.Permission -> stringResource(id = R.string.scan_state_permission) - ScanState.Scanning -> stringResource(id = R.string.scan_state_scanning) - ScanState.Failed -> stringResource(id = R.string.scan_state_failed) - }, + text = + when (scanState) { + ScanState.Permission -> stringResource(id = R.string.scan_state_permission) + ScanState.Scanning -> stringResource(id = R.string.scan_state_scanning) + ScanState.Failed -> stringResource(id = R.string.scan_state_failed) + }, color = Color.White, modifier = Modifier.testTag(ScanTag.TEXT_STATE) ) @@ -192,19 +194,21 @@ private fun ScanMainContent( ) { val context = LocalContext.current - val permissionState = rememberPermissionState( - Manifest.permission.CAMERA - ) - - val (scanState, setScanState) = rememberSaveable { - mutableStateOf( - if (permissionState.status.isGranted) { - ScanState.Scanning - } else { - ScanState.Permission - } + val permissionState = + rememberPermissionState( + Manifest.permission.CAMERA ) - } + + val (scanState, setScanState) = + rememberSaveable { + mutableStateOf( + if (permissionState.status.isGranted) { + ScanState.Scanning + } else { + ScanState.Permission + } + ) + } if (!permissionState.status.isGranted) { setScanState(ScanState.Permission) @@ -228,11 +232,12 @@ private fun ScanMainContent( val framePossibleSize = remember { mutableStateOf(IntSize.Zero) } val configuration = LocalConfiguration.current - val frameActualSize = if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { - (framePossibleSize.value.height * 0.85).roundToInt() - } else { - (framePossibleSize.value.width * 0.7).roundToInt() - } + val frameActualSize = + if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { + (framePossibleSize.value.height * 0.85).roundToInt() + } else { + (framePossibleSize.value.width * 0.7).roundToInt() + } ConstraintLayout(modifier) { val (frame, bottomItems) = createRefs() @@ -253,18 +258,19 @@ private fun ScanMainContent( ) Box( - modifier = Modifier - .constrainAs(frame) { - top.linkTo(parent.top) - bottom.linkTo(bottomItems.top) - start.linkTo(parent.start) - end.linkTo(parent.end) - width = Dimension.fillToConstraints - height = Dimension.fillToConstraints - } - .onSizeChanged { coordinates -> - framePossibleSize.value = coordinates - }, + modifier = + Modifier + .constrainAs(frame) { + top.linkTo(parent.top) + bottom.linkTo(bottomItems.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + height = Dimension.fillToConstraints + } + .onSizeChanged { coordinates -> + framePossibleSize.value = coordinates + }, contentAlignment = Alignment.Center ) { ScanFrame(frameActualSize) @@ -275,10 +281,11 @@ private fun ScanMainContent( LaunchedEffect(key1 = true) { setScanState(ScanState.Failed) onScanStateChanged(ScanState.Failed) - val snackbarResult = snackbarHostState.showSnackbar( - message = context.getString(R.string.scan_setup_failed), - actionLabel = context.getString(R.string.scan_setup_back) - ) + val snackbarResult = + snackbarHostState.showSnackbar( + message = context.getString(R.string.scan_setup_failed), + actionLabel = context.getString(R.string.scan_setup_back) + ) if (snackbarResult == SnackbarResult.ActionPerformed) { onBack() } @@ -290,12 +297,13 @@ private fun ScanMainContent( ScanBottomItems( scanState = scanState, onOpenSettings = onOpenSettings, - modifier = Modifier - .fillMaxWidth() - .padding( - vertical = ZcashTheme.dimens.spacingHuge, - horizontal = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .fillMaxWidth() + .padding( + vertical = ZcashTheme.dimens.spacingHuge, + horizontal = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @@ -305,11 +313,12 @@ private fun ScanMainContent( @Composable fun ScanFrame(frameSize: Int) { Box( - modifier = Modifier - .size(with(LocalDensity.current) { frameSize.toDp() }) - .background(Color.Transparent) - .border(BorderStroke(10.dp, Color.White), RoundedCornerShape(10)) - .testTag(ScanTag.QR_FRAME) + modifier = + Modifier + .size(with(LocalDensity.current) { frameSize.toDp() }) + .background(Color.Transparent) + .border(BorderStroke(10.dp, Color.White), RoundedCornerShape(10)) + .testTag(ScanTag.QR_FRAME) ) } @@ -326,13 +335,14 @@ fun ScanCameraView( // we check the permission first, as the ProcessCameraProvider's emit won't be called again after // recomposition with the permission granted - val cameraProviderFlow = if (permissionState.status.isGranted) { - remember { - flow { emit(ProcessCameraProvider.getInstance(context).await()) } + val cameraProviderFlow = + if (permissionState.status.isGranted) { + remember { + flow { emit(ProcessCameraProvider.getInstance(context).await()) } + } + } else { + null } - } else { - null - } val collectedCameraProvider = cameraProviderFlow?.collectAsState(initial = null)?.value @@ -341,26 +351,31 @@ fun ScanCameraView( } else { val contentDescription = stringResource(id = R.string.scan_preview_content_description) - val imageAnalysis = ImageAnalysis.Builder() - .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) - .build() + val imageAnalysis = + ImageAnalysis.Builder() + .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) + .build() AndroidView( factory = { factoryContext -> - val previewView = PreviewView(factoryContext).apply { - this.scaleType = PreviewView.ScaleType.FILL_CENTER - layoutParams = ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - ) - } + val previewView = + PreviewView(factoryContext).apply { + this.scaleType = PreviewView.ScaleType.FILL_CENTER + layoutParams = + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } previewView.contentDescription = contentDescription - val selector = CameraSelector.Builder() - .requireLensFacing(CameraSelector.LENS_FACING_BACK) - .build() - val preview = androidx.camera.core.Preview.Builder().build().apply { - setSurfaceProvider(previewView.surfaceProvider) - } + val selector = + CameraSelector.Builder() + .requireLensFacing(CameraSelector.LENS_FACING_BACK) + .build() + val preview = + androidx.camera.core.Preview.Builder().build().apply { + setSurfaceProvider(previewView.surfaceProvider) + } runCatching { // we must unbind the use-cases before rebinding them @@ -392,20 +407,21 @@ fun ScanCameraView( // Using callbackFlow because QrCodeAnalyzer has a non-suspending callback which makes // a basic flow builder not work here. @Composable -fun ImageAnalysis.qrCodeFlow(context: Context): Flow = remember { - callbackFlow { - setAnalyzer( - ContextCompat.getMainExecutor(context), - QrCodeAnalyzer { result -> - // Note that these callbacks aren't tied to the Compose lifecycle, so they could occur - // after the view goes away. Collection needs to occur within the Compose lifecycle - // to make this not be a problem. - trySend(result) - } - ) +fun ImageAnalysis.qrCodeFlow(context: Context): Flow = + remember { + callbackFlow { + setAnalyzer( + ContextCompat.getMainExecutor(context), + QrCodeAnalyzer { result -> + // Note that these callbacks aren't tied to the Compose lifecycle, so they could occur + // after the view goes away. Collection needs to occur within the Compose lifecycle + // to make this not be a problem. + trySend(result) + } + ) - awaitClose { - // Nothing to close + awaitClose { + // Nothing to close + } } } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtil.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtil.kt index 4b07df58..19ce4e17 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtil.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/util/WebBrowserUtil.kt @@ -4,10 +4,10 @@ import android.content.Intent import android.net.Uri object WebBrowserUtil { - - const val FLAGS = Intent.FLAG_ACTIVITY_NO_HISTORY or - Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_MULTIPLE_TASK + const val FLAGS = + Intent.FLAG_ACTIVITY_NO_HISTORY or + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_MULTIPLE_TASK const val ZCASH_PRIVACY_POLICY_URI = "https://z.cash/privacy-policy/" // NON-NLS diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningView.kt index caae750f..aaa8d3d8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/securitywarning/view/SecurityWarningView.kt @@ -76,23 +76,22 @@ fun SecurityWarning( onPrivacyPolicy = onPrivacyPolicy, onAcknowledged = onAcknowledged, onConfirm = onConfirm, - modifier = Modifier - .fillMaxSize() - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) - .verticalScroll(rememberScrollState()) + modifier = + Modifier + .fillMaxSize() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) + .verticalScroll(rememberScrollState()) ) } } @Composable -private fun SecurityWarningTopAppBar( - onBack: () -> Unit, -) { +private fun SecurityWarningTopAppBar(onBack: () -> Unit) { SmallTopAppBar( backText = stringResource(R.string.security_warning_back).uppercase(), backContentDescriptionText = stringResource(R.string.security_warning_back_content_description), @@ -128,9 +127,10 @@ private fun SecurityWarningContent( val checkedState = rememberSaveable { mutableStateOf(false) } CheckBox( - modifier = Modifier - .align(Alignment.Start) - .fillMaxWidth(), + modifier = + Modifier + .align(Alignment.Start) + .fillMaxWidth(), checked = checkedState.value, onCheckedChange = { checkedState.value = it @@ -141,9 +141,10 @@ private fun SecurityWarningContent( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) PrimaryButton( @@ -164,21 +165,23 @@ fun SecurityWarningContentText( val textPart1 = stringResource(R.string.security_warning_text_part_1, versionInfo.versionName) val textPart2 = stringResource(R.string.security_warning_text_part_2) ClickableText( - text = buildAnnotatedString { - append(textPart1) - withStyle(SpanStyle(textDecoration = TextDecoration.Underline)) { - append(textPart2) - } - append(stringResource(R.string.security_warning_text_part_3)) - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - append(stringResource(R.string.security_warning_text_part_4)) - } - append(stringResource(R.string.security_warning_text_part_5)) - }, + text = + buildAnnotatedString { + append(textPart1) + withStyle(SpanStyle(textDecoration = TextDecoration.Underline)) { + append(textPart2) + } + append(stringResource(R.string.security_warning_text_part_3)) + withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { + append(stringResource(R.string.security_warning_text_part_4)) + } + append(stringResource(R.string.security_warning_text_part_5)) + }, style = ZcashTheme.extendedTypography.securityWarningText, - modifier = Modifier - .fillMaxWidth() - .testTag(SecurityScreenTag.WARNING_TEXT_TAG), + modifier = + Modifier + .fillMaxWidth() + .testTag(SecurityScreenTag.WARNING_TEXT_TAG), onClick = { letterOffset -> // Call the callback only if user clicked the underlined part if (letterOffset >= textPart1.length && letterOffset <= (textPart1.length + textPart2.length)) { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/AndroidSeedRecovery.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/AndroidSeedRecovery.kt index 1238dfaa..587933f0 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/AndroidSeedRecovery.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/AndroidSeedRecovery.kt @@ -27,14 +27,15 @@ private fun WrapSeedRecovery( ) { val walletViewModel by activity.viewModels() - val persistableWallet = run { - val secretState = walletViewModel.secretState.collectAsStateWithLifecycle().value - if (secretState is SecretState.Ready) { - secretState.persistableWallet - } else { - null + val persistableWallet = + run { + val secretState = walletViewModel.secretState.collectAsStateWithLifecycle().value + if (secretState is SecretState.Ready) { + secretState.persistableWallet + } else { + null + } } - } val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value if (null == synchronizer || null == persistableWallet) { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryView.kt index 8d50441b..09dbaf76 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/seedrecovery/view/SeedRecoveryView.kt @@ -87,10 +87,11 @@ fun SeedRecovery( onBirthdayCopy = onBirthdayCopy, // Horizontal paddings will be part of each UI element to minimize a possible truncation on very // small screens - modifier = Modifier.padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding() - ) + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding() + ) ) } } @@ -123,9 +124,10 @@ private fun SeedRecoveryCopyToBufferMenuItem( text = stringResource(id = R.string.seed_recovery_copy), onClick = onCopyToClipboard, textAlign = TextAlign.Center, - modifier = modifier.then( - Modifier.padding(all = ZcashTheme.dimens.spacingDefault) - ) + modifier = + modifier.then( + Modifier.padding(all = ZcashTheme.dimens.spacingDefault) + ) ) } @@ -167,9 +169,10 @@ private fun SeedRecoveryMainContent( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) @@ -177,12 +180,13 @@ private fun SeedRecoveryMainContent( PrimaryButton( onClick = onDone, text = stringResource(R.string.seed_recovery_button_finished), - modifier = Modifier - .padding( - bottom = ZcashTheme.dimens.spacingHuge, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .padding( + bottom = ZcashTheme.dimens.spacingHuge, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } } @@ -215,16 +219,18 @@ private fun SeedRecoverySeedPhrase( ) { BodySmall( text = stringResource(R.string.seed_recovery_birthday_height, it.value), - modifier = Modifier - .testTag(WALLET_BIRTHDAY) - .padding(horizontal = ZcashTheme.dimens.spacingDefault) - .basicMarquee() - // Apply click callback to the text only as the wrapping layout can be much wider - .clickable( - interactionSource = interactionSource, - indication = null, // Disable ripple - onClick = onBirthdayCopy - ) + modifier = + Modifier + .testTag(WALLET_BIRTHDAY) + .padding(horizontal = ZcashTheme.dimens.spacingDefault) + .basicMarquee() + // Apply click callback to the text only as the wrapping layout can be much wider + .clickable( + interactionSource = interactionSource, + // Disable ripple + indication = null, + onClick = onBirthdayCopy + ) ) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt index 6a09e3b8..c04b314b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/AndroidSend.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.send diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExt.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExt.kt index 18346518..c60a7e27 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExt.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/ext/ZecSendExt.kt @@ -14,26 +14,28 @@ private const val KEY_MEMO = "memo" // $NON-NLS // Using a custom saver instead of Parcelize, to avoid adding an Android-specific API to // the ZecSend class internal val ZecSend.Companion.Saver - get() = run { - mapSaver( - save = { - it?.toSaverMap() ?: emptyMap() - }, - restore = { - if (it.isEmpty()) { - null - } else { - val address = runBlocking { WalletAddress.Unified.new(it[KEY_ADDRESS] as String) } - val amount = Zatoshi(it[KEY_AMOUNT] as Long) - val memo = Memo(it[KEY_MEMO] as String) - ZecSend(address, amount, memo) + get() = + run { + mapSaver( + save = { + it?.toSaverMap() ?: emptyMap() + }, + restore = { + if (it.isEmpty()) { + null + } else { + val address = runBlocking { WalletAddress.Unified.new(it[KEY_ADDRESS] as String) } + val amount = Zatoshi(it[KEY_AMOUNT] as Long) + val memo = Memo(it[KEY_MEMO] as String) + ZecSend(address, amount, memo) + } } - } - ) - } + ) + } -private fun ZecSend.toSaverMap() = buildMap { - put(KEY_ADDRESS, destination.address) - put(KEY_AMOUNT, amount.value) - put(KEY_MEMO, memo.value) -} +private fun ZecSend.toSaverMap() = + buildMap { + put(KEY_ADDRESS, destination.address) + put(KEY_AMOUNT, amount.value) + put(KEY_MEMO, memo.value) + } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt index 1149927a..1461a4c8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt @@ -88,11 +88,12 @@ private fun PreviewSendSuccessful() { ZcashTheme(forceDarkMode = false) { GradientSurface { SendSuccessful( - zecSend = ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new() - ), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new() + ), onDone = {} ) } @@ -105,11 +106,12 @@ private fun PreviewSendFailure() { ZcashTheme(forceDarkMode = false) { GradientSurface { SendFailure( - zecSend = ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new() - ), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new() + ), onDone = {} ) } @@ -122,11 +124,12 @@ private fun PreviewSendConfirmation() { ZcashTheme(forceDarkMode = false) { GradientSurface { Confirmation( - zecSend = ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new() - ), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new() + ), onConfirmation = {} ) } @@ -164,17 +167,18 @@ fun Send( onSendSubmit = onCreateAndSend, onQrScannerOpen = onQrScannerOpen, hasCameraFeature = hasCameraFeature, - modifier = Modifier - .fillMaxHeight() - .verticalScroll( - rememberScrollState() - ) - .padding( - top = paddingValues.calculateTopPadding() + dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding() + dimens.spacingHuge, - start = dimens.screenHorizontalSpacing, - end = dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .fillMaxHeight() + .verticalScroll( + rememberScrollState() + ) + .padding( + top = paddingValues.calculateTopPadding() + dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding() + dimens.spacingHuge, + start = dimens.screenHorizontalSpacing, + end = dimens.screenHorizontalSpacing + ) ) } } @@ -333,17 +337,22 @@ private fun SendForm( onValueChange = { recipientAddressString = it }, label = { Text(stringResource(id = R.string.send_to)) }, modifier = Modifier.fillMaxWidth(), - trailingIcon = if (hasCameraFeature) { { - IconButton( - onClick = onQrScannerOpen, - content = { - Icon( - imageVector = Icons.Outlined.QrCodeScanner, - contentDescription = stringResource(R.string.send_scan_content_description) + trailingIcon = + if (hasCameraFeature) { + { + IconButton( + onClick = onQrScannerOpen, + content = { + Icon( + imageVector = Icons.Outlined.QrCodeScanner, + contentDescription = stringResource(R.string.send_scan_content_description) + ) + } ) } - ) - } } else { null } + } else { + null + } ) Spacer(Modifier.size(dimens.spacingSmall)) @@ -377,9 +386,10 @@ private fun SendForm( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) if (validation.isNotEmpty()) { @@ -398,13 +408,14 @@ private fun SendForm( PrimaryButton( onClick = { - val zecSendValidation = ZecSendExt.new( - context, - recipientAddressString, - amountZecString, - memoString, - monetarySeparators - ) + val zecSendValidation = + ZecSendExt.new( + context, + recipientAddressString, + amountZecString, + memoString, + monetarySeparators + ) when (zecSendValidation) { is ZecSendExt.ZecSendValidation.Valid -> onCreateZecSend(zecSendValidation.zecSend) @@ -447,9 +458,10 @@ private fun Confirmation( } Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) PrimaryButton( @@ -470,10 +482,11 @@ private fun Sending( ) { Column(modifier) { Header( - text = stringResource( - R.string.send_in_progress_amount_format, - zecSend.amount.toZecString() - ), + text = + stringResource( + R.string.send_in_progress_amount_format, + zecSend.amount.toZecString() + ), textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth() ) @@ -496,15 +509,17 @@ private fun Sending( } Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Body( - modifier = Modifier - .padding(vertical = dimens.spacingSmall) - .fillMaxWidth(), + modifier = + Modifier + .padding(vertical = dimens.spacingSmall) + .fillMaxWidth(), text = stringResource(R.string.send_in_progress_wait), textAlign = TextAlign.Center, ) @@ -528,9 +543,10 @@ private fun SendSuccessful( ) Spacer( - modifier = Modifier - .fillMaxWidth() - .height(dimens.spacingDefault) + modifier = + Modifier + .fillMaxWidth() + .height(dimens.spacingDefault) ) Body( @@ -543,9 +559,10 @@ private fun SendSuccessful( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) PrimaryButton( @@ -576,9 +593,10 @@ private fun SendFailure( ) Spacer( - modifier = Modifier - .fillMaxWidth() - .height(dimens.spacingDefault) + modifier = + Modifier + .fillMaxWidth() + .height(dimens.spacingDefault) ) Body( @@ -591,9 +609,10 @@ private fun SendFailure( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) PrimaryButton( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/AndroidSettings.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/AndroidSettings.kt index 8fee21e6..55f3bbe6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/AndroidSettings.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/AndroidSettings.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.settings diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt index eb60c24b..15ef2e7e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/view/SettingsView.kt @@ -94,16 +94,17 @@ fun Settings( ) }) { paddingValues -> SettingsMainContent( - modifier = Modifier - .verticalScroll( - rememberScrollState() - ) - .padding( - top = paddingValues.calculateTopPadding() + dimens.spacingHuge, - bottom = paddingValues.calculateBottomPadding(), - start = dimens.screenHorizontalSpacing, - end = dimens.screenHorizontalSpacing - ), + modifier = + Modifier + .verticalScroll( + rememberScrollState() + ) + .padding( + top = paddingValues.calculateTopPadding() + dimens.spacingHuge, + bottom = paddingValues.calculateBottomPadding(), + start = dimens.screenHorizontalSpacing, + end = dimens.screenHorizontalSpacing + ), onSeedRecovery = onSeedRecovery, onDocumentation = onDocumentation, onPrivacyPolicy = onPrivacyPolicy, @@ -277,9 +278,10 @@ private fun SettingsMainContent( ) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(dimens.spacingDefault)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/viewmodel/SettingsViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/viewmodel/SettingsViewModel.kt index 71760762..a29a83d7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/viewmodel/SettingsViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/settings/viewmodel/SettingsViewModel.kt @@ -27,10 +27,11 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application val isKeepScreenOnWhileSyncing: StateFlow = booleanStateFlow(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC) - private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow = flow { - val preferenceProvider = StandardPreferenceSingleton.getInstance(getApplication()) - emitAll(default.observe(preferenceProvider)) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null) + private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow = + flow { + val preferenceProvider = StandardPreferenceSingleton.getInstance(getApplication()) + emitAll(default.observe(preferenceProvider)) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null) fun setAnalyticsEnabled(enabled: Boolean) { setBooleanPreference(StandardPreferenceKeys.IS_ANALYTICS_ENABLED, enabled) @@ -44,7 +45,10 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application setBooleanPreference(StandardPreferenceKeys.IS_KEEP_SCREEN_ON_DURING_SYNC, enabled) } - private fun setBooleanPreference(default: BooleanPreferenceDefault, newState: Boolean) { + private fun setBooleanPreference( + default: BooleanPreferenceDefault, + newState: Boolean + ) { viewModelScope.launch { val prefs = StandardPreferenceSingleton.getInstance(getApplication()) mutex.withLock { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/AndroidSupport.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/AndroidSupport.kt index 73c9b979..8dcdfdf9 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/AndroidSupport.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/AndroidSupport.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.support @@ -20,9 +20,7 @@ import co.electriccoin.zcash.ui.screen.support.viewmodel.SupportViewModel import kotlinx.coroutines.launch @Composable -internal fun MainActivity.WrapSupport( - goBack: () -> Unit -) { +internal fun MainActivity.WrapSupport(goBack: () -> Unit) { WrapSupport(this, goBack) } @@ -42,13 +40,14 @@ internal fun WrapSupport( onSend = { userMessage -> val fullMessage = formatMessage(userMessage, supportMessage) - val mailIntent = EmailUtil.newMailActivityIntent( - activity.getString(R.string.support_email_address), - activity.getString(R.string.app_name), - fullMessage - ).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK - } + val mailIntent = + EmailUtil.newMailActivityIntent( + activity.getString(R.string.support_email_address), + activity.getString(R.string.app_name), + fullMessage + ).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + } // TODO [#386]: This should only fail if there's no email app, e.g. on a TV device // TODO [#386]: https://github.com/Electric-Coin-Company/zashi-android/issues/386 diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/AppInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/AppInfo.kt index cdb6c367..41ef5341 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/AppInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/AppInfo.kt @@ -5,16 +5,18 @@ import co.electriccoin.zcash.build.gitSha import co.electriccoin.zcash.spackle.versionCodeCompat data class AppInfo(val versionName: String, val versionCode: Long, val gitSha: String) { - - fun toSupportString() = buildString { - appendLine("App version: $versionName ($versionCode) $gitSha") - } + fun toSupportString() = + buildString { + appendLine("App version: $versionName ($versionCode) $gitSha") + } companion object { - fun new(packageInfo: PackageInfo) = AppInfo( - packageInfo.versionName ?: "null", // Should only be null during tests - packageInfo.versionCodeCompat, - gitSha - ) + fun new(packageInfo: PackageInfo) = + AppInfo( + // Should only be null during tests + packageInfo.versionName ?: "null", + packageInfo.versionCodeCompat, + gitSha + ) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/ConfigInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/ConfigInfo.kt index 8b1a839f..2b3bd7f6 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/ConfigInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/ConfigInfo.kt @@ -4,14 +4,15 @@ import co.electriccoin.zcash.configuration.api.ConfigurationProvider import kotlinx.datetime.Instant data class ConfigInfo(val configurationUpdatedAt: Instant?) { - - fun toSupportString() = buildString { - append("Configuration: $configurationUpdatedAt") - } + fun toSupportString() = + buildString { + append("Configuration: $configurationUpdatedAt") + } companion object { - fun new(configurationProvider: ConfigurationProvider) = ConfigInfo( - configurationProvider.peekConfiguration().updatedAt - ) + fun new(configurationProvider: ConfigurationProvider) = + ConfigInfo( + configurationProvider.peekConfiguration().updatedAt + ) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt index 87e35a9a..f97f90c7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/CrashInfo.kt @@ -10,25 +10,27 @@ import kotlinx.datetime.Instant import java.io.File data class CrashInfo(val exceptionClassName: String, val isUncaught: Boolean, val timestamp: Instant) { - fun toSupportString() = buildString { - appendLine("Exception") - appendLine(" Class name: $exceptionClassName") - appendLine(" Is uncaught: $isUncaught") - appendLine(" Timestamp: $timestamp") + fun toSupportString() = + buildString { + appendLine("Exception") + appendLine(" Class name: $exceptionClassName") + appendLine(" Is uncaught: $isUncaught") + appendLine(" Timestamp: $timestamp") - // For now, don't include the stacktrace. It'll be too long for the emails we want to generate - } + // For now, don't include the stacktrace. It'll be too long for the emails we want to generate + } companion object } -fun List.toCrashSupportString() = buildString { - // Using the header "Exceptions" instead of "Crashes" to reduce risk of alarming users - appendLine("Exceptions:") - this@toCrashSupportString.forEach { - appendLine(it.toSupportString()) +fun List.toCrashSupportString() = + buildString { + // Using the header "Exceptions" instead of "Crashes" to reduce risk of alarming users + appendLine("Exceptions:") + this@toCrashSupportString.forEach { + appendLine(it.toSupportString()) + } } -} // If you change this, be sure to update the test case under /docs/testing/manual_testing/Contact Support.md private const val MAX_EXCEPTIONS_TO_REPORT = 5 diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/DeviceInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/DeviceInfo.kt index dc9fa3e2..debf0c89 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/DeviceInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/DeviceInfo.kt @@ -3,10 +3,10 @@ package co.electriccoin.zcash.ui.screen.support.model import android.os.Build data class DeviceInfo(val manufacturer: String, val device: String, val model: String) { - - fun toSupportString() = buildString { - appendLine("Device: $manufacturer $device $model") - } + fun toSupportString() = + buildString { + appendLine("Device: $manufacturer $device $model") + } companion object { fun new() = DeviceInfo(Build.MANUFACTURER, Build.DEVICE, Build.MODEL) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt index 32b65a0e..120fa34c 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt @@ -10,13 +10,13 @@ data class EnvironmentInfo( val monetarySeparators: MonetarySeparators, val usableStorageMegabytes: Int ) { - - fun toSupportString() = buildString { - appendLine("Locale: ${locale.androidResName()}") - appendLine("Currency grouping separator: ${monetarySeparators.grouping}") - appendLine("Currency decimal separator: ${monetarySeparators.decimal}") - appendLine("Usable storage: $usableStorageMegabytes MB") - } + fun toSupportString() = + buildString { + appendLine("Locale: ${locale.androidResName()}") + appendLine("Currency grouping separator: ${monetarySeparators.grouping}") + appendLine("Currency decimal separator: ${monetarySeparators.decimal}") + appendLine("Usable storage: $usableStorageMegabytes MB") + } companion object { suspend fun new(context: Context): EnvironmentInfo { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/OperatingSystemInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/OperatingSystemInfo.kt index 70f14635..6a0d28eb 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/OperatingSystemInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/OperatingSystemInfo.kt @@ -4,14 +4,14 @@ import android.os.Build import co.electriccoin.zcash.spackle.AndroidApiVersion data class OperatingSystemInfo(val sdkInt: Int, val isPreview: Boolean) { - - fun toSupportString() = buildString { - if (isPreview) { - appendLine("Android API: $sdkInt (preview)") - } else { - appendLine("Android API: $sdkInt") + fun toSupportString() = + buildString { + if (isPreview) { + appendLine("Android API: $sdkInt (preview)") + } else { + appendLine("Android API: $sdkInt") + } } - } companion object { fun new() = OperatingSystemInfo(Build.VERSION.SDK_INT, AndroidApiVersion.isPreview) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/PermissionInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/PermissionInfo.kt index b044df22..3a11838d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/PermissionInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/PermissionInfo.kt @@ -7,21 +7,27 @@ import android.content.pm.PackageManager import co.electriccoin.zcash.spackle.getPackageInfoCompatSuspend data class PermissionInfo(val permissionName: String, val permissionStatus: PermissionStatus) { - fun toSupportString() = buildString { - appendLine("$permissionName $permissionStatus") - } + fun toSupportString() = + buildString { + appendLine("$permissionName $permissionStatus") + } companion object { private val permissionsOfInterest = listOf(Manifest.permission.CAMERA) suspend fun all(context: Context): List { - val myPackageInfo: PackageInfo = context.packageManager - .getPackageInfoCompatSuspend(context.packageName, PackageManager.GET_PERMISSIONS.toLong()) + val myPackageInfo: PackageInfo = + context.packageManager + .getPackageInfoCompatSuspend(context.packageName, PackageManager.GET_PERMISSIONS.toLong()) return permissionsOfInterest.map { new(context, myPackageInfo, it) } } - private fun new(context: Context, packageInfo: PackageInfo, permissionName: String): PermissionInfo { + private fun new( + context: Context, + packageInfo: PackageInfo, + permissionName: String + ): PermissionInfo { return if (isPermissionGrantedByUser(context, permissionName)) { PermissionInfo(permissionName, PermissionStatus.Granted) } else if (isPermissionGrantedByManifest(packageInfo, permissionName)) { @@ -31,26 +37,33 @@ data class PermissionInfo(val permissionName: String, val permissionStatus: Perm } } - private fun isPermissionGrantedByUser(context: Context, permissionName: String): Boolean { + private fun isPermissionGrantedByUser( + context: Context, + permissionName: String + ): Boolean { // Note: this is only checking very basic permissions // Some permissions, such as REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, require different checks return PackageManager.PERMISSION_GRANTED == context.checkSelfPermission(permissionName) } - private fun isPermissionGrantedByManifest(packageInfo: PackageInfo, permissionName: String): Boolean { + private fun isPermissionGrantedByManifest( + packageInfo: PackageInfo, + permissionName: String + ): Boolean { return packageInfo.requestedPermissions?.any { permissionName == it } ?: false } } } -fun List.toPermissionSupportString() = buildString { - if (this@toPermissionSupportString.isNotEmpty()) { - appendLine("Permissions:") - this@toPermissionSupportString.forEach { - appendLine(it.toSupportString()) +fun List.toPermissionSupportString() = + buildString { + if (this@toPermissionSupportString.isNotEmpty()) { + appendLine("Permissions:") + this@toPermissionSupportString.forEach { + appendLine(it.toSupportString()) + } } } -} enum class PermissionStatus { NotGrantedByManifest, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfo.kt index ac92e543..ce3c0c52 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/SupportInfo.kt @@ -26,39 +26,39 @@ data class SupportInfo( val permissionInfo: PersistentList, val crashInfo: PersistentList ) { - // The set of enum values is to allow optional filtering of different types of information // by users in the future. This would mostly be useful for using a web service request to post // instead of email (where users can edit the auto generated message) - fun toSupportString(set: Set) = buildString { - if (set.contains(SupportInfoType.Time)) { - append(timeInfo.toSupportString()) - } + fun toSupportString(set: Set) = + buildString { + if (set.contains(SupportInfoType.Time)) { + append(timeInfo.toSupportString()) + } - if (set.contains(SupportInfoType.App)) { - append(appInfo.toSupportString()) - } + if (set.contains(SupportInfoType.App)) { + append(appInfo.toSupportString()) + } - if (set.contains(SupportInfoType.Os)) { - append(operatingSystemInfo.toSupportString()) - } + if (set.contains(SupportInfoType.Os)) { + append(operatingSystemInfo.toSupportString()) + } - if (set.contains(SupportInfoType.Device)) { - append(deviceInfo.toSupportString()) - } + if (set.contains(SupportInfoType.Device)) { + append(deviceInfo.toSupportString()) + } - if (set.contains(SupportInfoType.Environment)) { - append(environmentInfo.toSupportString()) - } + if (set.contains(SupportInfoType.Environment)) { + append(environmentInfo.toSupportString()) + } - if (set.contains(SupportInfoType.Permission)) { - append(permissionInfo.toPermissionSupportString()) - } + if (set.contains(SupportInfoType.Permission)) { + append(permissionInfo.toPermissionSupportString()) + } - if (set.contains(SupportInfoType.Crash)) { - append(crashInfo.toCrashSupportString()) + if (set.contains(SupportInfoType.Crash)) { + append(crashInfo.toCrashSupportString()) + } } - } companion object { // Although most of our calls now are non-blocking, we expect more of them to be blocking diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/TimeInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/TimeInfo.kt index 03323f28..993e66f4 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/TimeInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/TimeInfo.kt @@ -15,18 +15,19 @@ data class TimeInfo( val installTime: Instant, val updateTime: Instant ) { - // TODO [#388]: Consider fuzzing the times // TODO [#388]: https://github.com/Electric-Coin-Company/zashi-android/issues/388 - fun toSupportString() = buildString { - // Use a slightly more human friendly format instead of ISO, since this will appear in the emails that users see - val dateFormat = SimpleDateFormat("yyyy-MM-dd hh:mm:ss a", Locale.US) // $NON-NLS-1$ + fun toSupportString() = + buildString { + // Use a slightly more human friendly format instead of ISO, since this will appear in the emails that + // users see + val dateFormat = SimpleDateFormat("yyyy-MM-dd hh:mm:ss a", Locale.US) // $NON-NLS-1$ - appendLine("Current time: ${dateFormat.formatInstant(currentTime)}") - appendLine("Reboot time: ${dateFormat.formatInstant(rebootTime)}") - appendLine("Install time: ${dateFormat.formatInstant(installTime)}") - appendLine("Update time: ${dateFormat.formatInstant(updateTime)}") - } + appendLine("Current time: ${dateFormat.formatInstant(currentTime)}") + appendLine("Reboot time: ${dateFormat.formatInstant(rebootTime)}") + appendLine("Install time: ${dateFormat.formatInstant(installTime)}") + appendLine("Update time: ${dateFormat.formatInstant(updateTime)}") + } companion object { fun new(packageInfo: PackageInfo): TimeInfo { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/util/EmailUtil.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/util/EmailUtil.kt index 5ac71100..d63be7ba 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/util/EmailUtil.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/util/EmailUtil.kt @@ -4,7 +4,6 @@ import android.content.Intent import android.net.Uri object EmailUtil { - /* * This mimetype is to hopefully ensure that only email apps respond to * the Intent. That isn't always the case though, as Evernote seems to diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt index c43d2ab9..67399a2e 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/view/SupportView.kt @@ -82,12 +82,13 @@ fun Support( message = message, setMessage = setMessage, setShowDialog = setShowDialog, - modifier = Modifier.padding( - top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier.padding( + top = paddingValues.calculateTopPadding() + ZcashTheme.dimens.spacingDefault, + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) if (isShowingDialog) { @@ -140,8 +141,9 @@ private fun SupportMainContent( FormTextField( value = message, onValueChange = setMessage, - modifier = Modifier - .fillMaxWidth(), + modifier = + Modifier + .fillMaxWidth(), label = { Text(text = stringResource(id = R.string.support_hint)) } ) @@ -150,9 +152,10 @@ private fun SupportMainContent( Body(stringResource(id = R.string.support_disclaimer)) Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) + modifier = + Modifier + .fillMaxHeight() + .weight(MINIMAL_WEIGHT) ) Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingLarge)) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/viewmodel/SupportViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/viewmodel/SupportViewModel.kt index 527a7525..28c27392 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/viewmodel/SupportViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/viewmodel/SupportViewModel.kt @@ -16,8 +16,9 @@ class SupportViewModel(application: Application) : AndroidViewModel(application) // Technically, some of the support info could be invalidated after a configuration change, // such as the user's current locale. However it really doesn't matter here since all we // care about is capturing a snapshot of the app, OS, and device state. - val supportInfo: StateFlow = flow { - emit(SupportInfo.new(application)) - } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT, Duration.ZERO), null) + val supportInfo: StateFlow = + flow { + emit(SupportInfo.new(application)) + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT, Duration.ZERO), null) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateChecker.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateChecker.kt index 052938f3..ed1383b8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateChecker.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateChecker.kt @@ -7,27 +7,29 @@ import com.google.android.play.core.appupdate.AppUpdateInfo import kotlinx.coroutines.flow.Flow interface AppUpdateChecker { - val stalenessDays: Int enum class Priority { LOW { override fun priorityUpperBorder() = 1 - override fun belongs(actualPriority: Int) = - actualPriority <= this.priorityUpperBorder() + + override fun belongs(actualPriority: Int) = actualPriority <= this.priorityUpperBorder() }, MEDIUM { override fun priorityUpperBorder() = 3 + override fun belongs(actualPriority: Int) = actualPriority > LOW.priorityUpperBorder() && actualPriority <= this.priorityUpperBorder() }, HIGH { override fun priorityUpperBorder() = 5 + override fun belongs(actualPriority: Int) = actualPriority > MEDIUM.priorityUpperBorder() && actualPriority <= this.priorityUpperBorder() }; abstract fun priorityUpperBorder(): Int + abstract fun belongs(actualPriority: Int): Boolean } @@ -44,9 +46,7 @@ interface AppUpdateChecker { return getPriority(inAppUpdatePriority) == Priority.HIGH } - fun newCheckForUpdateAvailabilityFlow( - context: Context - ): Flow + fun newCheckForUpdateAvailabilityFlow(context: Context): Flow fun newStartUpdateFlow( activity: ComponentActivity, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateCheckerImp.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateCheckerImp.kt index d15dccba..c997bdbe 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateCheckerImp.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/AppUpdateCheckerImp.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow class AppUpdateCheckerImp private constructor() : AppUpdateChecker { - companion object { private const val DEFAULT_STALENESS_DAYS = 3 @@ -36,38 +35,41 @@ class AppUpdateCheckerImp private constructor() : AppUpdateChecker { * * @return UpdateInfo object encapsulated in Flow in case of conditions succeeded */ - override fun newCheckForUpdateAvailabilityFlow( - context: Context - ): Flow = callbackFlow { - val appUpdateInfoTask = AppUpdateManagerFactory.create(context.applicationContext).appUpdateInfo + override fun newCheckForUpdateAvailabilityFlow(context: Context): Flow = + callbackFlow { + val appUpdateInfoTask = AppUpdateManagerFactory.create(context.applicationContext).appUpdateInfo - appUpdateInfoTask.addOnCompleteListener { infoTask -> - if (!infoTask.isSuccessful) { - emitFailure(this) - return@addOnCompleteListener - } + appUpdateInfoTask.addOnCompleteListener { infoTask -> + if (!infoTask.isSuccessful) { + emitFailure(this) + return@addOnCompleteListener + } - val appUpdateInfo = infoTask.result - if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && - appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) - ) { - // we force user to update immediately in case of high priority - // or in case of staleness days passed - if (isHighPriority(appUpdateInfo.updatePriority()) || - (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= stalenessDays + val appUpdateInfo = infoTask.result + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && + appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) ) { - emitSuccess(this, infoTask.result, UpdateState.Prepared) - } else { - emitSuccess(this, infoTask.result, UpdateState.Done) + // we force user to update immediately in case of high priority + // or in case of staleness days passed + if (isHighPriority(appUpdateInfo.updatePriority()) || + (appUpdateInfo.clientVersionStalenessDays() ?: -1) >= stalenessDays + ) { + emitSuccess(this, infoTask.result, UpdateState.Prepared) + } else { + emitSuccess(this, infoTask.result, UpdateState.Done) + } } } + awaitClose { + // No resources to release + } } - awaitClose { - // No resources to release - } - } - private fun emitSuccess(producerScope: ProducerScope, info: AppUpdateInfo, state: UpdateState) { + private fun emitSuccess( + producerScope: ProducerScope, + info: AppUpdateInfo, + state: UpdateState + ) { producerScope.trySend( UpdateInfo( getPriority(info.updatePriority()), @@ -106,23 +108,25 @@ class AppUpdateCheckerImp private constructor() : AppUpdateChecker { override fun newStartUpdateFlow( activity: ComponentActivity, appUpdateInfo: AppUpdateInfo - ): Flow = callbackFlow { - val appUpdateResultTask = AppUpdateManagerFactory.create(activity).startUpdateFlow( - appUpdateInfo, - activity, - AppUpdateOptions.defaultOptions(AppUpdateType.IMMEDIATE) - ) + ): Flow = + callbackFlow { + val appUpdateResultTask = + AppUpdateManagerFactory.create(activity).startUpdateFlow( + appUpdateInfo, + activity, + AppUpdateOptions.defaultOptions(AppUpdateType.IMMEDIATE) + ) - appUpdateResultTask.addOnCompleteListener { resultTask -> - if (resultTask.isSuccessful) { - trySend(resultTask.result) - } else { - trySend(ActivityResult.RESULT_IN_APP_UPDATE_FAILED) + appUpdateResultTask.addOnCompleteListener { resultTask -> + if (resultTask.isSuccessful) { + trySend(resultTask.result) + } else { + trySend(ActivityResult.RESULT_IN_APP_UPDATE_FAILED) + } + } + + awaitClose { + // No resources to release } } - - awaitClose { - // No resources to release - } - } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtil.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtil.kt index 7dbdce85..ba4e3393 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtil.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/util/PlayStoreUtil.kt @@ -5,12 +5,12 @@ import android.content.Intent import android.net.Uri object PlayStoreUtil { - const val PLAY_STORE_APP_URI = "market://details?id=" - const val FLAGS = Intent.FLAG_ACTIVITY_NO_HISTORY or - Intent.FLAG_ACTIVITY_NEW_TASK or - Intent.FLAG_ACTIVITY_MULTIPLE_TASK + const val FLAGS = + Intent.FLAG_ACTIVITY_NO_HISTORY or + Intent.FLAG_ACTIVITY_NEW_TASK or + Intent.FLAG_ACTIVITY_MULTIPLE_TASK /** * Returns Google Play store app intent. We assume the Play store app is installed, as we use diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt index 51ad9c47..a0048bae 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/view/UpdateView.kt @@ -73,27 +73,29 @@ fun Update( updateInfo, onDownload, onLater, - modifier = Modifier - .fillMaxWidth() - .padding( - top = ZcashTheme.dimens.spacingDefault, - bottom = ZcashTheme.dimens.spacingHuge, - start = ZcashTheme.dimens.screenHorizontalSpacing, - end = ZcashTheme.dimens.screenHorizontalSpacing - ) + modifier = + Modifier + .fillMaxWidth() + .padding( + top = ZcashTheme.dimens.spacingDefault, + bottom = ZcashTheme.dimens.spacingHuge, + start = ZcashTheme.dimens.screenHorizontalSpacing, + end = ZcashTheme.dimens.screenHorizontalSpacing + ) ) } ) { paddingValues -> UpdateContentNormal( onReference, - modifier = Modifier - .fillMaxWidth() - .padding( - top = paddingValues.calculateTopPadding(), - bottom = paddingValues.calculateBottomPadding(), - start = ZcashTheme.dimens.spacingDefault, - end = ZcashTheme.dimens.spacingDefault - ) + modifier = + Modifier + .fillMaxWidth() + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding(), + start = ZcashTheme.dimens.spacingDefault, + end = ZcashTheme.dimens.spacingDefault + ) ) } UpdateOverlayRunning(updateInfo) @@ -123,15 +125,16 @@ private fun UpdateTopAppBar(updateInfo: UpdateInfo) { TopAppBar( title = { Text( - text = stringResource( - updateInfo.isForce.let { force -> - if (force) { - R.string.update_critical_header - } else { - R.string.update_header + text = + stringResource( + updateInfo.isForce.let { force -> + if (force) { + R.string.update_critical_header + } else { + R.string.update_header + } } - } - ) + ) ) } ) @@ -158,15 +161,16 @@ private fun UpdateBottomAppBar( TertiaryButton( onClick = onLater, - text = stringResource( - updateInfo.isForce.let { force -> - if (force) { - R.string.update_later_disabled_button - } else { - R.string.update_later_enabled_button + text = + stringResource( + updateInfo.isForce.let { force -> + if (force) { + R.string.update_later_disabled_button + } else { + R.string.update_later_enabled_button + } } - } - ), + ), modifier = Modifier.testTag(UpdateTag.BTN_LATER), enabled = !updateInfo.isForce && updateInfo.state != UpdateState.Running, outerPaddingValues = PaddingValues(top = ZcashTheme.dimens.spacingSmall) @@ -193,16 +197,18 @@ private fun UpdateContentNormal( Body( text = stringResource(id = R.string.update_description), - modifier = Modifier - .wrapContentHeight() - .align(Alignment.CenterHorizontally) + modifier = + Modifier + .wrapContentHeight() + .align(Alignment.CenterHorizontally) ) Reference( text = stringResource(id = R.string.update_link_text), - modifier = Modifier - .wrapContentHeight() - .align(Alignment.CenterHorizontally), + modifier = + Modifier + .wrapContentHeight() + .align(Alignment.CenterHorizontally), onClick = { onReference() } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/viewmodel/UpdateViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/viewmodel/UpdateViewModel.kt index e55faa5a..f0d4bbda 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/viewmodel/UpdateViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/update/viewmodel/UpdateViewModel.kt @@ -21,7 +21,6 @@ class UpdateViewModel( updateInfo: UpdateInfo, private val appUpdateChecker: AppUpdateChecker ) : AndroidViewModel(application) { - val updateInfo: MutableStateFlow = MutableStateFlow(updateInfo) fun checkForAppUpdate() { @@ -49,12 +48,13 @@ class UpdateViewModel( activity, appUpdateInfo ).onFirst { resultCode -> - val state = when (resultCode) { - Activity.RESULT_OK -> UpdateState.Done - Activity.RESULT_CANCELED -> UpdateState.Canceled - ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> UpdateState.Failed - else -> UpdateState.Prepared - } + val state = + when (resultCode) { + Activity.RESULT_OK -> UpdateState.Done + Activity.RESULT_CANCELED -> UpdateState.Canceled + ActivityResult.RESULT_IN_APP_UPDATE_FAILED -> UpdateState.Failed + else -> UpdateState.Prepared + } updateInfo.value = updateInfo.value.copy(state = state) } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/AndroidNotEnoughSpace.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/AndroidNotEnoughSpace.kt index 1e3a79a0..0983d04d 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/AndroidNotEnoughSpace.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/AndroidNotEnoughSpace.kt @@ -1,4 +1,4 @@ -@file:Suppress("ktlint:filename") +@file:Suppress("ktlint:standard:filename") package co.electriccoin.zcash.ui.screen.warning diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/view/NotEnoughSpaceView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/view/NotEnoughSpaceView.kt index b7d9243e..b65c30ec 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/view/NotEnoughSpaceView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/view/NotEnoughSpaceView.kt @@ -42,7 +42,10 @@ private fun NotEnoughSpacePreview() { // TODO [#883]: https://github.com/Electric-Coin-Company/zashi-android/issues/883 @Composable -fun NotEnoughSpaceView(storageSpaceRequiredGigabytes: Int, spaceRequiredToContinueMegabytes: Int) { +fun NotEnoughSpaceView( + storageSpaceRequiredGigabytes: Int, + spaceRequiredToContinueMegabytes: Int +) { @Suppress("MagicNumber") val backgroundColor = Color(0xFF1A233A) Column( diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/viewmodel/StorageCheckViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/viewmodel/StorageCheckViewModel.kt index b7978045..81123af8 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/viewmodel/StorageCheckViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/warning/viewmodel/StorageCheckViewModel.kt @@ -14,12 +14,13 @@ class StorageCheckViewModel : ViewModel() { val requiredStorageSpaceGigabytes: Int = (StorageChecker.REQUIRED_FREE_SPACE_MEGABYTES / 1000) - val isEnoughSpace = flow { emit(StorageChecker.isEnoughSpace()) } - .stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), - null - ) + val isEnoughSpace = + flow { emit(StorageChecker.isEnoughSpace()) } + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), + null + ) val spaceRequiredToContinueMegabytes = flow { emit(StorageChecker.spaceRequiredToContinueMegabytes()) } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/work/SyncWorker.kt b/ui-lib/src/main/java/co/electriccoin/zcash/work/SyncWorker.kt index 59815ca7..5a3c605c 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/work/SyncWorker.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/work/SyncWorker.kt @@ -44,17 +44,17 @@ class SyncWorker(context: Context, workerParameters: WorkerParameters) : Corouti } companion object { - /* * There may be better periods; we have not optimized for this yet. */ private val DEFAULT_SYNC_PERIOD = 24.hours fun newWorkRequest(): PeriodicWorkRequest { - val constraints = Constraints.Builder() - .setRequiresStorageNotLow(true) - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() + val constraints = + Constraints.Builder() + .setRequiresStorageNotLow(true) + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() return PeriodicWorkRequestBuilder(DEFAULT_SYNC_PERIOD.toJavaDuration()) .setConstraints(constraints) diff --git a/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt b/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt index 706f6b01..60025079 100644 --- a/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt +++ b/ui-screenshot-test/src/main/java/co/electroniccoin/zcash/ui/screenshot/ScreenshotTest.kt @@ -74,9 +74,11 @@ private const val DEFAULT_TIMEOUT_MILLISECONDS = 10_000L * we pull in the appcompat library. */ class ScreenshotTest : UiTestPrerequisites() { - companion object { - fun takeScreenshot(tag: String, screenshotName: String) { + fun takeScreenshot( + tag: String, + screenshotName: String + ) { onView(isRoot()) .captureToBitmap() .writeToTestStorage("$screenshotName - $tag") @@ -86,13 +88,18 @@ class ScreenshotTest : UiTestPrerequisites() { @get:Rule val composeTestRule = createAndroidComposeRule(MainActivity::class.java) - private fun navigateTo(route: String) = runBlocking { - withContext(Dispatchers.Main) { - composeTestRule.activity.navControllerForTesting.navigate(route) + private fun navigateTo(route: String) = + runBlocking { + withContext(Dispatchers.Main) { + composeTestRule.activity.navControllerForTesting.navigate(route) + } } - } - private fun runWith(uiMode: UiMode, locale: String, action: (Context, String) -> Unit) { + private fun runWith( + uiMode: UiMode, + locale: String, + action: (Context, String) -> Unit + ) { val configurationOverride = ConfigurationOverride(uiMode, LocaleList.forLanguageTags(locale)) composeTestRule.activity.configurationOverrideFlow.value = configurationOverride @@ -105,25 +112,25 @@ class ScreenshotTest : UiTestPrerequisites() { @Test @MediumTest - fun take_screenshots_for_restore_wallet_light_en_XA() { + fun takeScreenshotsForRestoreWalletLightEnXA() { runWith(UiMode.Light, "en-XA") { context, tag -> - take_screenshots_for_restore_wallet(context, tag) + takeScreenshotsForRestoreWallet(context, tag) } } @Test @MediumTest - fun take_screenshots_for_restore_wallet_light_ar_XB() { + fun takeScreenshotsForRestoreWalletLightArXB() { runWith(UiMode.Light, "ar-XB") { context, tag -> - take_screenshots_for_restore_wallet(context, tag) + takeScreenshotsForRestoreWallet(context, tag) } } @Test @MediumTest - fun take_screenshots_for_restore_wallet_light_en_US() { + fun takeScreenshotsForRestoreWalletLightEnUS() { runWith(UiMode.Light, "en-US") { context, tag -> - take_screenshots_for_restore_wallet(context, tag) + takeScreenshotsForRestoreWallet(context, tag) } } @@ -131,14 +138,17 @@ class ScreenshotTest : UiTestPrerequisites() { @Test @MediumTest @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) - fun take_screenshots_for_restore_wallet_dark_en_US() { + fun takeScreenshotsForRestoreWalletDarkEnUS() { runWith(UiMode.Dark, "en-US") { context, tag -> - take_screenshots_for_restore_wallet(context, tag) + takeScreenshotsForRestoreWallet(context, tag) } } - @Suppress("LongMethod", "FunctionNaming", "CyclomaticComplexMethod") - private fun take_screenshots_for_restore_wallet(resContext: Context, tag: String) { + @Suppress("LongMethod", "CyclomaticComplexMethod") + private fun takeScreenshotsForRestoreWallet( + resContext: Context, + tag: String + ) { // TODO [#286]: Screenshot tests fail on Firebase Test Lab // TODO [#286]: https://github.com/Electric-Coin-Company/zashi-android/issues/286 if (FirebaseTestLabUtil.isFirebaseTestLab(ApplicationProvider.getApplicationContext())) { @@ -150,9 +160,10 @@ class ScreenshotTest : UiTestPrerequisites() { } composeTestRule.onNodeWithText( - text = resContext.getString( - R.string.onboarding_import_existing_wallet - ), + text = + resContext.getString( + R.string.onboarding_import_existing_wallet + ), ignoreCase = true ).also { it.performScrollTo() @@ -212,25 +223,25 @@ class ScreenshotTest : UiTestPrerequisites() { @Test @LargeTest - fun take_screenshots_for_new_wallet_and_rest_of_app_light_en_XA() { + fun takeScreenshotsForNewWalletAndRestOfAppLightEnXA() { runWith(UiMode.Light, "en-XA") { context, tag -> - take_screenshots_for_new_wallet_and_rest_of_app(context, tag) + takeScreenshotsForNewWalletAndRestOfApp(context, tag) } } @Test @LargeTest - fun take_screenshots_for_new_wallet_and_rest_of_app_light_ar_XB() { + fun takeScreenshotsForNewWalletAndRestOfAppLightArXB() { runWith(UiMode.Light, "ar-XB") { context, tag -> - take_screenshots_for_new_wallet_and_rest_of_app(context, tag) + takeScreenshotsForNewWalletAndRestOfApp(context, tag) } } @Test @LargeTest - fun take_screenshots_for_new_wallet_and_rest_of_app_light_en_US() { + fun takeScreenshotsForNewWalletAndRestOfAppLightEnUS() { runWith(UiMode.Light, "en-US") { context, tag -> - take_screenshots_for_new_wallet_and_rest_of_app(context, tag) + takeScreenshotsForNewWalletAndRestOfApp(context, tag) } } @@ -238,13 +249,16 @@ class ScreenshotTest : UiTestPrerequisites() { @Test @LargeTest @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q) - fun take_screenshots_for_new_wallet_and_rest_of_app_dark_en_US() { + fun takeScreenshotsForNewWalletAndRestOfAppDarkEnUS() { runWith(UiMode.Dark, "en-US") { context, tag -> - take_screenshots_for_new_wallet_and_rest_of_app(context, tag) + takeScreenshotsForNewWalletAndRestOfApp(context, tag) } } - private fun take_screenshots_for_new_wallet_and_rest_of_app(resContext: Context, tag: String) { + private fun takeScreenshotsForNewWalletAndRestOfApp( + resContext: Context, + tag: String + ) { // TODO [#286]: Screenshot tests fail on Firebase Test Lab // TODO [#286]: https://github.com/Electric-Coin-Company/zashi-android/issues/286 if (FirebaseTestLabUtil.isFirebaseTestLab(ApplicationProvider.getApplicationContext())) { @@ -366,7 +380,10 @@ private fun homeScreenshots( } } -private fun settingsScreenshots(tag: String, composeTestRule: ComposeTestRule) { +private fun settingsScreenshots( + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasTestTag(SettingsTag.SETTINGS_TOP_APP_BAR)).also { it.assertExists() } @@ -374,7 +391,11 @@ private fun settingsScreenshots(tag: String, composeTestRule: ComposeTestRule) { ScreenshotTest.takeScreenshot(tag, "Settings 1") } -private fun addressDetailsScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun addressDetailsScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasText(resContext.getString(R.string.wallet_address_title))).also { it.assertExists() } @@ -382,7 +403,11 @@ private fun addressDetailsScreenshots(resContext: Context, tag: String, composeT ScreenshotTest.takeScreenshot(tag, "Addresses 1") } -private fun transactionHistoryScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun transactionHistoryScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasText(resContext.getString(R.string.history_title))).also { it.assertExists() } @@ -392,7 +417,11 @@ private fun transactionHistoryScreenshots(resContext: Context, tag: String, comp // This screen is not currently navigable from the app @Suppress("UnusedPrivateMember") -private fun requestZecScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun requestZecScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasText(resContext.getString(R.string.request_title))).also { it.assertExists() } @@ -471,7 +500,7 @@ private fun sendZecScreenshots( } /* - TODO [#817]: Screenshot test on Send with pseudolocales problem + TODO [#817]: Screenshot test on Send with pseudo-locales problem TODO [#817]: https://github.com/Electric-Coin-Company/zashi-android/issues/817 // Screenshot: Confirmation ScreenshotTest.takeScreenshot(tag, "Send 3") @@ -495,7 +524,11 @@ private fun sendZecScreenshots( */ } -private fun supportScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun supportScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasText(resContext.getString(R.string.support_header))).also { it.assertExists() } @@ -503,7 +536,11 @@ private fun supportScreenshots(resContext: Context, tag: String, composeTestRule ScreenshotTest.takeScreenshot(tag, "Support 1") } -private fun exportPrivateDataScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun exportPrivateDataScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasText(resContext.getString(R.string.export_data_header))).also { it.assertExists() } @@ -511,7 +548,11 @@ private fun exportPrivateDataScreenshots(resContext: Context, tag: String, compo ScreenshotTest.takeScreenshot(tag, "Export Private Data 1") } -private fun aboutScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun aboutScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode( hasText(resContext.getString(R.string.about_title).uppercase()) ).also { @@ -521,7 +562,11 @@ private fun aboutScreenshots(resContext: Context, tag: String, composeTestRule: ScreenshotTest.takeScreenshot(tag, "About 1") } -private fun seedScreenshots(resContext: Context, tag: String, composeTestRule: ComposeTestRule) { +private fun seedScreenshots( + resContext: Context, + tag: String, + composeTestRule: ComposeTestRule +) { composeTestRule.onNode(hasText(resContext.getString(R.string.seed_recovery_header))).also { it.assertExists() }