Dependency injection implementation (#1513)
* Dependency injection implementation * Code cleanup * Code cleanup * Test hotfix * Code cleanup * Code cleanup * Code cleanup * Merge dependencies to bundle * Changelog update --------- Co-authored-by: Honza <rychnovsky.honza@gmail.com>
This commit is contained in:
parent
23603dcd29
commit
d29b0f7bb2
|
@ -6,6 +6,12 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Balance now also displays USD value
|
||||
- An option to enter USD amount in Send Transaction screen
|
||||
- Dependency injection using Koin has been added to the project. This helps us keep the codebase organized while
|
||||
adding new app features.
|
||||
|
||||
## [1.1.6 (712)] - 2024-09-04
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
package co.electriccoin.zcash.app
|
||||
|
||||
import co.electriccoin.zcash.crash.android.GlobalCrashReporter
|
||||
import co.electriccoin.zcash.di.coreModule
|
||||
import co.electriccoin.zcash.di.repositoryModule
|
||||
import co.electriccoin.zcash.di.useCaseModule
|
||||
import co.electriccoin.zcash.di.viewModelModule
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.spackle.StrictModeCompat
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
@Suppress("unused")
|
||||
class ZcashApplication : CoroutineApplication() {
|
||||
private val standardPreferenceProvider by inject<StandardPreferenceProvider>()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
@ -16,6 +26,17 @@ class ZcashApplication : CoroutineApplication() {
|
|||
|
||||
configureStrictMode()
|
||||
|
||||
startKoin {
|
||||
androidLogger()
|
||||
androidContext(this@ZcashApplication)
|
||||
modules(
|
||||
coreModule,
|
||||
repositoryModule,
|
||||
useCaseModule,
|
||||
viewModelModule
|
||||
)
|
||||
}
|
||||
|
||||
// Since analytics will need disk IO internally, we want this to be registered after strict
|
||||
// mode is configured to ensure none of that IO happens on the main thread
|
||||
configureAnalytics()
|
||||
|
@ -40,8 +61,7 @@ class ZcashApplication : CoroutineApplication() {
|
|||
private fun configureAnalytics() {
|
||||
if (GlobalCrashReporter.register(this)) {
|
||||
applicationScope.launch {
|
||||
val prefs = StandardPreferenceSingleton.getInstance(applicationContext)
|
||||
StandardPreferenceKeys.IS_ANALYTICS_ENABLED.observe(prefs).collect {
|
||||
StandardPreferenceKeys.IS_ANALYTICS_ENABLED.observe(standardPreferenceProvider()).collect {
|
||||
if (it) {
|
||||
GlobalCrashReporter.enable()
|
||||
} else {
|
||||
|
|
|
@ -1,25 +1,12 @@
|
|||
package co.electriccoin.zcash.configuration
|
||||
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
|
||||
import co.electriccoin.zcash.configuration.api.MergingConfigurationProvider
|
||||
import co.electriccoin.zcash.configuration.internal.intent.IntentConfigurationProvider
|
||||
import co.electriccoin.zcash.spackle.LazyWithArgument
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
object AndroidConfigurationFactory {
|
||||
private val instance =
|
||||
LazyWithArgument<Context, ConfigurationProvider> { 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 {
|
||||
fun new(): ConfigurationProvider {
|
||||
val configurationProviders =
|
||||
buildList<ConfigurationProvider> {
|
||||
// For ordering, ensure the IntentConfigurationProvider is first so that it can
|
||||
|
|
|
@ -117,8 +117,6 @@ As a specific example, the "Request ZEC" button on the home screen is currently
|
|||
# Shared Resources
|
||||
There are some app-wide resources that share a common namespace, and these should be documented here to make it easy to ensure there are no collisions.
|
||||
|
||||
* SharedPreferences
|
||||
* "co.electriccoin.zcash.encrypted" is defined as a preference file in `EncryptedPreferenceSingleton.kt`
|
||||
* Databases
|
||||
* Some databases are defined by the SDK
|
||||
* Notification IDs
|
||||
|
|
|
@ -155,6 +155,7 @@ GOOGLE_PLAY_SERVICES_GRADLE_PLUGIN_VERSION=4.4.1
|
|||
GRADLE_VERSIONS_PLUGIN_VERSION=0.51.0
|
||||
JGIT_VERSION=6.4.0.202211300538-r
|
||||
KTLINT_VERSION=1.2.1
|
||||
KOIN_VERSION=3.5.6
|
||||
|
||||
ACCOMPANIST_PERMISSIONS_VERSION=0.34.0
|
||||
ANDROIDX_ACTIVITY_VERSION=1.8.2
|
||||
|
|
|
@ -28,12 +28,11 @@ 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.
|
||||
*/
|
||||
class AndroidPreferenceProvider(
|
||||
class AndroidPreferenceProvider private constructor(
|
||||
private val sharedPreferences: SharedPreferences,
|
||||
private val dispatcher: CoroutineDispatcher
|
||||
) : PreferenceProvider {
|
||||
private val mutex = Mutex()
|
||||
|
||||
/*
|
||||
* Implementation note: EncryptedSharedPreferences are not thread-safe, so this implementation
|
||||
* confines them to a single background thread.
|
||||
|
@ -76,7 +75,7 @@ class AndroidPreferenceProvider(
|
|||
}
|
||||
|
||||
override fun observe(key: PreferenceKey): Flow<String?> =
|
||||
callbackFlow<Unit> {
|
||||
callbackFlow {
|
||||
val listener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||
// Callback on main thread
|
||||
|
@ -122,15 +121,13 @@ class AndroidPreferenceProvider(
|
|||
*/
|
||||
val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
|
||||
|
||||
val mainKey =
|
||||
withContext(singleThreadedDispatcher) {
|
||||
MasterKey.Builder(context).apply {
|
||||
setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
}.build()
|
||||
}
|
||||
|
||||
val sharedPreferences =
|
||||
withContext(singleThreadedDispatcher) {
|
||||
val mainKey =
|
||||
MasterKey.Builder(context).apply {
|
||||
setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
}.build()
|
||||
|
||||
EncryptedSharedPreferences.create(
|
||||
context,
|
||||
filename,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package co.electriccoin.zcash.preference
|
||||
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.preference.api.PreferenceProvider
|
||||
|
||||
class EncryptedPreferenceProvider(private val context: Context) : PreferenceHolder() {
|
||||
override suspend fun create(): PreferenceProvider {
|
||||
return AndroidPreferenceProvider.newEncrypted(context, "co.electriccoin.zcash.encrypted")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package co.electriccoin.zcash.preference
|
||||
|
||||
import co.electriccoin.zcash.preference.api.PreferenceProvider
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
abstract class PreferenceHolder {
|
||||
private var preferenceProvider: PreferenceProvider? = null
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend operator fun invoke(): PreferenceProvider =
|
||||
mutex.withLock {
|
||||
val preferenceProvider = preferenceProvider
|
||||
|
||||
if (preferenceProvider != null) {
|
||||
return preferenceProvider
|
||||
}
|
||||
|
||||
val newPreferenceProvider = create()
|
||||
this.preferenceProvider = newPreferenceProvider
|
||||
return newPreferenceProvider
|
||||
}
|
||||
|
||||
protected abstract suspend fun create(): PreferenceProvider
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package co.electriccoin.zcash.preference
|
||||
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.preference.api.PreferenceProvider
|
||||
|
||||
class StandardPreferenceProvider(private val context: Context) : PreferenceHolder() {
|
||||
override suspend fun create(): PreferenceProvider {
|
||||
return AndroidPreferenceProvider.newStandard(context, "co.electriccoin.zcash")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package cash.z.ecc.sdk
|
||||
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
val ANDROID_STATE_FLOW_TIMEOUT = 5.seconds
|
|
@ -183,6 +183,7 @@ dependencyResolutionManagement {
|
|||
val zcashBip39Version = extra["ZCASH_BIP39_VERSION"].toString()
|
||||
val zcashSdkVersion = extra["ZCASH_SDK_VERSION"].toString()
|
||||
val zxingVersion = extra["ZXING_VERSION"].toString()
|
||||
val koinVersion = extra["KOIN_VERSION"].toString()
|
||||
|
||||
// Standalone versions
|
||||
version("flank", flankVersion)
|
||||
|
@ -241,6 +242,8 @@ dependencyResolutionManagement {
|
|||
library("zcash-sdk-incubator", "cash.z.ecc.android:zcash-android-sdk-incubator:$zcashSdkVersion")
|
||||
library("zcash-bip39", "cash.z.ecc.android:kotlin-bip39:$zcashBip39Version")
|
||||
library("zxing", "com.google.zxing:core:$zxingVersion")
|
||||
library("koin", "io.insert-koin:koin-android:$koinVersion")
|
||||
library("koin-compose", "io.insert-koin:koin-androidx-compose:$koinVersion")
|
||||
|
||||
// Test libraries
|
||||
library("androidx-compose-test-junit", "androidx.compose.ui:ui-test-junit4:$androidxComposeVersion")
|
||||
|
@ -294,13 +297,6 @@ dependencyResolutionManagement {
|
|||
"androidx-viewmodel-compose"
|
||||
)
|
||||
)
|
||||
bundle(
|
||||
"play-update",
|
||||
listOf(
|
||||
"play-update",
|
||||
"play-update-ktx",
|
||||
)
|
||||
)
|
||||
bundle(
|
||||
"androidx-test",
|
||||
listOf(
|
||||
|
@ -312,6 +308,20 @@ dependencyResolutionManagement {
|
|||
"androidx-test-runner"
|
||||
)
|
||||
)
|
||||
bundle(
|
||||
"koin",
|
||||
listOf(
|
||||
"koin",
|
||||
"koin-compose",
|
||||
)
|
||||
)
|
||||
bundle(
|
||||
"play-update",
|
||||
listOf(
|
||||
"play-update",
|
||||
"play-update-ktx",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import android.os.PowerManager
|
|||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.runner.AndroidJUnitRunner
|
||||
|
||||
class ZcashUiTestRunner : AndroidJUnitRunner() {
|
||||
open class ZcashUiTestRunner : AndroidJUnitRunner() {
|
||||
private lateinit var wakeLock: PowerManager.WakeLock
|
||||
|
||||
override fun onCreate(arguments: Bundle?) {
|
||||
|
|
|
@ -29,9 +29,8 @@ fun Override(
|
|||
val contextWrapper =
|
||||
run {
|
||||
val context = LocalContext.current
|
||||
object : ContextThemeWrapper() {
|
||||
object : ContextThemeWrapper(context, null) {
|
||||
init {
|
||||
attachBaseContext(context)
|
||||
applyOverrideConfiguration(configuration)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,10 +95,11 @@ dependencies {
|
|||
implementation(libs.androidx.lifecycle.livedata)
|
||||
implementation(libs.androidx.splash)
|
||||
implementation(libs.androidx.workmanager)
|
||||
implementation(libs.bundles.androidx.biometric)
|
||||
api(libs.bundles.androidx.biometric)
|
||||
implementation(libs.bundles.androidx.camera)
|
||||
implementation(libs.bundles.androidx.compose.core)
|
||||
implementation(libs.bundles.androidx.compose.extended)
|
||||
api(libs.bundles.koin)
|
||||
implementation(libs.bundles.play.update)
|
||||
implementation(libs.kotlin.stdlib)
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
|
@ -114,12 +115,12 @@ dependencies {
|
|||
|
||||
implementation(projects.buildInfoLib)
|
||||
implementation(projects.configurationApiLib)
|
||||
implementation(projects.configurationImplAndroidLib)
|
||||
implementation(projects.crashAndroidLib)
|
||||
implementation(projects.preferenceApiLib)
|
||||
implementation(projects.preferenceImplAndroidLib)
|
||||
implementation(projects.sdkExtLib)
|
||||
implementation(projects.spackleAndroidLib)
|
||||
api(projects.configurationImplAndroidLib)
|
||||
api(projects.sdkExtLib)
|
||||
api(projects.uiDesignLib)
|
||||
|
||||
androidTestImplementation(projects.testLib)
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.preference
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import co.electriccoin.zcash.preference.model.entry.PreferenceDefault
|
||||
import org.junit.Test
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.test.assertFalse
|
||||
|
||||
class EncryptedPreferenceKeysTest {
|
||||
// This test is primary to prevent copy-paste errors in preference keys
|
||||
@SmallTest
|
||||
@Test
|
||||
fun unique_keys() {
|
||||
val fieldValueSet = mutableSetOf<String>()
|
||||
|
||||
EncryptedPreferenceKeys::class.memberProperties
|
||||
.map { it.getter.call(EncryptedPreferenceKeys) }
|
||||
.map { it as PreferenceDefault<*> }
|
||||
.map { it.key }
|
||||
.forEach {
|
||||
assertFalse(fieldValueSet.contains(it.key), "Duplicate key $it")
|
||||
|
||||
fieldValueSet.add(it.key)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.support.model
|
||||
|
||||
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
|
||||
import co.electriccoin.zcash.ui.test.getAppContext
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
@ -10,7 +11,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_time() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.timeInfo.toSupportString()
|
||||
|
||||
|
@ -24,7 +25,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_app() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.appInfo.toSupportString()
|
||||
|
||||
|
@ -38,7 +39,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_os() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.operatingSystemInfo.toSupportString()
|
||||
|
||||
|
@ -52,7 +53,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_device() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.deviceInfo.toSupportString()
|
||||
|
||||
|
@ -66,7 +67,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_crash() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.crashInfo.toCrashSupportString()
|
||||
|
||||
|
@ -77,7 +78,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_environment() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.environmentInfo.toSupportString()
|
||||
|
||||
|
@ -91,7 +92,7 @@ class SupportInfoTest {
|
|||
@Test
|
||||
fun filter_permission() =
|
||||
runTest {
|
||||
val supportInfo = SupportInfo.new(getAppContext())
|
||||
val supportInfo = SupportInfo.new(getAppContext(), AndroidConfigurationFactory.new())
|
||||
|
||||
val individualExpected = supportInfo.permissionInfo.toPermissionSupportString()
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import androidx.activity.ComponentActivity
|
|||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.test.filters.MediumTest
|
||||
import cash.z.ecc.android.sdk.ext.onFirst
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImp
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImpl
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
import co.electriccoin.zcash.ui.test.getAppContext
|
||||
|
@ -19,13 +19,13 @@ import kotlin.test.assertNotNull
|
|||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class AppUpdateCheckerImpTest {
|
||||
class AppUpdateCheckerImplTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
companion object {
|
||||
val context: Context = getAppContext()
|
||||
val updateChecker = AppUpdateCheckerImp.new()
|
||||
val updateChecker = AppUpdateCheckerImpl()
|
||||
}
|
||||
|
||||
private fun getAppUpdateInfoFlow(): Flow<UpdateInfo> {
|
|
@ -23,8 +23,8 @@ class UpdateViewAndroidTest : UiTestPrerequisites() {
|
|||
|
||||
private fun newTestSetup(updateInfo: UpdateInfo) =
|
||||
UpdateViewAndroidTestSetup(
|
||||
composeTestRule,
|
||||
updateInfo
|
||||
updateInfo,
|
||||
composeTestRule
|
||||
).apply {
|
||||
setDefaultContent()
|
||||
}
|
||||
|
|
|
@ -3,21 +3,36 @@ package co.electriccoin.zcash.ui.screen.update.view
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerMock
|
||||
import co.electriccoin.zcash.ui.screen.update.WrapUpdate
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateInfo
|
||||
import co.electriccoin.zcash.ui.screen.update.viewmodel.UpdateViewModel
|
||||
|
||||
class UpdateViewAndroidTestSetup(
|
||||
private val composeTestRule: AndroidComposeTestRule<*, *>,
|
||||
private val updateInfo: UpdateInfo
|
||||
updateInfo: UpdateInfo,
|
||||
private val composeTestRule: AndroidComposeTestRule<*, *>
|
||||
) {
|
||||
private val viewModel =
|
||||
UpdateViewModel(
|
||||
application = composeTestRule.activity.application,
|
||||
updateInfo = updateInfo,
|
||||
appUpdateChecker = AppUpdateCheckerMock.new()
|
||||
)
|
||||
|
||||
@Composable
|
||||
@Suppress("TestFunctionName")
|
||||
fun DefaultContent() {
|
||||
CompositionLocalProvider(LocalActivity provides composeTestRule.activity) {
|
||||
val updateInfo = viewModel.updateInfo.collectAsStateWithLifecycle().value
|
||||
// val appUpdateInfo = updateInfo.appUpdateInfo
|
||||
WrapUpdate(
|
||||
updateInfo
|
||||
updateInfo = updateInfo,
|
||||
checkForUpdate = viewModel::checkForAppUpdate,
|
||||
remindLater = viewModel::remindLater,
|
||||
goForUpdate = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package co.electriccoin.zcash.di
|
||||
|
||||
import androidx.biometric.BiometricManager
|
||||
import cash.z.ecc.android.sdk.WalletCoordinator
|
||||
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
|
||||
import co.electriccoin.zcash.global.newInstance
|
||||
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
|
||||
import co.electriccoin.zcash.ui.preference.PersistableWalletPreferenceDefault
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImpl
|
||||
import org.koin.core.module.dsl.factoryOf
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
|
||||
val coreModule =
|
||||
module {
|
||||
single {
|
||||
WalletCoordinator.newInstance(
|
||||
context = get(),
|
||||
encryptedPreferenceProvider = get(),
|
||||
persistableWalletPreference = get(),
|
||||
)
|
||||
}
|
||||
|
||||
single {
|
||||
PersistableWalletPreferenceDefault(PreferenceKey("persistable_wallet"))
|
||||
}
|
||||
|
||||
singleOf(::StandardPreferenceProvider)
|
||||
singleOf(::EncryptedPreferenceProvider)
|
||||
|
||||
single { BiometricManager.from(get()) }
|
||||
|
||||
factoryOf(::AppUpdateCheckerImpl) bind AppUpdateChecker::class
|
||||
|
||||
factory { AndroidConfigurationFactory.new() }
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package co.electriccoin.zcash.di
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
import androidx.lifecycle.viewmodel.CreationExtras
|
||||
import org.koin.androidx.compose.defaultExtras
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.currentKoinScope
|
||||
import org.koin.core.parameter.ParametersDefinition
|
||||
import org.koin.core.qualifier.Qualifier
|
||||
import org.koin.core.scope.Scope
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
inline fun <reified T : ViewModel> koinActivityViewModel(
|
||||
qualifier: Qualifier? = null,
|
||||
viewModelStoreOwner: ViewModelStoreOwner = LocalContext.componentActivity(),
|
||||
key: String? = null,
|
||||
extras: CreationExtras = defaultExtras(LocalContext.componentActivity()),
|
||||
scope: Scope = currentKoinScope(),
|
||||
noinline parameters: ParametersDefinition? = null,
|
||||
) = koinViewModel<T>(
|
||||
qualifier = qualifier,
|
||||
viewModelStoreOwner = viewModelStoreOwner,
|
||||
key = key,
|
||||
extras = extras,
|
||||
scope = scope,
|
||||
parameters = parameters,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ProvidableCompositionLocal<Context>.componentActivity(): ComponentActivity {
|
||||
val context = this.current
|
||||
return when {
|
||||
context is ComponentActivity -> context
|
||||
context is ContextWrapper && context.baseContext is ComponentActivity ->
|
||||
context.baseContext as ComponentActivity
|
||||
|
||||
else -> throw ClassCastException("Context is not a ComponentActivity")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package co.electriccoin.zcash.di
|
||||
|
||||
import co.electriccoin.zcash.ui.common.repository.WalletRepository
|
||||
import co.electriccoin.zcash.ui.common.repository.WalletRepositoryImpl
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
|
||||
val repositoryModule =
|
||||
module {
|
||||
singleOf(::WalletRepositoryImpl) bind WalletRepository::class
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package co.electriccoin.zcash.di
|
||||
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSynchronizerUseCase
|
||||
import org.koin.core.module.dsl.singleOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
val useCaseModule =
|
||||
module {
|
||||
singleOf(::ObserveSynchronizerUseCase)
|
||||
singleOf(::GetSynchronizerUseCase)
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package co.electriccoin.zcash.di
|
||||
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.AuthenticationViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.HomeViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.account.viewmodel.TransactionHistoryViewModel
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restore.viewmodel.RestoreViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
|
||||
import co.electriccoin.zcash.ui.screen.sendconfirmation.viewmodel.CreateTransactionsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.ScreenBrightnessViewModel
|
||||
import co.electriccoin.zcash.ui.screen.settings.viewmodel.SettingsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.support.viewmodel.SupportViewModel
|
||||
import co.electriccoin.zcash.ui.screen.update.viewmodel.UpdateViewModel
|
||||
import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel
|
||||
import co.electriccoin.zcash.ui.screen.whatsnew.viewmodel.WhatsNewViewModel
|
||||
import org.koin.androidx.viewmodel.dsl.viewModelOf
|
||||
import org.koin.dsl.module
|
||||
|
||||
val viewModelModule =
|
||||
module {
|
||||
viewModelOf(::WalletViewModel)
|
||||
viewModelOf(::AuthenticationViewModel)
|
||||
viewModelOf(::CheckUpdateViewModel)
|
||||
viewModelOf(::HomeViewModel)
|
||||
viewModelOf(::TransactionHistoryViewModel)
|
||||
viewModelOf(::OnboardingViewModel)
|
||||
viewModelOf(::StorageCheckViewModel)
|
||||
viewModelOf(::RestoreViewModel)
|
||||
viewModelOf(::ScreenBrightnessViewModel)
|
||||
viewModelOf(::SettingsViewModel)
|
||||
viewModelOf(::SupportViewModel)
|
||||
viewModelOf(::CreateTransactionsViewModel)
|
||||
viewModelOf(::RestoreSuccessViewModel)
|
||||
viewModelOf(::WhatsNewViewModel)
|
||||
viewModelOf(::UpdateViewModel)
|
||||
}
|
|
@ -2,30 +2,21 @@ package co.electriccoin.zcash.global
|
|||
|
||||
import android.content.Context
|
||||
import cash.z.ecc.android.sdk.WalletCoordinator
|
||||
import co.electriccoin.zcash.spackle.LazyWithArgument
|
||||
import co.electriccoin.zcash.ui.preference.EncryptedPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.EncryptedPreferenceSingleton
|
||||
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
|
||||
import co.electriccoin.zcash.ui.preference.PersistableWalletPreferenceDefault
|
||||
import kotlinx.coroutines.flow.emitAll
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
private val lazy =
|
||||
LazyWithArgument<Context, WalletCoordinator> {
|
||||
/**
|
||||
* A flow of the user's stored wallet. Null indicates that no wallet has been stored.
|
||||
*/
|
||||
val persistableWallet =
|
||||
fun WalletCoordinator.Companion.newInstance(
|
||||
context: Context,
|
||||
encryptedPreferenceProvider: EncryptedPreferenceProvider,
|
||||
persistableWalletPreference: PersistableWalletPreferenceDefault
|
||||
): WalletCoordinator {
|
||||
return WalletCoordinator(
|
||||
context = context,
|
||||
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(persistableWalletPreference.observe(encryptedPreferenceProvider()))
|
||||
}
|
||||
|
||||
WalletCoordinator(
|
||||
context = it,
|
||||
persistableWallet = persistableWallet
|
||||
)
|
||||
}
|
||||
|
||||
fun WalletCoordinator.Companion.getInstance(context: Context) = lazy.getInstance(context)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.content.pm.ActivityInfo
|
|||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
@ -58,20 +57,19 @@ import kotlinx.coroutines.flow.combine
|
|||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class MainActivity : FragmentActivity() {
|
||||
private val homeViewModel by viewModels<HomeViewModel>()
|
||||
private val homeViewModel by viewModel<HomeViewModel>()
|
||||
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel by viewModel<WalletViewModel>()
|
||||
|
||||
val storageCheckViewModel by viewModels<StorageCheckViewModel>()
|
||||
val storageCheckViewModel by viewModel<StorageCheckViewModel>()
|
||||
|
||||
internal val authenticationViewModel by viewModels<AuthenticationViewModel> {
|
||||
AuthenticationViewModel.AuthenticationViewModelFactory(application)
|
||||
}
|
||||
internal val authenticationViewModel by viewModel<AuthenticationViewModel>()
|
||||
|
||||
lateinit var navControllerForTesting: NavHostController
|
||||
|
||||
|
@ -276,7 +274,6 @@ class MainActivity : FragmentActivity() {
|
|||
private fun monitorForBackgroundSync() {
|
||||
val isEnableBackgroundSyncFlow =
|
||||
run {
|
||||
val homeViewModel by viewModels<HomeViewModel>()
|
||||
val isSecretReadyFlow = walletViewModel.secretState.map { it is SecretState.Ready }
|
||||
val isBackgroundSyncEnabledFlow = homeViewModel.isBackgroundSyncEnabled.filterNotNull()
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.common
|
||||
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
// Recommended timeout for Android configuration changes to keep Kotlin Flow from restarting
|
||||
val ANDROID_STATE_FLOW_TIMEOUT = 5.seconds
|
|
@ -33,7 +33,7 @@ fun MainActivity.BindCompLocalProvider(content: @Composable () -> Unit) {
|
|||
LocalScreenBrightness provides screenBrightness,
|
||||
LocalScreenTimeout provides screenTimeout,
|
||||
LocalNavController provides navController,
|
||||
LocalActivity provides this,
|
||||
LocalActivity provides this@BindCompLocalProvider,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package co.electriccoin.zcash.ui.common.repository
|
||||
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.WalletCoordinator
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
interface WalletRepository {
|
||||
val synchronizer: StateFlow<Synchronizer?>
|
||||
}
|
||||
|
||||
class WalletRepositoryImpl(
|
||||
walletCoordinator: WalletCoordinator,
|
||||
) : WalletRepository {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
|
||||
|
||||
override val synchronizer: StateFlow<Synchronizer?> =
|
||||
walletCoordinator.synchronizer.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
initialValue = null
|
||||
)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.repository.WalletRepository
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
class GetSynchronizerUseCase(
|
||||
private val walletRepository: WalletRepository
|
||||
) {
|
||||
suspend operator fun invoke() = walletRepository.synchronizer.filterNotNull().first()
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.repository.WalletRepository
|
||||
|
||||
class ObserveSynchronizerUseCase(
|
||||
private val walletRepository: WalletRepository
|
||||
) {
|
||||
operator fun invoke() = walletRepository.synchronizer
|
||||
}
|
|
@ -7,17 +7,15 @@ import androidx.biometric.BiometricManager.Authenticators
|
|||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
|
||||
import co.electriccoin.zcash.spackle.AndroidApiVersion
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
|
||||
import co.electriccoin.zcash.ui.screen.authentication.AuthenticationUseCase
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -37,7 +35,9 @@ import kotlin.time.Duration.Companion.milliseconds
|
|||
private const val DEFAULT_INITIAL_DELAY = 0
|
||||
|
||||
class AuthenticationViewModel(
|
||||
private val application: Application,
|
||||
application: Application,
|
||||
private val standardPreferenceProvider: StandardPreferenceProvider,
|
||||
private val biometricManager: BiometricManager,
|
||||
) : AndroidViewModel(application) {
|
||||
private val executor: Executor by lazy { ContextCompat.getMainExecutor(application) }
|
||||
private lateinit var biometricPrompt: BiometricPrompt
|
||||
|
@ -54,6 +54,7 @@ class AuthenticationViewModel(
|
|||
// Android SDK version == 28 || 29
|
||||
(AndroidApiVersion.isExactlyP || AndroidApiVersion.isExactlyQ) ->
|
||||
Authenticators.BIOMETRIC_WEAK or Authenticators.DEVICE_CREDENTIAL
|
||||
|
||||
else -> error("Unsupported Android SDK version")
|
||||
}
|
||||
|
||||
|
@ -128,6 +129,7 @@ class AuthenticationViewModel(
|
|||
BiometricSupportResult.Success -> {
|
||||
// No action needed, let user proceed to the authentication steps
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Otherwise biometric authentication might not be available, but users still can use the
|
||||
// device credential authentication path
|
||||
|
@ -253,24 +255,28 @@ class AuthenticationViewModel(
|
|||
promptInfo =
|
||||
BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(
|
||||
application.applicationContext.run {
|
||||
getApplication<Application>().applicationContext.run {
|
||||
getString(R.string.authentication_system_ui_title, getString(R.string.app_name))
|
||||
}
|
||||
)
|
||||
.setSubtitle(
|
||||
application.applicationContext.run {
|
||||
getApplication<Application>().applicationContext.run {
|
||||
getString(
|
||||
R.string.authentication_system_ui_subtitle,
|
||||
getString(
|
||||
when (useCase) {
|
||||
AuthenticationUseCase.AppAccess ->
|
||||
R.string.app_name
|
||||
|
||||
AuthenticationUseCase.DeleteWallet ->
|
||||
R.string.authentication_use_case_delete_wallet
|
||||
|
||||
AuthenticationUseCase.ExportPrivateData ->
|
||||
R.string.authentication_use_case_export_data
|
||||
|
||||
AuthenticationUseCase.SeedRecovery ->
|
||||
R.string.authentication_use_case_seed_recovery
|
||||
|
||||
AuthenticationUseCase.SendFunds ->
|
||||
R.string.authentication_use_case_send_funds
|
||||
}
|
||||
|
@ -292,13 +298,12 @@ class AuthenticationViewModel(
|
|||
}
|
||||
|
||||
private fun getBiometricAuthenticationSupport(allowedAuthenticators: Int): BiometricSupportResult {
|
||||
val biometricManager = BiometricManager.from(application)
|
||||
|
||||
return when (biometricManager.canAuthenticate(allowedAuthenticators)) {
|
||||
BiometricManager.BIOMETRIC_SUCCESS -> {
|
||||
Twig.debug { "Auth canAuthenticate BIOMETRIC_SUCCESS: App can authenticate using biometrics." }
|
||||
BiometricSupportResult.Success
|
||||
}
|
||||
|
||||
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
|
||||
Twig.info {
|
||||
"Auth canAuthenticate BIOMETRIC_ERROR_NO_HARDWARE: No biometric features available on " +
|
||||
|
@ -306,6 +311,7 @@ class AuthenticationViewModel(
|
|||
}
|
||||
BiometricSupportResult.ErrorNoHardware
|
||||
}
|
||||
|
||||
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> {
|
||||
Twig.error {
|
||||
"Auth canAuthenticate BIOMETRIC_ERROR_HW_UNAVAILABLE: Biometric features are currently " +
|
||||
|
@ -313,6 +319,7 @@ class AuthenticationViewModel(
|
|||
}
|
||||
BiometricSupportResult.ErrorHwUnavailable
|
||||
}
|
||||
|
||||
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
|
||||
Twig.warn {
|
||||
"Auth canAuthenticate BIOMETRIC_ERROR_NONE_ENROLLED: Prompts the user to create " +
|
||||
|
@ -320,6 +327,7 @@ class AuthenticationViewModel(
|
|||
}
|
||||
BiometricSupportResult.ErrorNoneEnrolled
|
||||
}
|
||||
|
||||
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> {
|
||||
Twig.error {
|
||||
"Auth canAuthenticate BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: The user can't authenticate " +
|
||||
|
@ -328,6 +336,7 @@ class AuthenticationViewModel(
|
|||
}
|
||||
BiometricSupportResult.ErrorSecurityUpdateRequired
|
||||
}
|
||||
|
||||
BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> {
|
||||
Twig.error {
|
||||
"Auth canAuthenticate BIOMETRIC_ERROR_UNSUPPORTED: The user can't authenticate because " +
|
||||
|
@ -335,6 +344,7 @@ class AuthenticationViewModel(
|
|||
}
|
||||
BiometricSupportResult.ErrorUnsupported
|
||||
}
|
||||
|
||||
BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> {
|
||||
Twig.error {
|
||||
"Auth canAuthenticate BIOMETRIC_STATUS_UNKNOWN: Unable to determine whether the user can" +
|
||||
|
@ -345,6 +355,7 @@ class AuthenticationViewModel(
|
|||
}
|
||||
BiometricSupportResult.StatusUnknown
|
||||
}
|
||||
|
||||
else -> {
|
||||
Twig.error { "Unexpected biometric framework status" }
|
||||
BiometricSupportResult.StatusUnexpected
|
||||
|
@ -352,20 +363,9 @@ class AuthenticationViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class AuthenticationViewModelFactory(
|
||||
private val application: Application
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
require(modelClass.isAssignableFrom(AuthenticationViewModel::class.java)) { "ViewModel Not Found." }
|
||||
return AuthenticationViewModel(application) as T
|
||||
}
|
||||
}
|
||||
|
||||
private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow<Boolean?> =
|
||||
flow<Boolean?> {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
emitAll(default.observe(preferenceProvider))
|
||||
emitAll(default.observe(standardPreferenceProvider()))
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
|
|
|
@ -2,8 +2,6 @@ package co.electriccoin.zcash.ui.common.viewmodel
|
|||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.android.sdk.ext.onFirst
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
|
||||
|
@ -26,18 +24,4 @@ class CheckUpdateViewModel(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class CheckUpdateViewModelFactory(
|
||||
private val application: Application,
|
||||
private val appUpdateChecker: AppUpdateChecker
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return if (modelClass.isAssignableFrom(CheckUpdateViewModel::class.java)) {
|
||||
CheckUpdateViewModel(application, appUpdateChecker) as T
|
||||
} else {
|
||||
throw IllegalArgumentException("ViewModel Not Found.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package co.electriccoin.zcash.ui.common.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
|
||||
import co.electriccoin.zcash.configuration.model.map.Configuration
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
|
@ -19,7 +18,10 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class HomeViewModel(
|
||||
androidConfigurationProvider: ConfigurationProvider,
|
||||
private val standardPreferenceProvider: StandardPreferenceProvider,
|
||||
) : ViewModel() {
|
||||
/**
|
||||
* A flow of whether background sync is enabled
|
||||
*/
|
||||
|
@ -54,7 +56,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
|||
}
|
||||
|
||||
val configurationFlow: StateFlow<Configuration?> =
|
||||
AndroidConfigurationFactory.getInstance(application).getConfigurationFlow()
|
||||
androidConfigurationProvider.getConfigurationFlow()
|
||||
.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT.inWholeMilliseconds),
|
||||
|
@ -67,8 +69,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
|||
|
||||
private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow<Boolean?> =
|
||||
flow<Boolean?> {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
emitAll(default.observe(preferenceProvider))
|
||||
emitAll(default.observe(standardPreferenceProvider()))
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
|
@ -80,8 +81,7 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
|||
newState: Boolean
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
default.putValue(prefs, newState)
|
||||
default.putValue(standardPreferenceProvider(), newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,13 +24,14 @@ import cash.z.ecc.android.sdk.model.WalletBalance
|
|||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.global.getInstance
|
||||
import co.electriccoin.zcash.preference.EncryptedPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.NullableBooleanPreferenceDefault
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.NavigationTargets.EXCHANGE_RATE_OPT_IN
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.common.compose.BalanceState
|
||||
import co.electriccoin.zcash.ui.common.extension.throttle
|
||||
import co.electriccoin.zcash.ui.common.model.OnboardingState
|
||||
|
@ -41,13 +42,12 @@ import co.electriccoin.zcash.ui.common.model.hasChangePending
|
|||
import co.electriccoin.zcash.ui.common.model.hasValuePending
|
||||
import co.electriccoin.zcash.ui.common.model.spendableBalance
|
||||
import co.electriccoin.zcash.ui.common.model.totalBalance
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSynchronizerUseCase
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import co.electriccoin.zcash.ui.common.wallet.RefreshLock
|
||||
import co.electriccoin.zcash.ui.common.wallet.StaleLock
|
||||
import co.electriccoin.zcash.ui.preference.EncryptedPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.EncryptedPreferenceSingleton
|
||||
import co.electriccoin.zcash.ui.preference.PersistableWalletPreferenceDefault
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
|
||||
import co.electriccoin.zcash.ui.screen.account.ext.TransactionOverviewExt
|
||||
import co.electriccoin.zcash.ui.screen.account.ext.getSortHeight
|
||||
import co.electriccoin.zcash.ui.screen.account.state.TransactionHistorySyncState
|
||||
|
@ -88,12 +88,17 @@ import kotlin.time.Duration.Companion.seconds
|
|||
// for loading the preferences.
|
||||
// TODO [#292]: Should be moved to SDK-EXT-UI module.
|
||||
// TODO [#292]: https://github.com/Electric-Coin-Company/zashi-android/issues/292
|
||||
@Suppress("TooManyFunctions")
|
||||
@Suppress(
|
||||
"TooManyFunctions"
|
||||
)
|
||||
class WalletViewModel(
|
||||
application: Application
|
||||
application: Application,
|
||||
observeSynchronizer: ObserveSynchronizerUseCase,
|
||||
private val persistableWalletPreference: PersistableWalletPreferenceDefault,
|
||||
private val walletCoordinator: WalletCoordinator,
|
||||
private val encryptedPreferenceProvider: EncryptedPreferenceProvider,
|
||||
private val standardPreferenceProvider: StandardPreferenceProvider,
|
||||
) : AndroidViewModel(application) {
|
||||
private val walletCoordinator = WalletCoordinator.getInstance(application)
|
||||
|
||||
/*
|
||||
* Using the Mutex may be overkill, but it ensures that if multiple calls are accidentally made
|
||||
* that they have a consistent ordering.
|
||||
|
@ -107,23 +112,18 @@ class WalletViewModel(
|
|||
/**
|
||||
* 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 = observeSynchronizer()
|
||||
|
||||
/**
|
||||
* A flow of the wallet block synchronization state.
|
||||
*/
|
||||
val walletRestoringState: StateFlow<WalletRestoringState> =
|
||||
flow {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(application)
|
||||
emitAll(
|
||||
StandardPreferenceKeys.WALLET_RESTORING_STATE.observe(preferenceProvider).map { persistedNumber ->
|
||||
WalletRestoringState.fromNumber(persistedNumber)
|
||||
}
|
||||
StandardPreferenceKeys.WALLET_RESTORING_STATE
|
||||
.observe(standardPreferenceProvider()).map { persistedNumber ->
|
||||
WalletRestoringState.fromNumber(persistedNumber)
|
||||
}
|
||||
)
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
|
@ -162,11 +162,11 @@ class WalletViewModel(
|
|||
*/
|
||||
private val onboardingState =
|
||||
flow {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(application)
|
||||
emitAll(
|
||||
StandardPreferenceKeys.ONBOARDING_STATE.observe(preferenceProvider).map { persistedNumber ->
|
||||
OnboardingState.fromNumber(persistedNumber)
|
||||
}
|
||||
StandardPreferenceKeys.ONBOARDING_STATE
|
||||
.observe(standardPreferenceProvider()).map { persistedNumber ->
|
||||
OnboardingState.fromNumber(persistedNumber)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -510,12 +510,9 @@ class WalletViewModel(
|
|||
* Persists a wallet asynchronously. Clients observe [secretState] to see the side effects.
|
||||
*/
|
||||
private fun persistWallet(persistableWallet: PersistableWallet) {
|
||||
val application = getApplication<Application>()
|
||||
|
||||
viewModelScope.launch {
|
||||
val preferenceProvider = EncryptedPreferenceSingleton.getInstance(application)
|
||||
persistWalletMutex.withLock {
|
||||
EncryptedPreferenceKeys.PERSISTABLE_WALLET.putValue(preferenceProvider, persistableWallet)
|
||||
persistableWalletPreference.putValue(encryptedPreferenceProvider(), persistableWallet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -526,17 +523,17 @@ class WalletViewModel(
|
|||
* for a user creating a new wallet.
|
||||
*/
|
||||
fun persistOnboardingState(onboardingState: OnboardingState) {
|
||||
val application = getApplication<Application>()
|
||||
|
||||
viewModelScope.launch {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(application)
|
||||
|
||||
// Use the Mutex here to avoid timing issues. During wallet restore, persistOnboardingState()
|
||||
// is called prior to persistExistingWallet(). Although persistOnboardingState() should
|
||||
// complete quickly, it isn't guaranteed to complete before persistExistingWallet()
|
||||
// unless a mutex is used here.
|
||||
persistWalletMutex.withLock {
|
||||
StandardPreferenceKeys.ONBOARDING_STATE.putValue(preferenceProvider, onboardingState.toNumber())
|
||||
StandardPreferenceKeys.ONBOARDING_STATE.putValue(
|
||||
standardPreferenceProvider(),
|
||||
onboardingState
|
||||
.toNumber()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -548,11 +545,11 @@ class WalletViewModel(
|
|||
* state from the SDK, and thus, we need to note the wallet restoring state here on the client side.
|
||||
*/
|
||||
fun persistWalletRestoringState(walletRestoringState: WalletRestoringState) {
|
||||
val application = getApplication<Application>()
|
||||
|
||||
viewModelScope.launch {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(application)
|
||||
StandardPreferenceKeys.WALLET_RESTORING_STATE.putValue(preferenceProvider, walletRestoringState.toNumber())
|
||||
StandardPreferenceKeys.WALLET_RESTORING_STATE.putValue(
|
||||
standardPreferenceProvider(),
|
||||
walletRestoringState.toNumber()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,16 +586,12 @@ class WalletViewModel(
|
|||
|
||||
private fun clearAppStateFlow(): Flow<Boolean> =
|
||||
callbackFlow {
|
||||
val application = getApplication<Application>()
|
||||
|
||||
viewModelScope.launch {
|
||||
val standardPrefsCleared =
|
||||
StandardPreferenceSingleton
|
||||
.getInstance(application)
|
||||
standardPreferenceProvider()
|
||||
.clearPreferences()
|
||||
val encryptedPrefsCleared =
|
||||
EncryptedPreferenceSingleton
|
||||
.getInstance(application)
|
||||
encryptedPreferenceProvider()
|
||||
.clearPreferences()
|
||||
|
||||
Twig.info { "Both preferences cleared: ${standardPrefsCleared && encryptedPrefsCleared}" }
|
||||
|
@ -650,8 +643,7 @@ class WalletViewModel(
|
|||
|
||||
private fun nullableBooleanStateFlow(default: NullableBooleanPreferenceDefault): StateFlow<Boolean?> =
|
||||
flow {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
emitAll(default.observe(preferenceProvider))
|
||||
emitAll(default.observe(standardPreferenceProvider()))
|
||||
}.stateIn(
|
||||
viewModelScope,
|
||||
SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT),
|
||||
|
@ -663,8 +655,7 @@ class WalletViewModel(
|
|||
newState: Boolean
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
default.putValue(prefs, newState)
|
||||
default.putValue(standardPreferenceProvider(), newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.preference
|
||||
|
||||
import co.electriccoin.zcash.preference.model.entry.PreferenceKey
|
||||
|
||||
object EncryptedPreferenceKeys {
|
||||
val PERSISTABLE_WALLET = PersistableWalletPreferenceDefault(PreferenceKey("persistable_wallet"))
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.preference
|
||||
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.preference.AndroidPreferenceProvider
|
||||
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<Context, PreferenceProvider> {
|
||||
AndroidPreferenceProvider.newEncrypted(it, PREF_FILENAME)
|
||||
}
|
||||
|
||||
suspend fun getInstance(context: Context) = lazy.getInstance(context)
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package co.electriccoin.zcash.ui.preference
|
||||
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.preference.AndroidPreferenceProvider
|
||||
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<Context, PreferenceProvider> {
|
||||
AndroidPreferenceProvider.newStandard(it, PREF_FILENAME)
|
||||
}
|
||||
|
||||
suspend fun getInstance(context: Context) = lazy.getInstance(context)
|
||||
}
|
|
@ -4,14 +4,14 @@ package co.electriccoin.zcash.ui.screen.about
|
|||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
|
||||
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.model.VersionInfo
|
||||
|
@ -21,6 +21,7 @@ import co.electriccoin.zcash.ui.screen.about.view.About
|
|||
import co.electriccoin.zcash.ui.screen.support.model.ConfigInfo
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@Composable
|
||||
internal fun WrapAbout(
|
||||
|
@ -29,7 +30,7 @@ internal fun WrapAbout(
|
|||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
|
||||
|
||||
|
@ -37,12 +38,13 @@ internal fun WrapAbout(
|
|||
goBack()
|
||||
}
|
||||
|
||||
val configInfo = ConfigInfo.new(AndroidConfigurationFactory.getInstance(activity.applicationContext))
|
||||
val androidConfigurationProvider = koinInject<ConfigurationProvider>()
|
||||
val configInfo = ConfigInfo.new(androidConfigurationProvider)
|
||||
val versionInfo = VersionInfo.new(activity.applicationContext)
|
||||
|
||||
// Allows an implicit way to force configuration refresh by simply visiting the About screen
|
||||
LaunchedEffect(key1 = true) {
|
||||
AndroidConfigurationFactory.getInstance(activity.applicationContext).hintToRefresh()
|
||||
androidConfigurationProvider.hintToRefresh()
|
||||
}
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.account
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -13,6 +12,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.BalanceState
|
||||
|
@ -40,11 +40,11 @@ internal fun WrapAccount(
|
|||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val transactionHistoryViewModel by activity.viewModels<TransactionHistoryViewModel>()
|
||||
val transactionHistoryViewModel = koinActivityViewModel<TransactionHistoryViewModel>()
|
||||
|
||||
val homeViewModel by activity.viewModels<HomeViewModel>()
|
||||
val homeViewModel = koinActivityViewModel<HomeViewModel>()
|
||||
|
||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package co.electriccoin.zcash.ui.screen.account.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.model.FirstClassByteArray
|
||||
import cash.z.ecc.android.sdk.model.TransactionOverview
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.screen.account.ext.TransactionOverviewExt
|
||||
import co.electriccoin.zcash.ui.screen.account.model.TransactionUi
|
||||
import co.electriccoin.zcash.ui.screen.account.model.TransactionUiState
|
||||
|
@ -24,7 +23,7 @@ import kotlinx.coroutines.flow.combine
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.toList
|
||||
|
||||
class TransactionHistoryViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class TransactionHistoryViewModel : ViewModel() {
|
||||
private val state: MutableStateFlow<State> = MutableStateFlow(State.LOADING)
|
||||
|
||||
private val transactions: MutableStateFlow<ImmutableList<TransactionUi>> = MutableStateFlow(persistentListOf())
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
package co.electriccoin.zcash.ui.screen.advancedsettings
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
|
@ -21,7 +21,7 @@ internal fun MainActivity.WrapAdvancedSettings(
|
|||
goSeedRecovery: () -> Unit,
|
||||
onCurrencyConversion: () -> Unit
|
||||
) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
package co.electriccoin.zcash.ui.screen.authentication
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
|
@ -115,7 +115,7 @@ private fun WrapDeleteWalletAuth(
|
|||
onCancel: () -> Unit,
|
||||
onFailed: () -> Unit,
|
||||
) {
|
||||
val authenticationViewModel by activity.viewModels<AuthenticationViewModel>()
|
||||
val authenticationViewModel = koinActivityViewModel<AuthenticationViewModel>()
|
||||
|
||||
val authenticationResult =
|
||||
authenticationViewModel.authenticationResult
|
||||
|
@ -188,7 +188,7 @@ private fun WrapAppExportPrivateDataAuth(
|
|||
onCancel: () -> Unit,
|
||||
onFailed: () -> Unit,
|
||||
) {
|
||||
val authenticationViewModel by activity.viewModels<AuthenticationViewModel>()
|
||||
val authenticationViewModel = koinActivityViewModel<AuthenticationViewModel>()
|
||||
|
||||
val authenticationResult =
|
||||
authenticationViewModel.authenticationResult
|
||||
|
@ -261,7 +261,7 @@ private fun WrapSeedRecoveryAuth(
|
|||
onCancel: () -> Unit,
|
||||
onFailed: () -> Unit,
|
||||
) {
|
||||
val authenticationViewModel by activity.viewModels<AuthenticationViewModel>()
|
||||
val authenticationViewModel = koinActivityViewModel<AuthenticationViewModel>()
|
||||
|
||||
val authenticationResult =
|
||||
authenticationViewModel.authenticationResult
|
||||
|
@ -335,7 +335,7 @@ private fun WrapSendFundsAuth(
|
|||
onCancel: () -> Unit,
|
||||
onFailed: () -> Unit,
|
||||
) {
|
||||
val authenticationViewModel by activity.viewModels<AuthenticationViewModel>()
|
||||
val authenticationViewModel = koinActivityViewModel<AuthenticationViewModel>()
|
||||
|
||||
val authenticationResult =
|
||||
authenticationViewModel.authenticationResult
|
||||
|
@ -409,7 +409,7 @@ private fun WrapAppAccessAuth(
|
|||
onCancel: () -> Unit,
|
||||
onFailed: () -> Unit,
|
||||
) {
|
||||
val authenticationViewModel by activity.viewModels<AuthenticationViewModel>()
|
||||
val authenticationViewModel = koinActivityViewModel<AuthenticationViewModel>()
|
||||
|
||||
val welcomeAnimVisibility = authenticationViewModel.showWelcomeAnimation.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ package co.electriccoin.zcash.ui.screen.balances
|
|||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -18,6 +17,7 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
|
|||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.BalanceState
|
||||
|
@ -36,7 +36,6 @@ import co.electriccoin.zcash.ui.screen.balances.model.StatusAction
|
|||
import co.electriccoin.zcash.ui.screen.balances.view.Balances
|
||||
import co.electriccoin.zcash.ui.screen.sendconfirmation.model.SubmitResult
|
||||
import co.electriccoin.zcash.ui.screen.sendconfirmation.viewmodel.CreateTransactionsViewModel
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateCheckerImp
|
||||
import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
||||
import co.electriccoin.zcash.ui.util.PlayStoreUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -51,11 +50,11 @@ internal fun WrapBalances(
|
|||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val createTransactionsViewModel by activity.viewModels<CreateTransactionsViewModel>()
|
||||
val createTransactionsViewModel = koinActivityViewModel<CreateTransactionsViewModel>()
|
||||
|
||||
val homeViewModel by activity.viewModels<HomeViewModel>()
|
||||
val homeViewModel = koinActivityViewModel<HomeViewModel>()
|
||||
|
||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||
|
||||
|
@ -69,12 +68,7 @@ internal fun WrapBalances(
|
|||
|
||||
val isHideBalances = homeViewModel.isHideBalances.collectAsStateWithLifecycle().value ?: false
|
||||
|
||||
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||
CheckUpdateViewModel.CheckUpdateViewModelFactory(
|
||||
activity.application,
|
||||
AppUpdateCheckerImp.new()
|
||||
)
|
||||
}
|
||||
val checkUpdateViewModel = koinActivityViewModel<CheckUpdateViewModel>()
|
||||
|
||||
val balanceState = walletViewModel.balanceState.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.chooseserver
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
@ -18,6 +17,7 @@ import cash.z.ecc.android.sdk.model.PersistableWallet
|
|||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.type.ServerValidation
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.SecretState
|
||||
|
@ -28,7 +28,7 @@ import kotlinx.coroutines.launch
|
|||
|
||||
@Composable
|
||||
internal fun MainActivity.WrapChooseServer(goBack: () -> Unit) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val secretState = walletViewModel.secretState.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ package co.electriccoin.zcash.ui.screen.deletewallet
|
|||
|
||||
import android.app.Activity
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
|
@ -18,7 +18,7 @@ import kotlinx.coroutines.launch
|
|||
|
||||
@Composable
|
||||
internal fun MainActivity.WrapDeleteWallet(goBack: () -> Unit) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui.screen.exportdata
|
|||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
|
@ -12,7 +11,7 @@ import cash.z.ecc.android.sdk.SdkSynchronizer
|
|||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
|
@ -27,11 +26,11 @@ import kotlinx.coroutines.flow.callbackFlow
|
|||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
internal fun MainActivity.WrapExportPrivateData(
|
||||
internal fun WrapExportPrivateData(
|
||||
goBack: () -> Unit,
|
||||
onConfirm: () -> Unit
|
||||
) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.home
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -19,6 +18,7 @@ import androidx.compose.ui.platform.LocalFocusManager
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import cash.z.ecc.android.sdk.model.ZecSend
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.compose.RestoreScreenBrightness
|
||||
|
@ -47,11 +47,9 @@ internal fun WrapHome(
|
|||
goSendConfirmation: (ZecSend) -> Unit,
|
||||
sendArguments: SendArguments
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
val homeViewModel = koinActivityViewModel<HomeViewModel>()
|
||||
|
||||
val homeViewModel by activity.viewModels<HomeViewModel>()
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val isKeepScreenOnWhileSyncing = homeViewModel.isKeepScreenOnWhileSyncing.collectAsStateWithLifecycle().value
|
||||
|
||||
|
@ -117,7 +115,7 @@ internal fun WrapHome(
|
|||
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.onboarding
|
||||
|
||||
import android.content.Context
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import cash.z.ecc.android.sdk.WalletInitMode
|
||||
|
@ -13,6 +12,7 @@ import cash.z.ecc.android.sdk.model.PersistableWallet
|
|||
import cash.z.ecc.android.sdk.model.SeedPhrase
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.FirebaseTestLabUtil
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.model.OnboardingState
|
||||
|
@ -28,8 +28,8 @@ import co.electriccoin.zcash.ui.screen.restore.WrapRestore
|
|||
@Composable
|
||||
internal fun WrapOnboarding() {
|
||||
val activity = LocalActivity.current
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val onboardingViewModel by activity.viewModels<OnboardingViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
val onboardingViewModel = koinActivityViewModel<OnboardingViewModel>()
|
||||
|
||||
val versionInfo = VersionInfo.new(activity.applicationContext)
|
||||
|
||||
|
@ -75,7 +75,7 @@ internal fun WrapOnboarding() {
|
|||
|
||||
activity.reportFullyDrawn()
|
||||
} else {
|
||||
WrapRestore(activity)
|
||||
WrapRestore()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ package co.electriccoin.zcash.ui.screen.receive
|
|||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.graphics.asAndroidBitmap
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.spackle.getInternalCacheDirSuspend
|
||||
|
@ -34,9 +34,9 @@ import java.io.File
|
|||
internal fun WrapReceive(onSettings: () -> Unit) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val brightnessViewModel by activity.viewModels<ScreenBrightnessViewModel>()
|
||||
val brightnessViewModel = koinActivityViewModel<ScreenBrightnessViewModel>()
|
||||
|
||||
val screenBrightnessState = brightnessViewModel.screenBrightnessState.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package co.electriccoin.zcash.ui.screen.restore
|
||||
|
||||
import android.content.ClipboardManager
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import cash.z.ecc.android.sdk.model.SeedPhrase
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.persistExistingWalletWithSeedPhrase
|
||||
import co.electriccoin.zcash.ui.screen.onboarding.viewmodel.OnboardingViewModel
|
||||
|
@ -17,10 +16,10 @@ import co.electriccoin.zcash.ui.screen.restore.viewmodel.CompleteWordSetState
|
|||
import co.electriccoin.zcash.ui.screen.restore.viewmodel.RestoreViewModel
|
||||
|
||||
@Composable
|
||||
fun WrapRestore(activity: ComponentActivity) {
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val onboardingViewModel by activity.viewModels<OnboardingViewModel>()
|
||||
val restoreViewModel by activity.viewModels<RestoreViewModel>()
|
||||
fun WrapRestore() {
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
val onboardingViewModel = koinActivityViewModel<OnboardingViewModel>()
|
||||
val restoreViewModel = koinActivityViewModel<RestoreViewModel>()
|
||||
|
||||
val applicationContext = LocalContext.current.applicationContext
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import cash.z.ecc.android.bip39.Mnemonics
|
|||
import cash.z.ecc.android.sdk.ext.collectWith
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import cash.z.ecc.sdk.type.fromResources
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.screen.restore.model.RestoreStage
|
||||
import co.electriccoin.zcash.ui.screen.restore.state.RestoreState
|
||||
import co.electriccoin.zcash.ui.screen.restore.state.WordList
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
package co.electriccoin.zcash.ui.screen.restoresuccess
|
||||
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restoresuccess.view.RestoreSuccess
|
||||
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
|
||||
|
||||
@Composable
|
||||
fun WrapRestoreSuccess(onDone: () -> Unit) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val viewModel by activity.viewModels<RestoreSuccessViewModel>()
|
||||
val viewModel = koinActivityViewModel<RestoreSuccessViewModel>()
|
||||
|
||||
val state = viewModel.state.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ package co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel
|
|||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
|
||||
import co.electriccoin.zcash.ui.screen.restoresuccess.view.RestoreSuccessViewState
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
@ -16,7 +16,10 @@ import kotlinx.coroutines.flow.stateIn
|
|||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class RestoreSuccessViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class RestoreSuccessViewModel(
|
||||
application: Application,
|
||||
private val standardPreferenceProvider: StandardPreferenceProvider,
|
||||
) : AndroidViewModel(application) {
|
||||
private val keepScreenOn = MutableStateFlow(DEFAULT_KEEP_SCREEN_ON)
|
||||
|
||||
val state =
|
||||
|
@ -45,8 +48,7 @@ class RestoreSuccessViewModel(application: Application) : AndroidViewModel(appli
|
|||
default: BooleanPreferenceDefault,
|
||||
newState: Boolean
|
||||
) = viewModelScope.launch {
|
||||
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
default.putValue(prefs, newState)
|
||||
default.putValue(standardPreferenceProvider(), newState)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.scan
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
@ -13,6 +12,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.type.AddressType
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.SerializableAddress
|
||||
|
@ -28,7 +28,7 @@ internal fun MainActivity.WrapScanValidator(
|
|||
onScanValid: (address: SerializableAddress) -> Unit,
|
||||
goBack: () -> Unit
|
||||
) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -3,10 +3,11 @@ package co.electriccoin.zcash.ui.screen.securitywarning
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
|
||||
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.model.VersionInfo
|
||||
import co.electriccoin.zcash.ui.screen.securitywarning.view.SecurityWarning
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@Composable
|
||||
internal fun WrapSecurityWarning(
|
||||
|
@ -14,7 +15,7 @@ internal fun WrapSecurityWarning(
|
|||
onConfirm: () -> Unit
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val androidConfigurationProvider = koinInject<ConfigurationProvider>()
|
||||
BackHandler {
|
||||
onBack()
|
||||
}
|
||||
|
@ -29,6 +30,6 @@ internal fun WrapSecurityWarning(
|
|||
)
|
||||
|
||||
LaunchedEffect(key1 = true) {
|
||||
AndroidConfigurationFactory.getInstance(activity.applicationContext).hintToRefresh()
|
||||
androidConfigurationProvider.hintToRefresh()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package co.electriccoin.zcash.ui.screen.seedrecovery
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.ClipboardManagerUtil
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
|
@ -20,9 +20,7 @@ internal fun WrapSeedRecovery(
|
|||
goBack: () -> Unit,
|
||||
onDone: () -> Unit,
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.send
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
@ -19,6 +18,7 @@ import cash.z.ecc.android.sdk.model.ZecSend
|
|||
import cash.z.ecc.android.sdk.model.proposeSend
|
||||
import cash.z.ecc.android.sdk.model.toZecString
|
||||
import cash.z.ecc.android.sdk.type.AddressType
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.common.compose.BalanceState
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
|
@ -50,9 +50,9 @@ internal fun WrapSend(
|
|||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val homeViewModel by activity.viewModels<HomeViewModel>()
|
||||
val homeViewModel = koinActivityViewModel<HomeViewModel>()
|
||||
|
||||
val hasCameraFeature = activity.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ package co.electriccoin.zcash.ui.screen.sendconfirmation
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -23,6 +22,7 @@ import cash.z.ecc.android.sdk.model.Proposal
|
|||
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.ZecSend
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
|
@ -57,15 +57,13 @@ internal fun MainActivity.WrapSendConfirmation(
|
|||
goSupport: () -> Unit,
|
||||
arguments: SendConfirmationArguments
|
||||
) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val createTransactionsViewModel by viewModels<CreateTransactionsViewModel>()
|
||||
val createTransactionsViewModel = koinActivityViewModel<CreateTransactionsViewModel>()
|
||||
|
||||
val supportViewModel by viewModels<SupportViewModel>()
|
||||
val supportViewModel = koinActivityViewModel<SupportViewModel>()
|
||||
|
||||
val authenticationViewModel by viewModels<AuthenticationViewModel> {
|
||||
AuthenticationViewModel.AuthenticationViewModelFactory(application)
|
||||
}
|
||||
val authenticationViewModel = koinActivityViewModel<AuthenticationViewModel>()
|
||||
|
||||
val synchronizer = walletViewModel.synchronizer.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package co.electriccoin.zcash.ui.screen.sendconfirmation.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.model.Proposal
|
||||
import cash.z.ecc.android.sdk.model.TransactionSubmitResult
|
||||
|
@ -10,7 +9,7 @@ import co.electriccoin.zcash.spackle.Twig
|
|||
import co.electriccoin.zcash.ui.screen.sendconfirmation.model.SubmitResult
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class CreateTransactionsViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class CreateTransactionsViewModel : ViewModel() {
|
||||
// Technically this value will not survive process dead, but will survive all possible configuration changes
|
||||
// Possible solution would be storing the value within [SavedStateHandle]
|
||||
val submissions: MutableStateFlow<List<TransactionSubmitResult>> = MutableStateFlow(emptyList())
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package co.electriccoin.zcash.ui.screen.settings
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.model.VersionInfo
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
|
@ -23,9 +23,9 @@ internal fun WrapSettings(
|
|||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val settingsViewModel by activity.viewModels<SettingsViewModel>()
|
||||
val settingsViewModel = koinActivityViewModel<SettingsViewModel>()
|
||||
|
||||
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package co.electriccoin.zcash.ui.screen.settings.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.preference.StandardPreferenceProvider
|
||||
import co.electriccoin.zcash.preference.model.entry.BooleanPreferenceDefault
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceKeys
|
||||
import co.electriccoin.zcash.ui.preference.StandardPreferenceSingleton
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
|
@ -15,7 +14,9 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class SettingsViewModel(
|
||||
private val standardPreferenceProvider: StandardPreferenceProvider,
|
||||
) : ViewModel() {
|
||||
val isAnalyticsEnabled: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_ANALYTICS_ENABLED)
|
||||
|
||||
val isBackgroundSync: StateFlow<Boolean?> = booleanStateFlow(StandardPreferenceKeys.IS_BACKGROUND_SYNC_ENABLED)
|
||||
|
@ -25,8 +26,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
|||
|
||||
private fun booleanStateFlow(default: BooleanPreferenceDefault): StateFlow<Boolean?> =
|
||||
flow<Boolean?> {
|
||||
val preferenceProvider = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
emitAll(default.observe(preferenceProvider))
|
||||
emitAll(default.observe(standardPreferenceProvider()))
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)
|
||||
|
||||
fun setAnalyticsEnabled(enabled: Boolean) {
|
||||
|
@ -46,8 +46,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
|||
newState: Boolean
|
||||
) {
|
||||
viewModelScope.launch {
|
||||
val prefs = StandardPreferenceSingleton.getInstance(getApplication())
|
||||
default.putValue(prefs, newState)
|
||||
default.putValue(standardPreferenceProvider(), newState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ package co.electriccoin.zcash.ui.screen.support
|
|||
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -13,6 +12,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
|
@ -26,11 +26,9 @@ import kotlinx.coroutines.launch
|
|||
|
||||
@Composable
|
||||
internal fun WrapSupport(goBack: () -> Unit) {
|
||||
val activity = LocalActivity.current
|
||||
val supportViewModel = koinActivityViewModel<SupportViewModel>()
|
||||
|
||||
val supportViewModel by activity.viewModels<SupportViewModel>()
|
||||
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val supportInfo = supportViewModel.supportInfo.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package co.electriccoin.zcash.ui.screen.support.model
|
||||
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.configuration.AndroidConfigurationFactory
|
||||
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
|
||||
import co.electriccoin.zcash.spackle.getPackageInfoCompatSuspend
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
@ -63,15 +63,17 @@ data class SupportInfo(
|
|||
companion object {
|
||||
// Although most of our calls now are non-blocking, we expect more of them to be blocking
|
||||
// in the future.
|
||||
suspend fun new(context: Context): SupportInfo {
|
||||
suspend fun new(
|
||||
context: Context,
|
||||
androidConfigurationProvider: ConfigurationProvider
|
||||
): SupportInfo {
|
||||
val applicationContext = context.applicationContext
|
||||
val packageInfo = applicationContext.packageManager.getPackageInfoCompatSuspend(context.packageName, 0L)
|
||||
val configurationProvider = AndroidConfigurationFactory.getInstance(applicationContext)
|
||||
|
||||
return SupportInfo(
|
||||
TimeInfo.new(packageInfo),
|
||||
AppInfo.new(packageInfo),
|
||||
ConfigInfo.new(configurationProvider),
|
||||
ConfigInfo.new(androidConfigurationProvider),
|
||||
OperatingSystemInfo.new(),
|
||||
DeviceInfo.new(),
|
||||
EnvironmentInfo.new(applicationContext),
|
||||
|
|
|
@ -3,7 +3,8 @@ package co.electriccoin.zcash.ui.screen.support.viewmodel
|
|||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.configuration.api.ConfigurationProvider
|
||||
import co.electriccoin.zcash.ui.screen.support.model.SupportInfo
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -12,13 +13,16 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlin.time.Duration
|
||||
|
||||
class SupportViewModel(application: Application) : AndroidViewModel(application) {
|
||||
class SupportViewModel(
|
||||
application: Application,
|
||||
androidConfigurationProvider: ConfigurationProvider
|
||||
) : 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<SupportInfo?> =
|
||||
flow<SupportInfo?> {
|
||||
emit(SupportInfo.new(application))
|
||||
emit(SupportInfo.new(application, androidConfigurationProvider))
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT, Duration.ZERO), null)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package co.electriccoin.zcash.ui.screen.update
|
|||
|
||||
import android.content.Context
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
|
@ -10,6 +9,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.CheckUpdateViewModel
|
||||
|
@ -20,29 +20,36 @@ import co.electriccoin.zcash.ui.screen.update.viewmodel.UpdateViewModel
|
|||
import co.electriccoin.zcash.ui.util.PlayStoreUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.core.parameter.parametersOf
|
||||
|
||||
@Composable
|
||||
internal fun WrapCheckForUpdate() {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
// TODO [#403]: Manual testing of already implemented in-app update mechanisms
|
||||
// TODO [#403]: https://github.com/Electric-Coin-Company/zashi-android/issues/403
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||
val checkUpdateViewModel by activity.viewModels<CheckUpdateViewModel> {
|
||||
CheckUpdateViewModel.CheckUpdateViewModelFactory(
|
||||
activity.application,
|
||||
AppUpdateCheckerImp.new()
|
||||
val checkUpdateViewModel = koinActivityViewModel<CheckUpdateViewModel>()
|
||||
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val inputUpdateInfo = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value ?: return
|
||||
|
||||
val viewModel = koinActivityViewModel<UpdateViewModel> { parametersOf(inputUpdateInfo) }
|
||||
val updateInfo = viewModel.updateInfo.collectAsStateWithLifecycle().value
|
||||
|
||||
if (updateInfo.appUpdateInfo != null && updateInfo.state == UpdateState.Prepared) {
|
||||
WrapUpdate(
|
||||
updateInfo = updateInfo,
|
||||
checkForUpdate = viewModel::checkForAppUpdate,
|
||||
remindLater = viewModel::remindLater,
|
||||
goForUpdate = {
|
||||
viewModel.goForUpdate(
|
||||
activity = activity,
|
||||
appUpdateInfo = updateInfo.appUpdateInfo
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val updateInfo = checkUpdateViewModel.updateInfo.collectAsStateWithLifecycle().value
|
||||
|
||||
updateInfo?.let {
|
||||
if (it.appUpdateInfo != null && it.state == UpdateState.Prepared) {
|
||||
WrapUpdate(updateInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for an app update asynchronously. We create an effect that matches the activity
|
||||
// lifecycle. If the wrapping compose recomposes, the check shouldn't run again.
|
||||
LaunchedEffect(true) {
|
||||
|
@ -52,22 +59,17 @@ internal fun WrapCheckForUpdate() {
|
|||
|
||||
@VisibleForTesting
|
||||
@Composable
|
||||
internal fun WrapUpdate(inputUpdateInfo: UpdateInfo) {
|
||||
internal fun WrapUpdate(
|
||||
updateInfo: UpdateInfo,
|
||||
checkForUpdate: () -> Unit,
|
||||
remindLater: () -> Unit,
|
||||
goForUpdate: () -> Unit,
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
||||
val viewModel by activity.viewModels<UpdateViewModel> {
|
||||
UpdateViewModel.UpdateViewModelFactory(
|
||||
activity.application,
|
||||
inputUpdateInfo,
|
||||
AppUpdateCheckerImp.new()
|
||||
)
|
||||
}
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val updateInfo = viewModel.updateInfo.collectAsStateWithLifecycle().value
|
||||
|
||||
when (updateInfo.state) {
|
||||
UpdateState.Done, UpdateState.Canceled -> {
|
||||
// just return as we are already in Home compose
|
||||
|
@ -76,7 +78,7 @@ internal fun WrapUpdate(inputUpdateInfo: UpdateInfo) {
|
|||
|
||||
UpdateState.Failed -> {
|
||||
// we need to refresh AppUpdateInfo object, as it can be used only once
|
||||
viewModel.checkForAppUpdate()
|
||||
checkForUpdate()
|
||||
}
|
||||
|
||||
UpdateState.Prepared, UpdateState.Running -> {
|
||||
|
@ -86,7 +88,7 @@ internal fun WrapUpdate(inputUpdateInfo: UpdateInfo) {
|
|||
|
||||
val onLaterAction = {
|
||||
if (!updateInfo.isForce && updateInfo.state != UpdateState.Running) {
|
||||
viewModel.remindLater()
|
||||
remindLater()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,11 +102,7 @@ internal fun WrapUpdate(inputUpdateInfo: UpdateInfo) {
|
|||
onDownload = {
|
||||
// in this state of the update we have the AppUpdateInfo filled
|
||||
requireNotNull(updateInfo.appUpdateInfo)
|
||||
|
||||
viewModel.goForUpdate(
|
||||
activity,
|
||||
updateInfo.appUpdateInfo
|
||||
)
|
||||
goForUpdate()
|
||||
},
|
||||
onLater = onLaterAction,
|
||||
onReference = {
|
||||
|
|
|
@ -15,13 +15,7 @@ import kotlinx.coroutines.channels.awaitClose
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
|
||||
class AppUpdateCheckerImp private constructor() : AppUpdateChecker {
|
||||
companion object {
|
||||
private const val DEFAULT_STALENESS_DAYS = 3
|
||||
|
||||
fun new() = AppUpdateCheckerImp()
|
||||
}
|
||||
|
||||
class AppUpdateCheckerImpl : AppUpdateChecker {
|
||||
override val stalenessDays = DEFAULT_STALENESS_DAYS
|
||||
|
||||
/**
|
||||
|
@ -133,3 +127,5 @@ class AppUpdateCheckerImp private constructor() : AppUpdateChecker {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_STALENESS_DAYS = 3
|
|
@ -4,8 +4,6 @@ import android.app.Activity
|
|||
import android.app.Application
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.android.sdk.ext.onFirst
|
||||
import co.electriccoin.zcash.ui.screen.update.AppUpdateChecker
|
||||
|
@ -14,6 +12,7 @@ import co.electriccoin.zcash.ui.screen.update.model.UpdateState
|
|||
import com.google.android.play.core.appupdate.AppUpdateInfo
|
||||
import com.google.android.play.core.install.model.ActivityResult
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class UpdateViewModel(
|
||||
|
@ -62,21 +61,6 @@ class UpdateViewModel(
|
|||
|
||||
fun remindLater() {
|
||||
// for mvp we just return user back to the previous screen
|
||||
updateInfo.value = updateInfo.value.copy(state = UpdateState.Canceled)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class UpdateViewModelFactory(
|
||||
private val application: Application,
|
||||
private val updateInfo: UpdateInfo,
|
||||
private val appUpdateChecker: AppUpdateChecker
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return if (modelClass.isAssignableFrom(UpdateViewModel::class.java)) {
|
||||
UpdateViewModel(application, updateInfo, appUpdateChecker) as T
|
||||
} else {
|
||||
throw IllegalArgumentException("ViewModel Not Found.")
|
||||
}
|
||||
}
|
||||
updateInfo.update { it.copy(state = UpdateState.Canceled) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
package co.electriccoin.zcash.ui.screen.warning
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.MainActivity
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState
|
||||
|
@ -24,9 +24,9 @@ fun MainActivity.WrapNotEnoughSpace(
|
|||
goPrevious: () -> Unit,
|
||||
goSettings: () -> Unit
|
||||
) {
|
||||
val walletViewModel by viewModels<WalletViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
|
||||
val storageCheckViewModel by viewModels<StorageCheckViewModel>()
|
||||
val storageCheckViewModel = koinActivityViewModel<StorageCheckViewModel>()
|
||||
|
||||
val walletState = walletViewModel.walletStateInformation.collectAsStateWithLifecycle().value
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package co.electriccoin.zcash.ui.screen.warning.viewmodel
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.global.StorageChecker
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package co.electriccoin.zcash.ui.screen.whatsnew
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.di.koinActivityViewModel
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.screen.whatsnew.view.WhatsNewView
|
||||
|
@ -13,10 +12,9 @@ import co.electriccoin.zcash.ui.screen.whatsnew.viewmodel.WhatsNewViewModel
|
|||
|
||||
@Composable
|
||||
fun WrapWhatsNew() {
|
||||
val activity = LocalActivity.current
|
||||
val navController = LocalNavController.current
|
||||
val viewModel by activity.viewModels<WhatsNewViewModel>()
|
||||
val walletViewModel by activity.viewModels<WalletViewModel>()
|
||||
val viewModel = koinActivityViewModel<WhatsNewViewModel>()
|
||||
val walletViewModel = koinActivityViewModel<WalletViewModel>()
|
||||
val walletState by walletViewModel.walletStateInformation.collectAsStateWithLifecycle()
|
||||
val state = viewModel.state.collectAsStateWithLifecycle().value ?: return
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package co.electriccoin.zcash.ui.screen.whatsnew.viewmodel
|
|||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import co.electriccoin.zcash.ui.common.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.common.model.VersionInfo
|
||||
import co.electriccoin.zcash.ui.screen.whatsnew.model.WhatsNewState
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
|
|
|
@ -9,10 +9,9 @@ import androidx.work.PeriodicWorkRequest
|
|||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkerParameters
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.WalletCoordinator
|
||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
import co.electriccoin.zcash.global.getInstance
|
||||
import co.electriccoin.zcash.spackle.Twig
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveSynchronizerUseCase
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
@ -27,6 +26,8 @@ import kotlinx.datetime.atTime
|
|||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import kotlinx.datetime.until
|
||||
import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.inject
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
@ -38,14 +39,18 @@ import kotlin.time.toJavaDuration
|
|||
|
||||
// TODO [#1249]: Add documentation and tests on background syncing
|
||||
// TODO [#1249]: https://github.com/Electric-Coin-Company/zashi-android/issues/1249
|
||||
|
||||
@Keep
|
||||
class SyncWorker(context: Context, workerParameters: WorkerParameters) : CoroutineWorker(context, workerParameters) {
|
||||
class SyncWorker(
|
||||
context: Context,
|
||||
workerParameters: WorkerParameters
|
||||
) : CoroutineWorker(context, workerParameters), KoinComponent {
|
||||
private val observeSynchronizer: ObserveSynchronizerUseCase by inject()
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override suspend fun doWork(): Result {
|
||||
Twig.debug { "BG Sync: starting..." }
|
||||
|
||||
WalletCoordinator.getInstance(applicationContext).synchronizer
|
||||
observeSynchronizer()
|
||||
.flatMapLatest {
|
||||
Twig.debug { "BG Sync: synchronizer: $it" }
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ android {
|
|||
testInstrumentationRunnerArguments["clearPackageData"] = "true"
|
||||
}
|
||||
|
||||
testInstrumentationRunner = "co.electriccoin.zcash.test.ZcashUiTestRunner"
|
||||
testInstrumentationRunner = "co.electroniccoin.zcash.ui.screenshot.ZcashScreenshotTestRunner"
|
||||
}
|
||||
|
||||
// Define the same flavors as in app module
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package co.electroniccoin.zcash.ui.screenshot
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import co.electriccoin.zcash.di.coreModule
|
||||
import co.electriccoin.zcash.di.repositoryModule
|
||||
import co.electriccoin.zcash.di.useCaseModule
|
||||
import co.electriccoin.zcash.di.viewModelModule
|
||||
import co.electriccoin.zcash.test.ZcashUiTestRunner
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.android.ext.koin.androidLogger
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
class ZcashScreenshotTestRunner : ZcashUiTestRunner() {
|
||||
override fun newApplication(
|
||||
cl: ClassLoader?,
|
||||
className: String?,
|
||||
context: Context?
|
||||
): Application {
|
||||
return super.newApplication(cl, ZcashUiTestApplication::class.java.name, context)
|
||||
}
|
||||
}
|
||||
|
||||
class ZcashUiTestApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
startKoin {
|
||||
androidLogger()
|
||||
androidContext(this@ZcashUiTestApplication)
|
||||
modules(
|
||||
coreModule,
|
||||
repositoryModule,
|
||||
useCaseModule,
|
||||
viewModelModule
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue