mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-22 05:38:02 +00:00
Initial commit: Pezkuwi Wallet Android
Security hardened release: - Code obfuscation enabled (minifyEnabled=true, shrinkResources=true) - Sensitive files excluded (google-services.json, keystores) - Branch.io key moved to BuildConfig placeholder - Updated dependencies: OkHttp 4.12.0, Gson 2.10.1, BouncyCastle 1.77 - Comprehensive ProGuard rules for crypto wallet - Navigation 2.7.7, Lifecycle 2.7.0, ConstraintLayout 2.1.4
This commit is contained in:
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,47 @@
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply from: '../tests.gradle'
|
||||
|
||||
android {
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
namespace 'io.novafoundation.nova.feature_onboarding_impl'
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation project(':core-db')
|
||||
implementation project(':common')
|
||||
implementation project(':feature-onboarding-api')
|
||||
implementation project(':feature-account-api')
|
||||
implementation project(':feature-wallet-api')
|
||||
implementation project(':feature-ledger-core')
|
||||
implementation project(':feature-versions-api')
|
||||
|
||||
implementation project(':feature-cloud-backup-api')
|
||||
|
||||
implementation kotlinDep
|
||||
|
||||
implementation androidDep
|
||||
implementation materialDep
|
||||
implementation constraintDep
|
||||
|
||||
implementation coroutinesDep
|
||||
|
||||
implementation daggerDep
|
||||
ksp daggerCompiler
|
||||
|
||||
implementation lifecycleDep
|
||||
ksp lifecycleCompiler
|
||||
|
||||
testImplementation jUnitDep
|
||||
testImplementation mockitoDep
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest />
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl
|
||||
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.ImportAccountPayload
|
||||
|
||||
interface OnboardingRouter {
|
||||
|
||||
fun openCreateFirstWallet()
|
||||
|
||||
fun openMnemonicScreen(accountName: String?, payload: AddAccountPayload)
|
||||
|
||||
fun openImportAccountScreen(payload: ImportAccountPayload)
|
||||
|
||||
fun openCreateWatchWallet()
|
||||
|
||||
fun openStartImportParitySigner()
|
||||
|
||||
fun openStartImportLegacyLedger()
|
||||
|
||||
fun openStartImportGenericLedger()
|
||||
|
||||
fun back()
|
||||
|
||||
fun openStartImportPolkadotVault()
|
||||
|
||||
fun openImportOptionsScreen()
|
||||
|
||||
fun restoreCloudBackup()
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.di
|
||||
|
||||
import dagger.BindsInstance
|
||||
import dagger.Component
|
||||
import io.novafoundation.nova.common.di.CommonApi
|
||||
import io.novafoundation.nova.common.di.scope.FeatureScope
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.di.CloudBackupFeatureApi
|
||||
import io.novafoundation.nova.feature_ledger_core.di.LedgerCoreApi
|
||||
import io.novafoundation.nova.feature_onboarding_api.di.OnboardingFeatureApi
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser.di.ImportWalletOptionsComponent
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.di.WelcomeComponent
|
||||
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
OnboardingFeatureDependencies::class
|
||||
],
|
||||
modules = [
|
||||
OnboardingFeatureModule::class
|
||||
]
|
||||
)
|
||||
@FeatureScope
|
||||
interface OnboardingFeatureComponent : OnboardingFeatureApi {
|
||||
|
||||
fun welcomeComponentFactory(): WelcomeComponent.Factory
|
||||
|
||||
fun importWalletOptionsComponentFactory(): ImportWalletOptionsComponent.Factory
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
|
||||
fun create(
|
||||
@BindsInstance onboardingRouter: OnboardingRouter,
|
||||
deps: OnboardingFeatureDependencies
|
||||
): OnboardingFeatureComponent
|
||||
}
|
||||
|
||||
@Component(
|
||||
dependencies = [
|
||||
CommonApi::class,
|
||||
AccountFeatureApi::class,
|
||||
VersionsFeatureApi::class,
|
||||
LedgerCoreApi::class,
|
||||
CloudBackupFeatureApi::class
|
||||
]
|
||||
)
|
||||
interface OnboardingFeatureDependenciesComponent : OnboardingFeatureDependencies
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.di
|
||||
|
||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
|
||||
import io.novafoundation.nova.common.mixin.api.CustomDialogDisplayer
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.utils.progress.ProgressDialogMixinFactory
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.mixin.importType.ImportTypeChooserMixin
|
||||
import io.novafoundation.nova.feature_ledger_core.domain.LedgerMigrationTracker
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.domain.CloudBackupService
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.presenter.mixin.CloudBackupChangingWarningMixinFactory
|
||||
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
|
||||
|
||||
interface OnboardingFeatureDependencies {
|
||||
|
||||
fun updateNotificationsInteractor(): UpdateNotificationsInteractor
|
||||
|
||||
fun accountRepository(): AccountRepository
|
||||
|
||||
fun resourceManager(): ResourceManager
|
||||
|
||||
fun appLinksProvider(): AppLinksProvider
|
||||
|
||||
fun importTypeChooserMixin(): ImportTypeChooserMixin.Presentation
|
||||
|
||||
fun progressDialogMixinFactory(): ProgressDialogMixinFactory
|
||||
|
||||
fun customDialogProvider(): CustomDialogDisplayer.Presentation
|
||||
|
||||
val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
|
||||
|
||||
val ledgerMigrationTracker: LedgerMigrationTracker
|
||||
|
||||
val cloudBackupService: CloudBackupService
|
||||
|
||||
val cloudBackupChangingWarningMixinFactory: CloudBackupChangingWarningMixinFactory
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.di
|
||||
|
||||
import io.novafoundation.nova.common.di.FeatureApiHolder
|
||||
import io.novafoundation.nova.common.di.FeatureContainer
|
||||
import io.novafoundation.nova.common.di.scope.ApplicationScope
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.di.CloudBackupFeatureApi
|
||||
import io.novafoundation.nova.feature_ledger_core.di.LedgerCoreApi
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
|
||||
import javax.inject.Inject
|
||||
|
||||
@ApplicationScope
|
||||
class OnboardingFeatureHolder @Inject constructor(
|
||||
featureContainer: FeatureContainer,
|
||||
private val onboardingRouter: OnboardingRouter
|
||||
) : FeatureApiHolder(featureContainer) {
|
||||
|
||||
override fun initializeDependencies(): Any {
|
||||
val onboardingFeatureDependencies = DaggerOnboardingFeatureComponent_OnboardingFeatureDependenciesComponent.builder()
|
||||
.commonApi(commonApi())
|
||||
.versionsFeatureApi(getFeature(VersionsFeatureApi::class.java))
|
||||
.accountFeatureApi(getFeature(AccountFeatureApi::class.java))
|
||||
.cloudBackupFeatureApi(getFeature(CloudBackupFeatureApi::class.java))
|
||||
.ledgerCoreApi(getFeature(LedgerCoreApi::class.java))
|
||||
.build()
|
||||
return DaggerOnboardingFeatureComponent.factory()
|
||||
.create(onboardingRouter, onboardingFeatureDependencies)
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.domain.CloudBackupService
|
||||
import io.novafoundation.nova.feature_onboarding_api.domain.OnboardingInteractor
|
||||
import io.novafoundation.nova.feature_onboarding_impl.domain.OnboardingInteractorImpl
|
||||
|
||||
@Module
|
||||
class OnboardingFeatureModule {
|
||||
|
||||
@Provides
|
||||
fun provideOnboardingInteractor(
|
||||
cloudBackupService: CloudBackupService,
|
||||
accountRepository: AccountRepository
|
||||
): OnboardingInteractor {
|
||||
return OnboardingInteractorImpl(cloudBackupService, accountRepository)
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.domain
|
||||
|
||||
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.domain.CloudBackupService
|
||||
import io.novafoundation.nova.feature_onboarding_api.domain.OnboardingInteractor
|
||||
|
||||
class OnboardingInteractorImpl(
|
||||
private val cloudBackupService: CloudBackupService,
|
||||
private val accountRepository: AccountRepository
|
||||
) : OnboardingInteractor {
|
||||
|
||||
override suspend fun checkCloudBackupIsExist(): Result<Boolean> {
|
||||
return cloudBackupService.isCloudBackupExist()
|
||||
}
|
||||
|
||||
override suspend fun isCloudBackupAvailableForImport(): Boolean {
|
||||
return !cloudBackupService.session.isSyncWithCloudEnabled() &&
|
||||
!accountRepository.hasActiveMetaAccounts()
|
||||
}
|
||||
|
||||
override suspend fun signInToCloud(): Result<Unit> {
|
||||
return cloudBackupService.signInToCloud()
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser
|
||||
|
||||
import io.novafoundation.nova.common.base.BaseFragment
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.mixin.impl.setupCustomDialogDisplayer
|
||||
import io.novafoundation.nova.common.utils.progress.observeProgressDialog
|
||||
import io.novafoundation.nova.common.utils.setVisible
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.presenter.mixin.observeConfirmationAction
|
||||
import io.novafoundation.nova.feature_onboarding_api.di.OnboardingFeatureApi
|
||||
import io.novafoundation.nova.feature_onboarding_impl.databinding.FragmentImportWalletOptionsBinding
|
||||
import io.novafoundation.nova.feature_onboarding_impl.di.OnboardingFeatureComponent
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.SelectHardwareWalletBottomSheet
|
||||
|
||||
class ImportWalletOptionsFragment : BaseFragment<ImportWalletOptionsViewModel, FragmentImportWalletOptionsBinding>() {
|
||||
|
||||
override fun createBinding() = FragmentImportWalletOptionsBinding.inflate(layoutInflater)
|
||||
|
||||
override fun initViews() {
|
||||
binder.importOptionsToolbar.setHomeButtonListener { viewModel.backClicked() }
|
||||
|
||||
binder.importOptionPassphrase.setOnClickListener { viewModel.importMnemonicClicked() }
|
||||
binder.importOptionTrustWallet.setOnClickListener { viewModel.importTrustWalletClicked() }
|
||||
binder.importOptionCloud.setOnClickListener { viewModel.importCloudClicked() }
|
||||
binder.importOptionHardware.setOnClickListener { viewModel.importHardwareClicked() }
|
||||
binder.importOptionWatchOnly.setOnClickListener { viewModel.importWatchOnlyClicked() }
|
||||
binder.importOptionRawSeed.setOnClickListener { viewModel.importRawSeedClicked() }
|
||||
binder.importOptionJson.setOnClickListener { viewModel.importJsonClicked() }
|
||||
}
|
||||
|
||||
override fun inject() {
|
||||
FeatureUtils.getFeature<OnboardingFeatureComponent>(context!!, OnboardingFeatureApi::class.java)
|
||||
.importWalletOptionsComponentFactory()
|
||||
.create(this)
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
override fun subscribe(viewModel: ImportWalletOptionsViewModel) {
|
||||
setupCustomDialogDisplayer(viewModel)
|
||||
observeProgressDialog(viewModel.progressDialogMixin)
|
||||
observeConfirmationAction(viewModel.cloudBackupChangingWarningMixin)
|
||||
|
||||
viewModel.selectHardwareWallet.awaitableActionLiveData.observeEvent {
|
||||
SelectHardwareWalletBottomSheet(requireContext(), it.payload, it.onSuccess)
|
||||
.show()
|
||||
}
|
||||
|
||||
viewModel.showImportViaCloudButton.observe {
|
||||
binder.importOptionCloud.setVisible(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser
|
||||
|
||||
import io.novafoundation.nova.common.base.BaseViewModel
|
||||
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
|
||||
import io.novafoundation.nova.common.mixin.api.CustomDialogDisplayer
|
||||
import io.novafoundation.nova.common.mixin.api.displayDialogOrNothing
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.utils.flowOf
|
||||
import io.novafoundation.nova.common.utils.progress.ProgressDialogMixinFactory
|
||||
import io.novafoundation.nova.common.utils.progress.startProgress
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.PolkadotVaultVariant
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.ImportAccountPayload
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.ImportType
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.ImportType.Mnemonic.Origin
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.presenter.errorHandling.mapCheckBackupAvailableFailureToUi
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.presenter.mixin.CloudBackupChangingWarningMixinFactory
|
||||
import io.novafoundation.nova.feature_ledger_core.domain.LedgerMigrationTracker
|
||||
import io.novafoundation.nova.feature_onboarding_api.domain.OnboardingInteractor
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_onboarding_impl.R
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.SelectHardwareWalletBottomSheet
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.model.HardwareWalletModel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ImportWalletOptionsViewModel(
|
||||
private val resourceManager: ResourceManager,
|
||||
private val router: OnboardingRouter,
|
||||
private val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory,
|
||||
private val onboardingInteractor: OnboardingInteractor,
|
||||
private val progressDialogMixinFactory: ProgressDialogMixinFactory,
|
||||
customDialogProvider: CustomDialogDisplayer.Presentation,
|
||||
cloudBackupChangingWarningMixinFactory: CloudBackupChangingWarningMixinFactory,
|
||||
private val ledgerMigrationTracker: LedgerMigrationTracker,
|
||||
) : BaseViewModel(), CustomDialogDisplayer.Presentation by customDialogProvider {
|
||||
|
||||
val progressDialogMixin = progressDialogMixinFactory.create()
|
||||
|
||||
val cloudBackupChangingWarningMixin = cloudBackupChangingWarningMixinFactory.create(this)
|
||||
|
||||
val selectHardwareWallet = actionAwaitableMixinFactory.create<SelectHardwareWalletBottomSheet.Payload, HardwareWalletModel>()
|
||||
|
||||
val showImportViaCloudButton = flowOf { onboardingInteractor.isCloudBackupAvailableForImport() }
|
||||
.shareInBackground()
|
||||
|
||||
fun backClicked() {
|
||||
router.back()
|
||||
}
|
||||
|
||||
fun importMnemonicClicked() {
|
||||
openImportType(ImportType.Mnemonic())
|
||||
}
|
||||
|
||||
fun importTrustWalletClicked() {
|
||||
openImportType(ImportType.Mnemonic(origin = Origin.TRUST_WALLET))
|
||||
}
|
||||
|
||||
fun importCloudClicked() = launch {
|
||||
progressDialogMixin.startProgress(R.string.loocking_backup_progress) {
|
||||
onboardingInteractor.checkCloudBackupIsExist()
|
||||
.onSuccess { isCloudBackupExist ->
|
||||
if (isCloudBackupExist) {
|
||||
router.restoreCloudBackup()
|
||||
} else {
|
||||
showBackupNotFoundError()
|
||||
}
|
||||
}.onFailure {
|
||||
val payload = mapCheckBackupAvailableFailureToUi(resourceManager, it, ::initSignIn)
|
||||
displayDialogOrNothing(payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun importHardwareClicked() {
|
||||
cloudBackupChangingWarningMixin.launchChangingConfirmationIfNeeded {
|
||||
launch {
|
||||
val genericLedgerSupported = ledgerMigrationTracker.anyChainSupportsMigrationApp()
|
||||
val payload = SelectHardwareWalletBottomSheet.Payload(genericLedgerSupported)
|
||||
|
||||
when (val selection = selectHardwareWallet.awaitAction(payload)) {
|
||||
HardwareWalletModel.LedgerLegacy -> router.openStartImportLegacyLedger()
|
||||
|
||||
HardwareWalletModel.LedgerGeneric -> router.openStartImportGenericLedger()
|
||||
|
||||
is HardwareWalletModel.PolkadotVault -> when (selection.variant) {
|
||||
PolkadotVaultVariant.POLKADOT_VAULT -> router.openStartImportPolkadotVault()
|
||||
PolkadotVaultVariant.PARITY_SIGNER -> router.openStartImportParitySigner()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun importWatchOnlyClicked() {
|
||||
cloudBackupChangingWarningMixin.launchChangingConfirmationIfNeeded {
|
||||
router.openCreateWatchWallet()
|
||||
}
|
||||
}
|
||||
|
||||
fun importRawSeedClicked() {
|
||||
openImportType(ImportType.Seed)
|
||||
}
|
||||
|
||||
fun importJsonClicked() {
|
||||
openImportType(ImportType.Json)
|
||||
}
|
||||
|
||||
private fun openImportType(importType: ImportType) {
|
||||
cloudBackupChangingWarningMixin.launchChangingConfirmationIfNeeded {
|
||||
router.openImportAccountScreen(ImportAccountPayload(importType = importType, addAccountPayload = AddAccountPayload.MetaAccount))
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSignIn() {
|
||||
launch {
|
||||
onboardingInteractor.signInToCloud()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBackupNotFoundError() {
|
||||
showError(
|
||||
resourceManager.getString(R.string.import_wallet_cloud_backup_not_found_title),
|
||||
resourceManager.getString(R.string.import_wallet_cloud_backup_not_found_subtitle),
|
||||
)
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser.di
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import dagger.BindsInstance
|
||||
import dagger.Subcomponent
|
||||
import io.novafoundation.nova.common.di.scope.ScreenScope
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser.ImportWalletOptionsFragment
|
||||
|
||||
@Subcomponent(
|
||||
modules = [
|
||||
ImportWalletOptionsModule::class
|
||||
]
|
||||
)
|
||||
@ScreenScope
|
||||
interface ImportWalletOptionsComponent {
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
fun create(
|
||||
@BindsInstance fragment: Fragment,
|
||||
): ImportWalletOptionsComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: ImportWalletOptionsFragment)
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser.di
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoMap
|
||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
|
||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
|
||||
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
|
||||
import io.novafoundation.nova.common.mixin.api.CustomDialogDisplayer
|
||||
import io.novafoundation.nova.common.resources.ResourceManager
|
||||
import io.novafoundation.nova.common.utils.progress.ProgressDialogMixinFactory
|
||||
import io.novafoundation.nova.feature_cloud_backup_api.presenter.mixin.CloudBackupChangingWarningMixinFactory
|
||||
import io.novafoundation.nova.feature_ledger_core.domain.LedgerMigrationTracker
|
||||
import io.novafoundation.nova.feature_onboarding_api.domain.OnboardingInteractor
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.importChooser.ImportWalletOptionsViewModel
|
||||
|
||||
@Module(includes = [ViewModelModule::class])
|
||||
class ImportWalletOptionsModule {
|
||||
|
||||
@Provides
|
||||
@IntoMap
|
||||
@ViewModelKey(ImportWalletOptionsViewModel::class)
|
||||
fun provideViewModel(
|
||||
resourceManager: ResourceManager,
|
||||
router: OnboardingRouter,
|
||||
actionAwaitableMixin: ActionAwaitableMixin.Factory,
|
||||
progressDialogMixinFactory: ProgressDialogMixinFactory,
|
||||
onboardingInteractor: OnboardingInteractor,
|
||||
customDialogProvider: CustomDialogDisplayer.Presentation,
|
||||
cloudBackupChangingWarningMixinFactory: CloudBackupChangingWarningMixinFactory,
|
||||
ledgerMigrationTracker: LedgerMigrationTracker
|
||||
): ViewModel {
|
||||
return ImportWalletOptionsViewModel(
|
||||
resourceManager,
|
||||
router,
|
||||
actionAwaitableMixin,
|
||||
onboardingInteractor,
|
||||
progressDialogMixinFactory,
|
||||
customDialogProvider,
|
||||
cloudBackupChangingWarningMixinFactory,
|
||||
ledgerMigrationTracker
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideViewModelCreator(
|
||||
fragment: Fragment,
|
||||
viewModelFactory: ViewModelProvider.Factory
|
||||
): ImportWalletOptionsViewModel {
|
||||
return ViewModelProvider(fragment, viewModelFactory).get(ImportWalletOptionsViewModel::class.java)
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import io.novafoundation.nova.common.utils.WithContextExtensions
|
||||
import io.novafoundation.nova.common.utils.inflater
|
||||
import io.novafoundation.nova.common.utils.useAttributes
|
||||
import io.novafoundation.nova.feature_onboarding_impl.R
|
||||
import io.novafoundation.nova.feature_onboarding_impl.databinding.ViewImportOptionBinding
|
||||
|
||||
class ImportOptionView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0,
|
||||
) : MaterialCardView(context, attrs, defStyle), WithContextExtensions by WithContextExtensions(context) {
|
||||
|
||||
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 2.dpF // It will be drawn along border with clipping and finally will be viewed as 1dp
|
||||
color = context.getColor(R.color.container_border)
|
||||
}
|
||||
|
||||
private val binder = ViewImportOptionBinding.inflate(inflater(), this)
|
||||
|
||||
init {
|
||||
setCardBackgroundColor(context.getColor(R.color.button_background_secondary))
|
||||
radius = 12.dpF
|
||||
cardElevation = 0.dpF
|
||||
elevation = 0.dpF
|
||||
|
||||
attrs?.let(::applyAttributes)
|
||||
}
|
||||
|
||||
override fun onDrawForeground(canvas: Canvas) {
|
||||
super.onDrawForeground(canvas)
|
||||
canvas.save()
|
||||
canvas.clipRect(binder.importOptionImage.left, binder.importOptionImage.top, binder.importOptionImage.right, binder.importOptionImage.bottom)
|
||||
canvas.drawRoundRect(
|
||||
binder.importOptionImage.left.toFloat(),
|
||||
binder.importOptionImage.top.toFloat(),
|
||||
binder.importOptionImage.right.toFloat(),
|
||||
binder.importOptionImage.bottom.toFloat() + radius,
|
||||
radius,
|
||||
radius,
|
||||
strokePaint
|
||||
)
|
||||
canvas.restore()
|
||||
}
|
||||
|
||||
private fun applyAttributes(attributeSet: AttributeSet) = context.useAttributes(attributeSet, R.styleable.ImportOptionView) {
|
||||
if (it.hasValue(R.styleable.ImportOptionView_android_src)) {
|
||||
binder.importOptionImage.setImageDrawable(it.getDrawable(R.styleable.ImportOptionView_android_src))
|
||||
}
|
||||
|
||||
binder.importOptionName.text = it.getString(R.styleable.ImportOptionView_title)
|
||||
binder.importOptionDescription.text = it.getString(R.styleable.ImportOptionView_android_text)
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.welcome
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import io.novafoundation.nova.common.databinding.BottomSheeetFixedListBinding
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.view.bottomSheet.list.fixed.FixedListBottomSheet
|
||||
import io.novafoundation.nova.common.view.bottomSheet.list.fixed.textItem
|
||||
import io.novafoundation.nova.common.view.bottomSheet.list.fixed.textWithDescriptionItem
|
||||
import io.novafoundation.nova.feature_account_api.di.AccountFeatureApi
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.PolkadotVaultVariant
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.polkadotVault.config.PolkadotVaultVariantConfigProvider
|
||||
import io.novafoundation.nova.feature_onboarding_impl.R
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.model.HardwareWalletModel
|
||||
|
||||
class SelectHardwareWalletBottomSheet(
|
||||
context: Context,
|
||||
private val payload: Payload,
|
||||
private val onSuccess: (HardwareWalletModel) -> Unit
|
||||
) : FixedListBottomSheet<BottomSheeetFixedListBinding>(context, viewConfiguration = ViewConfiguration.default(context)) {
|
||||
|
||||
class Payload(
|
||||
val genericLedgerSupported: Boolean
|
||||
)
|
||||
|
||||
private val polkadotVaultVariantConfigProvider: PolkadotVaultVariantConfigProvider
|
||||
|
||||
init {
|
||||
polkadotVaultVariantConfigProvider = FeatureUtils
|
||||
.getFeature<AccountFeatureApi>(context, AccountFeatureApi::class.java)
|
||||
.polkadotVaultVariantConfigProvider
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setTitle(R.string.account_select_hardware_wallet)
|
||||
|
||||
polkadotVaultItem(PolkadotVaultVariant.POLKADOT_VAULT)
|
||||
|
||||
if (payload.genericLedgerSupported) {
|
||||
textWithDescriptionItem(
|
||||
iconRes = R.drawable.ic_ledger,
|
||||
titleRes = R.string.account_ledger_generic_item_title,
|
||||
descriptionRes = R.string.account_ledger_generic_item_subtitle,
|
||||
showArrowWhenEnabled = true,
|
||||
onClick = { onSuccess(HardwareWalletModel.LedgerGeneric) }
|
||||
)
|
||||
|
||||
textItem(
|
||||
iconRes = R.drawable.ic_ledger_legacy,
|
||||
titleRes = R.string.account_ledger_nano_x_legacy,
|
||||
showArrow = true,
|
||||
applyIconTint = false,
|
||||
onClick = { onSuccess(HardwareWalletModel.LedgerLegacy) }
|
||||
)
|
||||
} else {
|
||||
textItem(
|
||||
iconRes = R.drawable.ic_ledger,
|
||||
titleRes = R.string.account_ledger_nano_x,
|
||||
showArrow = true,
|
||||
applyIconTint = false,
|
||||
onClick = { onSuccess(HardwareWalletModel.LedgerLegacy) }
|
||||
)
|
||||
}
|
||||
|
||||
polkadotVaultItem(PolkadotVaultVariant.PARITY_SIGNER)
|
||||
}
|
||||
|
||||
private fun polkadotVaultItem(variant: PolkadotVaultVariant) {
|
||||
val config = polkadotVaultVariantConfigProvider.variantConfigFor(variant)
|
||||
|
||||
textItem(
|
||||
iconRes = config.common.iconRes,
|
||||
titleRes = config.common.nameRes,
|
||||
showArrow = true,
|
||||
applyIconTint = false,
|
||||
onClick = { onSuccess(HardwareWalletModel.PolkadotVault(variant)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.welcome
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
|
||||
import io.novafoundation.nova.common.base.BaseFragment
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.mixin.impl.observeBrowserEvents
|
||||
import io.novafoundation.nova.common.utils.insets.applyNavigationBarInsets
|
||||
import io.novafoundation.nova.common.utils.insets.applyStatusBarInsets
|
||||
import io.novafoundation.nova.common.utils.clickableSpan
|
||||
import io.novafoundation.nova.common.utils.colorSpan
|
||||
import io.novafoundation.nova.common.utils.formatting.spannable.SpannableFormatter
|
||||
import io.novafoundation.nova.common.utils.setFullSpan
|
||||
import io.novafoundation.nova.common.utils.setVisible
|
||||
import io.novafoundation.nova.common.utils.toSpannable
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
|
||||
import io.novafoundation.nova.feature_onboarding_api.di.OnboardingFeatureApi
|
||||
import io.novafoundation.nova.feature_onboarding_impl.R
|
||||
import io.novafoundation.nova.feature_onboarding_impl.databinding.FragmentWelcomeBinding
|
||||
import io.novafoundation.nova.feature_onboarding_impl.di.OnboardingFeatureComponent
|
||||
|
||||
class WelcomeFragment : BaseFragment<WelcomeViewModel, FragmentWelcomeBinding>() {
|
||||
|
||||
companion object {
|
||||
private const val KEY_DISPLAY_BACK = "display_back"
|
||||
private const val KEY_ADD_ACCOUNT_PAYLOAD = "add_account_payload"
|
||||
|
||||
fun bundle(displayBack: Boolean): Bundle {
|
||||
return Bundle().apply {
|
||||
putBoolean(KEY_DISPLAY_BACK, displayBack)
|
||||
putParcelable(KEY_ADD_ACCOUNT_PAYLOAD, AddAccountPayload.MetaAccount)
|
||||
}
|
||||
}
|
||||
|
||||
fun bundle(payload: AddAccountPayload): Bundle {
|
||||
return Bundle().apply {
|
||||
putBoolean(KEY_DISPLAY_BACK, true)
|
||||
putParcelable(KEY_ADD_ACCOUNT_PAYLOAD, payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createBinding() = FragmentWelcomeBinding.inflate(layoutInflater)
|
||||
|
||||
override fun applyInsets(rootView: View) {
|
||||
binder.welcomeStatus.applyStatusBarInsets()
|
||||
binder.welcomeTerms.applyNavigationBarInsets()
|
||||
}
|
||||
|
||||
override fun initViews() {
|
||||
configureTermsAndPrivacy(
|
||||
getString(R.string.onboarding_terms_and_conditions_1_v2_2_1),
|
||||
getString(R.string.onboarding_terms_and_conditions_2),
|
||||
getString(R.string.onboarding_privacy_policy)
|
||||
)
|
||||
binder.welcomeTerms.movementMethod = LinkMovementMethod.getInstance()
|
||||
binder.welcomeTerms.highlightColor = Color.TRANSPARENT
|
||||
|
||||
binder.welcomeCreateWalletButton.setOnClickListener { viewModel.createAccountClicked() }
|
||||
binder.welcomeRestoreWalletButton.setOnClickListener { viewModel.importAccountClicked() }
|
||||
|
||||
binder.welcomeBackButton.setOnClickListener { viewModel.backClicked() }
|
||||
}
|
||||
|
||||
private fun configureTermsAndPrivacy(sourceText: String, terms: String, privacy: String) {
|
||||
val clickableColor = requireContext().getColor(R.color.text_primary)
|
||||
|
||||
binder.welcomeTerms.text = SpannableFormatter.format(
|
||||
sourceText,
|
||||
terms.toSpannable(colorSpan(clickableColor)).setFullSpan(clickableSpan(viewModel::termsClicked)),
|
||||
privacy.toSpannable(colorSpan(clickableColor)).setFullSpan(clickableSpan(viewModel::privacyClicked)),
|
||||
)
|
||||
}
|
||||
|
||||
override fun inject() {
|
||||
FeatureUtils.getFeature<OnboardingFeatureComponent>(context!!, OnboardingFeatureApi::class.java)
|
||||
.welcomeComponentFactory()
|
||||
.create(
|
||||
fragment = this,
|
||||
shouldShowBack = argument(KEY_DISPLAY_BACK),
|
||||
addAccountPayload = argument(KEY_ADD_ACCOUNT_PAYLOAD)
|
||||
)
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
override fun subscribe(viewModel: WelcomeViewModel) {
|
||||
observeBrowserEvents(viewModel)
|
||||
|
||||
viewModel.shouldShowBackLiveData.observe(binder.welcomeBackButton::setVisible)
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.welcome
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.novafoundation.nova.common.base.BaseViewModel
|
||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||
import io.novafoundation.nova.common.mixin.api.Browserable
|
||||
import io.novafoundation.nova.common.utils.Event
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
|
||||
|
||||
class WelcomeViewModel(
|
||||
shouldShowBack: Boolean,
|
||||
private val router: OnboardingRouter,
|
||||
private val appLinksProvider: AppLinksProvider,
|
||||
private val addAccountPayload: AddAccountPayload,
|
||||
updateNotificationsInteractor: UpdateNotificationsInteractor
|
||||
) : BaseViewModel(),
|
||||
Browserable {
|
||||
|
||||
val shouldShowBackLiveData: LiveData<Boolean> = MutableLiveData(shouldShowBack)
|
||||
|
||||
override val openBrowserEvent = MutableLiveData<Event<String>>()
|
||||
|
||||
init {
|
||||
updateNotificationsInteractor.allowInAppUpdateCheck()
|
||||
}
|
||||
|
||||
fun createAccountClicked() {
|
||||
when (addAccountPayload) {
|
||||
is AddAccountPayload.MetaAccount -> router.openCreateFirstWallet()
|
||||
is AddAccountPayload.ChainAccount -> router.openMnemonicScreen(accountName = null, addAccountPayload)
|
||||
}
|
||||
}
|
||||
|
||||
fun importAccountClicked() {
|
||||
router.openImportOptionsScreen()
|
||||
}
|
||||
|
||||
fun termsClicked() {
|
||||
openBrowserEvent.value = Event(appLinksProvider.termsUrl)
|
||||
}
|
||||
|
||||
fun privacyClicked() {
|
||||
openBrowserEvent.value = Event(appLinksProvider.privacyUrl)
|
||||
}
|
||||
|
||||
fun backClicked() {
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.di
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import dagger.BindsInstance
|
||||
import dagger.Subcomponent
|
||||
import io.novafoundation.nova.common.di.scope.ScreenScope
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.WelcomeFragment
|
||||
|
||||
@Subcomponent(
|
||||
modules = [
|
||||
WelcomeModule::class
|
||||
]
|
||||
)
|
||||
@ScreenScope
|
||||
interface WelcomeComponent {
|
||||
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
fun create(
|
||||
@BindsInstance fragment: Fragment,
|
||||
@BindsInstance shouldShowBack: Boolean,
|
||||
@BindsInstance addAccountPayload: AddAccountPayload,
|
||||
): WelcomeComponent
|
||||
}
|
||||
|
||||
fun inject(welcomeFragment: WelcomeFragment)
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.di
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoMap
|
||||
import io.novafoundation.nova.common.data.network.AppLinksProvider
|
||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
|
||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.add.AddAccountPayload
|
||||
import io.novafoundation.nova.feature_ledger_core.domain.LedgerMigrationTracker
|
||||
import io.novafoundation.nova.feature_onboarding_impl.OnboardingRouter
|
||||
import io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.WelcomeViewModel
|
||||
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
|
||||
|
||||
@Module(includes = [ViewModelModule::class])
|
||||
class WelcomeModule {
|
||||
|
||||
@Provides
|
||||
@IntoMap
|
||||
@ViewModelKey(WelcomeViewModel::class)
|
||||
fun provideViewModel(
|
||||
router: OnboardingRouter,
|
||||
appLinksProvider: AppLinksProvider,
|
||||
shouldShowBack: Boolean,
|
||||
addAccountPayload: AddAccountPayload,
|
||||
updateNotificationsInteractor: UpdateNotificationsInteractor,
|
||||
ledgerMigrationTracker: LedgerMigrationTracker,
|
||||
): ViewModel {
|
||||
return WelcomeViewModel(
|
||||
shouldShowBack,
|
||||
router,
|
||||
appLinksProvider,
|
||||
addAccountPayload,
|
||||
updateNotificationsInteractor
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideViewModelCreator(
|
||||
fragment: Fragment,
|
||||
viewModelFactory: ViewModelProvider.Factory
|
||||
): WelcomeViewModel {
|
||||
return ViewModelProvider(fragment, viewModelFactory).get(WelcomeViewModel::class.java)
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl.presentation.welcome.model
|
||||
|
||||
import io.novafoundation.nova.feature_account_api.domain.model.PolkadotVaultVariant
|
||||
|
||||
sealed class HardwareWalletModel {
|
||||
|
||||
class PolkadotVault(val variant: PolkadotVaultVariant) : HardwareWalletModel()
|
||||
|
||||
object LedgerGeneric : HardwareWalletModel()
|
||||
|
||||
object LedgerLegacy : HardwareWalletModel()
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:background="@color/secondary_screen_background">
|
||||
|
||||
<io.novafoundation.nova.common.view.Toolbar
|
||||
android:id="@+id/importOptionsToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:dividerVisible="false"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="24dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/importOptionsToolbar">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.NovaFoundation.Bold.Title3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/import_wallet_options_title" />
|
||||
|
||||
<GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="14dp"
|
||||
android:columnCount="2">
|
||||
|
||||
<io.novafoundation.nova.feature_onboarding_impl.presentation.view.ImportOptionView
|
||||
android:id="@+id/importOptionCloud"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnSpan="2"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_margin="6dp"
|
||||
android:src="@drawable/ic_import_option_cloud"
|
||||
android:text="@string/import_wallet_options_cloud_backup_subtitle"
|
||||
app:title="@string/common_cloud_backup" />
|
||||
|
||||
<io.novafoundation.nova.feature_onboarding_impl.presentation.view.ImportOptionView
|
||||
android:id="@+id/import_option_passphrase"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnSpan="1"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:layout_margin="6dp"
|
||||
android:src="@drawable/ic_import_option_passphrase"
|
||||
android:text="@string/import_wallet_options_mnemonic_subtitle"
|
||||
app:title="@string/common_passphrase" />
|
||||
|
||||
<io.novafoundation.nova.feature_onboarding_impl.presentation.view.ImportOptionView
|
||||
android:id="@+id/importOptionHardware"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:layout_margin="6dp"
|
||||
android:src="@drawable/ic_import_option_hardware"
|
||||
android:text="@string/account_welcome_hardware_wallet_subtitle"
|
||||
app:title="@string/account_welcome_hardware_wallet_title" />
|
||||
|
||||
<io.novafoundation.nova.feature_onboarding_impl.presentation.view.ImportOptionView
|
||||
android:id="@+id/importOptionTrustWallet"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:layout_margin="6dp"
|
||||
android:src="@drawable/ic_import_option_trust_wallet"
|
||||
android:text="@string/account_welcome_trust_wallet_subtitle"
|
||||
app:title="@string/account_welcome_trust_wallet_title" />
|
||||
|
||||
|
||||
<io.novafoundation.nova.feature_onboarding_impl.presentation.view.ImportOptionView
|
||||
android:id="@+id/importOptionWatchOnly"
|
||||
android:layout_width="0dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:layout_margin="6dp"
|
||||
android:src="@drawable/ic_import_option_watch_only"
|
||||
android:text="@string/account_add_watch_only_description"
|
||||
app:title="@string/import_wallet_options_watch_only_title" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/importOptionRawSeed"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:layout_margin="6dp"
|
||||
android:elevation="0dp"
|
||||
app:cardBackgroundColor="@color/button_background_secondary"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.NovaFoundation.SemiBold.SubHeadline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:drawablePadding="12dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/recovery_raw_seed"
|
||||
app:drawableStartCompat="@drawable/ic_raw_seed" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/importOptionJson"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="52dp"
|
||||
android:layout_columnWeight="1"
|
||||
android:layout_gravity="fill"
|
||||
android:layout_margin="6dp"
|
||||
android:elevation="0dp"
|
||||
app:cardBackgroundColor="@color/button_background_secondary"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<TextView
|
||||
style="@style/TextAppearance.NovaFoundation.SemiBold.SubHeadline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:drawablePadding="12dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:lines="1"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/recovery_json"
|
||||
app:drawableStartCompat="@drawable/ic_file_outline" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</GridLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/welcomeStatus"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/logoImg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_create_wallet_background"
|
||||
app:layout_constraintBottom_toTopOf="@+id/welcomeCreateWalletButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/welcomeBackButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_primary_list_item"
|
||||
android:padding="@dimen/x2"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/welcomeStatus"
|
||||
app:srcCompat="@drawable/ic_arrow_back"
|
||||
app:tint="@color/icon_primary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<io.novafoundation.nova.common.view.PrimaryButton
|
||||
android:id="@+id/welcomeCreateWalletButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="@string/onboarding_create_wallet"
|
||||
app:appearance="primary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/welcomeRestoreWalletButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<io.novafoundation.nova.common.view.PrimaryButton
|
||||
android:id="@+id/welcomeRestoreWalletButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="@string/onboarding_restore_wallet"
|
||||
app:appearance="secondary"
|
||||
app:icon="@drawable/ic_watch_only_filled"
|
||||
app:layout_constraintBottom_toTopOf="@+id/welcomeTerms"
|
||||
app:layout_constraintEnd_toEndOf="@+id/welcomeCreateWalletButton"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/welcomeCreateWalletButton"
|
||||
app:subTitle="@string/account_add_watch_only_description" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/welcomeTerms"
|
||||
style="@style/TextAppearance.NovaFoundation.Body1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/x2"
|
||||
android:layout_marginEnd="@dimen/x2"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/text_secondary"
|
||||
android:textColorLink="@color/text_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="@string/onboarding_terms_and_conditions_1_v2_2_1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:parentTag="com.google.android.material.card.MaterialCardView">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/importOptionImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:adjustViewBounds="true"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_import_option_watch_only" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/importOptionName"
|
||||
style="@style/TextAppearance.NovaFoundation.SemiBold.SubHeadline"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textColor="@color/text_primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/importOptionImage"
|
||||
tools:text="Hardware wallet" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/importOptionDescription"
|
||||
style="@style/TextAppearance.NovaFoundation.Regular.Footnote"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textColor="@color/text_secondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/importOptionName"
|
||||
tools:text="Polkadot Vault, Parity Signer or Ledger" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</merge>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<declare-styleable name="ImportOptionView">
|
||||
<attr name="android:src" />
|
||||
<attr name="title" />
|
||||
<attr name="android:text" />
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.novafoundation.nova.feature_onboarding_impl
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user