Skip to content

Commit

Permalink
feat: Android dynamic derivations support (#1943)
Browse files Browse the repository at this point in the history
* started wotk with derived screen

* started wotk with derived screen progress 2

* add derivation screen progress

* components file renamed

* add derived keys screen finished

* adjusted paddings

* small adjustements

* key details added lable for dynamic derivations

* added todo

* fixed size for text

* network filter work in progress

* network filter work in progress 2

* network filter implementation

* network filter implementation 2

* dependencies updated

* netfilter workin in progress

* netfilter working in progress 2 ui clearing

* network filters preferences added

* network filters preferences added 2

* network filters preferences added 3

* keyset details refactoring

* moved backup BS to keyset details screen

* filters implemented, only keyset old items missing

* fixed network filters

* changed elements for a old ones for keyset details

* key derived elements updated

* removed outdated todo

* feat: #1882 import dymanic derivations

* fix: ios: fix building after enum change

* fixed compilation errors from rust

* fixed compilation errors from rust for android

* implemented new view for keyset details when all keys filtered out

* error messages added

* animated qr code implemented for dynamic derivations

* add response QR

* integration dynamic derivations screen 1

* integration dynamic derivations screen 2

* removed unrelated todos

* padding added

* padding added 2

* add was_imported field to MKeysCard

* fix grammar

* fix ios

* fix #1935 investigation

* add was_imported to MKeyDetails

* fix #1935 fixed

* autoformat

* implemented to show was imported label

* fixed paddings for imported label as per design

* removed temp notes

* filter network - hide done if empty list

* filter network - fixed difference between clean selection and close

* fixing new rust changes to make app compile. Next will be to adjust behaviour

* remove build tools version as now agp defines it

* work in progress - implementation separate import keys

* implementation import derivation in progress

* implemented create import dynamic derevation keys

* improved UX - text, password asking while still screen presented.

* fix to show errors in case if keyset doesn't exist.

---------

Co-authored-by: Pavel Rybalko <[email protected]>
Co-authored-by: Krzysztof Rodak <[email protected]>
  • Loading branch information
3 people authored Aug 2, 2023
1 parent 56e9c21 commit 184c87d
Show file tree
Hide file tree
Showing 36 changed files with 1,151 additions and 252 deletions.
14 changes: 8 additions & 6 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ android {
pickFirsts += ['lib/armeabi-v7a/libc++_shared.so', 'lib/arm64-v8a/libc++_shared.so', 'lib/x86/libc++_shared.so', 'lib/x86_64/libc++_shared.so']
}
}
buildToolsVersion '31.0.0'
compileSdk 33
ndkVersion '24.0.8215888'
namespace 'io.parity.signer'
Expand Down Expand Up @@ -90,19 +89,21 @@ dependencies {
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.activity:activity-compose:1.7.2'
implementation 'androidx.biometric:biometric:1.1.0'
implementation "androidx.compose.ui:ui:$compose_libs_version"
implementation "androidx.compose.material:material:$compose_libs_version"
implementation "androidx.compose.material:material-icons-extended:$compose_libs_version"
implementation "com.google.accompanist:accompanist-flowlayout:$accompanist_version" //for flow-layout which is non-lazy grid
implementation "com.google.accompanist:accompanist-permissions:$accompanist_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_libs_version"
implementation "androidx.navigation:navigation-compose:2.5.3"
implementation "androidx.datastore:datastore-preferences:1.0.0"
implementation "androidx.navigation:navigation-compose:2.6.0"
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.1"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1'
implementation 'androidx.activity:activity-compose:1.7.1'
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha06'
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'androidx.camera:camera-core:1.2.0'
implementation 'androidx.camera:camera-lifecycle:1.2.0'
implementation 'androidx.camera:camera-view:1.2.0'
Expand All @@ -111,17 +112,18 @@ dependencies {
implementation 'com.halilibo.compose-richtext:richtext-commonmark-android:0.17.0'
implementation 'com.halilibo.compose-richtext:richtext-ui-material-android:0.17.0'
implementation 'androidx.core:core-ktx:1.10.1'
implementation "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5"
implementation "net.java.dev.jna:jna:5.13.0@aar"
implementation "io.coil-kt:coil-compose:$coilVersion"
implementation "io.coil-kt:coil-svg:$coilVersion"
testImplementation 'junit:junit:4.13.2'
testImplementation "org.mockito:mockito-core:5.4.0"
testImplementation "androidx.test:core:1.5.0"
testImplementation 'androidx.test.ext:junit:1.1.5'
testImplementation "androidx.test.ext:junit:1.1.5"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_libs_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_libs_version"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import io.parity.signer.ui.theme.textSecondary
@Composable
fun BottomSheetHeader(
title: String,
subtitile: String? = null,
modifier: Modifier = Modifier,
subtitile: String? = null,
onCloseClicked: Callback?
) {
Row(
Expand Down Expand Up @@ -86,11 +86,11 @@ fun BottomSheetSubtitle(
private fun PreviewHeaderWithClose() {
SignerNewTheme {
Column() {
BottomSheetHeader(title = "Title") {}
BottomSheetHeader(title = "Title", onCloseClicked = {})
Divider()
BottomSheetHeader(title = "Very very very very long title Very very very very long title") {}
Divider()
BottomSheetHeader(title = "Title", subtitile = "With subtitle") {}
BottomSheetHeader(title = "Title", subtitile = "With subtitle", onCloseClicked = {})
Divider()
BottomSheetSubtitle(R.string.subtitle_secret_recovery_phrase)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ fun NotificationFrameText(
fun NotificationFrameTextImportant(
message: String,
withBorder: Boolean = true,
textColor: Color = MaterialTheme.colors.pink300,
modifier: Modifier = Modifier,
) {
val BACKGROUND = Color(0x14F272B6)
Expand All @@ -88,7 +89,7 @@ fun NotificationFrameTextImportant(
) {
Text(
text = message,
color = MaterialTheme.colors.pink300,
color = textColor,
style = SignerTypeface.CaptionM,
modifier = Modifier
.weight(1f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ import io.parity.signer.ui.theme.fill12
import io.parity.signer.ui.theme.pink500
import io.parity.signer.ui.theme.textTertiary


@Composable
fun ScreenHeader(
title: String?,
onBack: Callback? = null,
onMenu: Callback? = null,
modifier: Modifier = Modifier,
) {
Row(
modifier = Modifier
modifier = modifier
.fillMaxWidth(1f)
.defaultMinSize(minHeight = 56.dp)
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package io.parity.signer.components.items

import SignerCheckbox
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.parity.signer.components.networkicon.NetworkIcon
import io.parity.signer.domain.NetworkModel
import io.parity.signer.ui.theme.SignerNewTheme
import io.parity.signer.ui.theme.SignerTypeface
import io.parity.signer.ui.theme.textTertiary


@Composable
fun NetworkItemClickable(
network: NetworkModel,
onClick: (NetworkModel) -> Unit,
) {
Row(
modifier = Modifier.clickable { onClick(network) },
verticalAlignment = Alignment.CenterVertically
) {
NetworkIcon(
networkLogoName = network.logo,
modifier = Modifier
.padding(
top = 16.dp,
bottom = 16.dp,
start = 16.dp,
end = 12.dp
)
.size(36.dp),
)
Text(
text = network.title,
color = MaterialTheme.colors.primary,
style = SignerTypeface.TitleS,
)
Spacer(modifier = Modifier.weight(1f))
Image(
imageVector = Icons.Filled.ChevronRight,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colors.textTertiary),
modifier = Modifier
.padding(2.dp)// because it's 28 not 32pd
.padding(end = 16.dp)
.size(28.dp)
)
}
}

@Composable
fun NetworkItemMultiselect(
network: NetworkModel,
isSelected: Boolean,
modifier: Modifier = Modifier,
onClick: (NetworkModel) -> Unit,
) {
Row(
modifier = modifier.clickable { onClick(network) },
verticalAlignment = Alignment.CenterVertically
) {
NetworkIcon(
networkLogoName = network.logo,
modifier = Modifier
.padding(
top = 16.dp,
bottom = 16.dp,
start = 16.dp,
end = 12.dp
)
.size(36.dp),
)
Text(
text = network.title,
color = MaterialTheme.colors.primary,
style = SignerTypeface.TitleS,
)
Spacer(modifier = Modifier.weight(1f))
SignerCheckbox(
isChecked = isSelected,
modifier = Modifier.padding(end = 8.dp),
uncheckedColor = MaterialTheme.colors.primary,
) {
onClick(network)
}
}
}



@Preview(
name = "light", group = "general", uiMode = Configuration.UI_MODE_NIGHT_NO,
showBackground = true, backgroundColor = 0xFFFFFFFF,
)
@Preview(
name = "dark", group = "general",
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true, backgroundColor = 0xFF000000,
)
@Composable
private fun PreviewNetworkItem() {
val networks = listOf(
NetworkModel(
key = "0",
logo = "polkadot",
title = "Polkadot",
pathId = "polkadot",
),
NetworkModel(
key = "1",
logo = "Kusama",
title = "Kusama",
pathId = "kusama",
),
)
SignerNewTheme {
Column() {
NetworkItemClickable(
network = networks[0],
onClick = {})
NetworkItemMultiselect(
network = networks[0],
isSelected = true,
onClick = {}
)
NetworkItemMultiselect(
network = networks[1],
isSelected = false,
onClick = {}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.parity.signer.components.networkicon.UnknownNetworkColorsGenerator
import io.parity.signer.domain.Authentication
import io.parity.signer.domain.NetworkExposedStateKeeper
import io.parity.signer.domain.storage.DatabaseAssetsInteractor
import io.parity.signer.domain.storage.PreferencesRepository
import io.parity.signer.domain.storage.SeedRepository
import io.parity.signer.domain.storage.SeedStorage

Expand All @@ -32,6 +33,7 @@ object ServiceLocator {
val uniffiInteractor by lazy { UniffiInteractor(appContext) }

val seedStorage: SeedStorage = SeedStorage()
val preferencesRepository: PreferencesRepository by lazy { PreferencesRepository(appContext) }
val databaseAssetsInteractor by lazy { DatabaseAssetsInteractor(appContext, seedStorage) }
val networkExposedStateKeeper by lazy { NetworkExposedStateKeeper(appContext, uniffiInteractor) }
val authentication = Authentication()
Expand Down
4 changes: 1 addition & 3 deletions android/src/main/java/io/parity/signer/domain/Helpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package io.parity.signer.domain
import android.util.Log
import io.parity.signer.BuildConfig
import io.parity.signer.uniffi.ErrorDisplayed
import kotlinx.coroutines.Dispatchers
import java.lang.RuntimeException
import java.util.concurrent.Executors

fun submitErrorState(message: String) {
Log.e("error state", message)
Expand All @@ -15,7 +13,7 @@ fun submitErrorState(message: String) {
}


fun ErrorDisplayed.getDetailedDescriptionString(): String {
fun ErrorDisplayed.getDebugDetailedDescriptionString(): String {
return this.javaClass.name + "Message: " + message
}

18 changes: 15 additions & 3 deletions android/src/main/java/io/parity/signer/domain/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ fun MKeysNew.toKeySetDetailsModel() = KeySetDetailsModel(
root = root?.toKeysModel(),
)

data class KeyAndNetworkModel(val key: KeyModel, val network: NetworkInfoModel)
data class KeyAndNetworkModel(val key: KeyModel, val network: NetworkInfoModel) {
companion object {
fun createStub() = KeyAndNetworkModel(KeyModel.createStub(), NetworkInfoModel.createStub())
}
}

fun MKeyAndNetworkCard.toKeyAndNetworkModel() = KeyAndNetworkModel(
key = key.toKeyModel(),
Expand All @@ -64,17 +68,19 @@ data class KeyModel(
val base58: String,
val hasPwd: Boolean,
val path: String,
val secretExposed: Boolean
val secretExposed: Boolean,
val wasImported: Boolean?,
) {
companion object {
fun createStub() = KeyModel(
fun createStub(wasImported: Boolean = false) = KeyModel(
addressKey = "address key",
base58 = "5F3sa2TJAWMqDhXG6jhV4N8ko9SxwGy8TpaNS1repo5EYjQX",
identicon = PreviewData.Identicon.exampleIdenticonPng,
hasPwd = true,
path = "//polkadot//path2",
secretExposed = false,
seedName = "sdsdsd",
wasImported = wasImported,
)
}
}
Expand All @@ -87,6 +93,7 @@ fun MAddressCard.toKeysModel() = KeyModel(
path = address.path,
secretExposed = address.secretExposed,
seedName = address.seedName,
wasImported = null,
)

/**
Expand All @@ -100,6 +107,7 @@ fun MKeysCard.toKeyModel() = KeyModel(
path = address.path,
secretExposed = address.secretExposed,
seedName = address.seedName,
wasImported = wasImported,
)

/**
Expand Down Expand Up @@ -163,6 +171,7 @@ data class KeyDetailsModel(
val address: KeyCardModel,
val base58: String,
val secretExposed: Boolean,
val wasImported: Boolean,
) {
val isRootKey = address.cardBase.path.isEmpty()

Expand All @@ -179,6 +188,7 @@ data class KeyDetailsModel(
address = keyCard,
base58 = keyCard.cardBase.base58,
secretExposed = true,
wasImported = false,
)
}

Expand All @@ -196,6 +206,7 @@ data class KeyDetailsModel(
),
base58 = keyCard.cardBase.base58,
secretExposed = true,
wasImported = false,
)
}
}
Expand All @@ -213,6 +224,7 @@ fun MKeyDetails.toKeyDetailsModel() =
),
base58 = base58,
secretExposed = address.secretExposed,
wasImported = wasImported,
)


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package io.parity.signer.domain

import android.util.Log
import android.widget.Toast
import io.parity.signer.dependencygraph.ServiceLocator
import io.parity.signer.domain.storage.getSeed

Expand Down Expand Up @@ -37,6 +39,8 @@ suspend fun getSeedPhraseForBackup(
try {
seedStorage.getSeed(seedName, showInLogs = true)
} catch (e: Exception) {
Log.d("get seed failure", e.toString())
Toast.makeText(activity, "get seed failure: $e", Toast.LENGTH_LONG).show()
null
}
}
Expand Down
Loading

0 comments on commit 184c87d

Please sign in to comment.