Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
742 changes: 742 additions & 0 deletions .cursor/rules/design_system_rules.md

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions app/src/main/kotlin/com/algorand/android/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.algorand.android.models.TransactionSignData
import com.algorand.android.models.WalletConnectRequest
import com.algorand.android.models.WalletConnectRequest.WalletConnectArbitraryDataRequest
import com.algorand.android.models.WalletConnectRequest.WalletConnectTransaction
import com.algorand.android.modules.addaccount.joint.transaction.ui.PendingSignaturesDialogFragment
import com.algorand.android.modules.assetinbox.assetinboxoneaccount.ui.model.AssetInboxOneAccountNavArgs
import com.algorand.android.modules.autolockmanager.ui.AutoLockManager
import com.algorand.android.modules.deeplink.ui.DeeplinkHandler
Expand Down Expand Up @@ -393,7 +394,8 @@ class MainActivity :
is TransactionManagerResult.Success.TransactionRequestSigned -> {
hideProgress()
hideLedgerLoadingDialog()
TODO("Implement this")
PendingSignaturesDialogFragment.newInstance(result.signRequestId)
.show(supportFragmentManager, PendingSignaturesDialogFragment.TAG)
}

TransactionManagerResult.LedgerOperationCanceled -> {
Expand Down Expand Up @@ -576,7 +578,11 @@ class MainActivity :

private fun navToJointAccountImportDeepLink(address: String) {
navToHome()
TODO("Implement this")
nav(
HomeNavigationDirections.actionGlobalToJointAccountDetailFragment(
accountAddress = address
)
)
}

fun navToContactAdditionNavigation(address: String, label: String?) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package com.algorand.android.core.transaction

import com.algorand.android.models.TransactionSignData
import com.algorand.android.modules.addaccount.joint.transaction.domain.usecase.SignAndSubmitJointAccountSignature
import com.algorand.android.utils.extensions.encodeBase64
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import com.algorand.wallet.account.local.domain.model.LocalAccount
Expand All @@ -31,6 +32,7 @@ class JointAccountTransactionSignHelper @Inject constructor(
private val getSignableAccountsByAddresses: GetSignableAccountsByAddresses,
private val localAccountSigningHelper: LocalAccountSigningHelper,
private val proposeJointSignRequest: ProposeJointSignRequest,
private val signAndSubmitJointAccountSignature: SignAndSubmitJointAccountSignature,
private val getJointAccountProposerAddress: GetJointAccountProposerAddress,
private val getAccountRekeyAdminAddress: GetAccountRekeyAdminAddress
) {
Expand Down Expand Up @@ -98,7 +100,16 @@ class JointAccountTransactionSignHelper @Inject constructor(
) {
val eligibleSigners = getSignableAccountsByAddresses(jointAccount.participantAddresses)

TODO("Implement auto sign with local accounts")
for (signer in eligibleSigners) {
val result = signAndSubmitJointAccountSignature(
signRequestId = signRequestId,
participantAddress = signer.algoAddress,
rawTransactions = rawTransactions
)
if (result !is PeraResult.Success) {
// Failed to auto-sign for participant, continue with others
}
}
}

private fun prepareRawTransactionLists(transactionDataList: List<TransactionSignData>): List<List<String>>? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ abstract class TransactionSignBaseFragment(
}

protected open fun onJointAccountSignRequestCreated(signRequestId: String) {
TODO("Implement this")
nav(HomeNavigationDirections.actionGlobalToJointAccountSignRequestFragment(signRequestId))
}

private val ledgerLoadingDialogListener = LedgerLoadingDialog.Listener { shouldStopResources ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,11 @@ class AccountHistoryFragment : BaseFragment(R.layout.fragment_account_history) {
initUi()
initObserver()
handleLoadState()
initSavedStateListener()
}

override fun onResume() {
super.onResume()
initSavedStateListener()
accountHistoryViewModel.activatePendingTransaction()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import android.view.ViewGroup
import androidx.compose.runtime.getValue
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.algorand.android.HomeNavigationDirections
import com.algorand.android.core.DaggerBaseFragment
import com.algorand.android.models.FragmentConfiguration
import com.algorand.android.modules.accountdetail.jointaccountdetail.viewmodel.JointAccountDetailViewModel
Expand Down Expand Up @@ -95,10 +96,22 @@ class JointAccountDetailFragment : DaggerBaseFragment(0), JointAccountDetailList
}

private fun navigateToNameJointAccount(event: ViewEvent.NavigateToNameJointAccount) {
TODO("Implement this")
nav(
HomeNavigationDirections.actionGlobalToNameJointAccountFragment(
threshold = event.threshold,
participantAddresses = event.participantAddresses.toTypedArray()
)
)
}

private fun navigateToEditContact(event: ViewEvent.NavigateToEditContact) {
TODO("Implement this")
nav(
HomeNavigationDirections.actionGlobalEditContactFragment(
contactName = event.contactName,
contactPublicKey = event.contactPublicKey,
contactDatabaseId = event.contactDatabaseId,
contactProfileImageUri = event.contactProfileImageUri
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,11 @@ class AccountDetailFragment :
}

private fun navToJointAccountDetailFragment() {
TODO("Implement this")
nav(
AccountDetailFragmentDirections.actionAccountDetailFragmentToJointAccountDetailFragment(
accountDetailViewModel.accountAddress
)
)
}

override fun onImageItemClick(nftAssetId: Long) {
Expand Down Expand Up @@ -559,7 +563,12 @@ class AccountDetailFragment :
}

private fun navToInboxWithFilter() {
TODO("Implement this")
nav(
AccountDetailFragmentDirections
.actionAccountDetailFragmentToAssetInboxAllAccountsNavigation(
filterAccountAddress = accountDetailViewModel.accountAddress
)
)
}

private fun navToBuySellActionsBottomSheet() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ import com.algorand.android.modules.parity.domain.model.SelectedCurrencyDetail
import com.algorand.android.modules.peraconnectivitymanager.ui.PeraConnectivityManager
import com.algorand.android.utils.CacheResult
import com.algorand.wallet.banner.domain.usecase.GetBannerFlow
import com.algorand.wallet.inbox.asset.domain.usecase.GetAssetInboxRequestCountFlow
import com.algorand.wallet.privacy.domain.usecase.GetPrivacyModeFlow
import com.algorand.wallet.remoteconfig.domain.usecase.IsFeatureToggleEnabled
import com.algorand.wallet.spotbanner.domain.model.SpotBannerFlowData
import com.algorand.wallet.spotbanner.domain.usecase.GetSpotBannersFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -44,12 +42,10 @@ class AccountsPreviewUseCase @Inject constructor(
private val portfolioValueItemMapper: PortfolioValueItemMapper,
private val peraConnectivityManager: PeraConnectivityManager,
private val accountPreviewProcessor: AccountPreviewProcessor,
private val getAssetInboxRequestCountFlow: GetAssetInboxRequestCountFlow,
private val getAccountLiteCacheFlow: GetAccountLiteCacheFlow,
private val getPrivacyModeFlow: GetPrivacyModeFlow,
private val getBannerFlow: GetBannerFlow,
private val getSpotBannersFlow: GetSpotBannersFlow,
private val isFeatureToggleEnabled: IsFeatureToggleEnabled
) {

suspend fun getInitialAccountPreview(): AccountPreview {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.addaccount.intro.di

import com.algorand.android.modules.addaccount.intro.domain.usecase.CreateAlgo25Account
import com.algorand.android.modules.addaccount.intro.domain.usecase.CreateAlgo25AccountUseCase
import com.algorand.android.modules.addaccount.intro.domain.usecase.CreateHdKeyAccount
import com.algorand.android.modules.addaccount.intro.domain.usecase.CreateHdKeyAccountUseCase
import com.algorand.android.modules.addaccount.intro.domain.usecase.GetAddAccountIntroPreview
import com.algorand.android.modules.addaccount.intro.domain.usecase.GetAddAccountIntroPreviewUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
internal object AddAccountIntroUiModule {

@Provides
fun provideGetAddAccountIntroPreview(
impl: GetAddAccountIntroPreviewUseCase
): GetAddAccountIntroPreview = impl

@Provides
fun provideCreateHdKeyAccount(
impl: CreateHdKeyAccountUseCase
): CreateHdKeyAccount = impl

@Provides
fun provideCreateAlgo25Account(
impl: CreateAlgo25AccountUseCase
): CreateAlgo25Account = impl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.addaccount.intro.domain.exception

class AccountCreationException(message: String) : Exception(message)
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
* limitations under the License
*/

package com.algorand.android.models
package com.algorand.android.modules.addaccount.intro.domain.model

import androidx.annotation.StringRes

data class RegisterIntroPreview(
data class AddAccountIntroPreview(
@param:StringRes val titleRes: Int,
val isSkipButtonVisible: Boolean,
val isCloseButtonVisible: Boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.addaccount.intro.domain.usecase

import com.algorand.android.models.AccountCreation
import com.algorand.android.models.Result

fun interface CreateAlgo25Account {
suspend operator fun invoke(): Result<AccountCreation>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.addaccount.intro.domain.usecase

import com.algorand.android.models.AccountCreation
import com.algorand.android.models.Result
import com.algorand.android.modules.addaccount.intro.domain.exception.AccountCreationException
import com.algorand.android.utils.analytics.CreationType
import com.algorand.wallet.algosdk.transaction.sdk.AlgoAccountSdk
import com.algorand.wallet.encryption.domain.manager.AESPlatformManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject

internal class CreateAlgo25AccountUseCase @Inject constructor(
private val algoAccountSdk: AlgoAccountSdk,
private val aesPlatformManager: AESPlatformManager
) : CreateAlgo25Account {

override suspend fun invoke(): Result<AccountCreation> = withContext(Dispatchers.Default) {
val account = algoAccountSdk.createAlgo25Account()
?: return@withContext Result.Error(AccountCreationException("Failed to generate Algo25 account"))

Result.Success(
AccountCreation(
address = account.address,
customName = null,
isBackedUp = false,
type = AccountCreation.Type.Algo25(
aesPlatformManager.encryptByteArray(account.secretKey)
),
creationType = CreationType.CREATE
)
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.addaccount.intro.domain.usecase

import com.algorand.android.models.AccountCreation
import com.algorand.android.models.Result

fun interface CreateHdKeyAccount {
operator fun invoke(): Result<AccountCreation>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.addaccount.intro.domain.usecase

import com.algorand.android.models.AccountCreation
import com.algorand.android.models.Result
import com.algorand.android.modules.addaccount.intro.domain.exception.AccountCreationException
import com.algorand.android.ui.onboarding.creation.mapper.AccountCreationHdKeyTypeMapper
import com.algorand.android.utils.analytics.CreationType
import com.algorand.wallet.algosdk.bip39.model.HdKeyAddressIndex
import com.algorand.wallet.algosdk.bip39.sdk.Bip39WalletProvider
import javax.inject.Inject

internal class CreateHdKeyAccountUseCase @Inject constructor(
private val bip39WalletProvider: Bip39WalletProvider,
private val accountCreationHdKeyTypeMapper: AccountCreationHdKeyTypeMapper
) : CreateHdKeyAccount {

override fun invoke(): Result<AccountCreation> {
return try {
val wallet = bip39WalletProvider.createBip39Wallet()
val hdKeyAddress = wallet.generateAddress(HdKeyAddressIndex())
val hdKeyType = accountCreationHdKeyTypeMapper(
wallet.getEntropy().value,
hdKeyAddress,
seedId = null
)
Result.Success(
AccountCreation(
address = hdKeyAddress.address,
customName = null,
isBackedUp = false,
type = hdKeyType,
creationType = CreationType.CREATE
)
)
} catch (e: Exception) {
Result.Error(AccountCreationException("Failed to generate HD key account: ${e.message}"))
}
}
}
Loading