Initial commit: Pezkuwi Wallet Android

Complete rebrand of Nova Wallet for Pezkuwichain ecosystem.

## Features
- Full Pezkuwichain support (HEZ & PEZ tokens)
- Polkadot ecosystem compatibility
- Staking, Governance, DeFi, NFTs
- XCM cross-chain transfers
- Hardware wallet support (Ledger, Polkadot Vault)
- WalletConnect v2
- Push notifications

## Languages
- English, Turkish, Kurmanci (Kurdish), Spanish, French, German, Russian, Japanese, Chinese, Korean, Portuguese, Vietnamese

Based on Nova Wallet by Novasama Technologies GmbH
© Dijital Kurdistan Tech Institute 2026
This commit is contained in:
2026-01-23 01:31:12 +03:00
commit 31c8c5995f
7621 changed files with 425838 additions and 0 deletions
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>
@@ -0,0 +1,3 @@
package io.novafoundation.nova.feature_vote.di
interface VoteFeatureApi
@@ -0,0 +1,40 @@
package io.novafoundation.nova.feature_vote.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_vote.presentation.VoteRouter
import io.novafoundation.nova.feature_vote.presentation.vote.di.VoteComponent
@Component(
dependencies = [
VoteFeatureDependencies::class
],
modules = [
VoteFeatureModule::class,
]
)
@FeatureScope
interface VoteFeatureComponent : VoteFeatureApi {
@Component.Factory
interface Factory {
fun create(
@BindsInstance voteRouter: VoteRouter,
deps: VoteFeatureDependencies
): VoteFeatureComponent
}
fun voteComponentFactory(): VoteComponent.Factory
@Component(
dependencies = [
CommonApi::class,
AccountFeatureApi::class
]
)
interface VoteFeatureDependenciesComponent : VoteFeatureDependencies
}
@@ -0,0 +1,8 @@
package io.novafoundation.nova.feature_vote.di
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
interface VoteFeatureDependencies {
val selectedAccountUseCase: SelectedAccountUseCase
}
@@ -0,0 +1,24 @@
package io.novafoundation.nova.feature_vote.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_vote.presentation.VoteRouter
import javax.inject.Inject
@ApplicationScope
class VoteFeatureHolder @Inject constructor(
featureContainer: FeatureContainer,
private val router: VoteRouter
) : FeatureApiHolder(featureContainer) {
override fun initializeDependencies(): Any {
val dependencies = DaggerVoteFeatureComponent_VoteFeatureDependenciesComponent.builder()
.commonApi(commonApi())
.accountFeatureApi(getFeature(AccountFeatureApi::class.java))
.build()
return DaggerVoteFeatureComponent.factory()
.create(router, dependencies)
}
}
@@ -0,0 +1,6 @@
package io.novafoundation.nova.feature_vote.di
import dagger.Module
@Module
class VoteFeatureModule
@@ -0,0 +1,12 @@
package io.novafoundation.nova.feature_vote.presentation
import androidx.fragment.app.Fragment
interface VoteRouter {
fun getDemocracyFragment(): Fragment
fun getCrowdloansFragment(): Fragment
fun openSwitchWallet()
}
@@ -0,0 +1,44 @@
package io.novafoundation.nova.feature_vote.presentation.vote
import android.view.View
import io.novafoundation.nova.common.base.BaseFragment
import io.novafoundation.nova.common.di.FeatureUtils
import io.novafoundation.nova.common.utils.insets.applyStatusBarInsets
import io.novafoundation.nova.common.utils.setupWithViewPager2
import io.novafoundation.nova.feature_vote.databinding.FragmentVoteBinding
import io.novafoundation.nova.feature_vote.di.VoteFeatureApi
import io.novafoundation.nova.feature_vote.di.VoteFeatureComponent
import io.novafoundation.nova.feature_vote.presentation.VoteRouter
import javax.inject.Inject
class VoteFragment : BaseFragment<VoteViewModel, FragmentVoteBinding>() {
override fun createBinding() = FragmentVoteBinding.inflate(layoutInflater)
@Inject
lateinit var router: VoteRouter
override fun applyInsets(rootView: View) {
binder.voteContainer.applyStatusBarInsets()
}
override fun initViews() {
val adapter = VotePagerAdapter(this, router)
binder.voteViewPager.adapter = adapter
binder.voteTabs.setupWithViewPager2(binder.voteViewPager, adapter::getPageTitle)
binder.voteAvatar.setOnClickListener { viewModel.avatarClicked() }
}
override fun inject() {
FeatureUtils.getFeature<VoteFeatureComponent>(this, VoteFeatureApi::class.java)
.voteComponentFactory()
.create(this)
.inject(this)
}
override fun subscribe(viewModel: VoteViewModel) {
viewModel.selectedWalletModel.observe(binder.voteAvatar::setModel)
}
}
@@ -0,0 +1,29 @@
package io.novafoundation.nova.feature_vote.presentation.vote
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import io.novafoundation.nova.feature_vote.R
import io.novafoundation.nova.feature_vote.presentation.VoteRouter
class VotePagerAdapter(private val fragment: Fragment, private val router: VoteRouter) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> router.getDemocracyFragment()
1 -> router.getCrowdloansFragment()
else -> throw IllegalArgumentException("Invalid position")
}
}
fun getPageTitle(position: Int): CharSequence {
return when (position) {
0 -> fragment.getString(R.string.common_governance)
1 -> fragment.getString(R.string.crowdloan_crowdloan)
else -> throw IllegalArgumentException("Invalid position")
}
}
}
@@ -0,0 +1,18 @@
package io.novafoundation.nova.feature_vote.presentation.vote
import io.novafoundation.nova.common.base.BaseViewModel
import io.novafoundation.nova.feature_account_api.domain.interfaces.SelectedAccountUseCase
import io.novafoundation.nova.feature_vote.presentation.VoteRouter
class VoteViewModel(
private val router: VoteRouter,
private val selectedAccountUseCase: SelectedAccountUseCase,
) : BaseViewModel() {
val selectedWalletModel = selectedAccountUseCase.selectedWalletModelFlow()
.shareInBackground()
fun avatarClicked() {
router.openSwitchWallet()
}
}
@@ -0,0 +1,26 @@
package io.novafoundation.nova.feature_vote.presentation.vote.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_vote.presentation.vote.VoteFragment
@Subcomponent(
modules = [
VoteModule::class
]
)
@ScreenScope
interface VoteComponent {
@Subcomponent.Factory
interface Factory {
fun create(
@BindsInstance fragment: Fragment
): VoteComponent
}
fun inject(fragment: VoteFragment)
}
@@ -0,0 +1,35 @@
package io.novafoundation.nova.feature_vote.presentation.vote.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.feature_account_api.domain.interfaces.SelectedAccountUseCase
import io.novafoundation.nova.feature_vote.presentation.VoteRouter
import io.novafoundation.nova.feature_vote.presentation.vote.VoteViewModel
@Module(includes = [ViewModelModule::class])
class VoteModule {
@Provides
internal fun provideViewModel(fragment: Fragment, factory: ViewModelProvider.Factory): VoteViewModel {
return ViewModelProvider(fragment, factory).get(VoteViewModel::class.java)
}
@Provides
@IntoMap
@ViewModelKey(VoteViewModel::class)
fun provideViewModel(
voteRouter: VoteRouter,
selectedAccountUseCase: SelectedAccountUseCase
): ViewModel {
return VoteViewModel(
router = voteRouter,
selectedAccountUseCase = selectedAccountUseCase
)
}
}
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/voteContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/drawable_background_image"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/voteTitle"
style="@style/TextAppearance.NovaFoundation.Header1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="17dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:text="@string/vote_vote"
android:textColor="@color/text_primary" />
<io.novafoundation.nova.feature_account_api.view.SelectedWalletView
android:id="@+id/voteAvatar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:layout_marginEnd="16dp" />
</LinearLayout>
<io.novafoundation.nova.common.view.SegmentedTabLayout
android:id="@+id/voteTabs"
style="@style/SegmentedTab"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/voteViewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
@@ -0,0 +1,17 @@
package io.novafoundation.nova.feature_vote
import org.junit.Test
import org.junit.Assert.*
/**
* 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)
}
}