mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-22 02:07:58 +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,24 @@
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
|
||||
android {
|
||||
namespace 'io.novafoundation.nova.feature_external_sign_api'
|
||||
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':feature-account-api')
|
||||
implementation project(':common')
|
||||
|
||||
implementation coroutinesDep
|
||||
|
||||
implementation androidDep
|
||||
implementation materialDep
|
||||
implementation constraintDep
|
||||
|
||||
testImplementation jUnitDep
|
||||
testImplementation mockitoDep
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.di
|
||||
|
||||
import io.novafoundation.nova.feature_external_sign_api.domain.sign.evm.EvmTypedMessageParser
|
||||
|
||||
interface ExternalSignFeatureApi {
|
||||
|
||||
val evmTypedMessageParser: EvmTypedMessageParser
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.domain.sign.evm
|
||||
|
||||
import io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm.EvmTypedMessage
|
||||
|
||||
interface EvmTypedMessageParser {
|
||||
|
||||
fun parseEvmTypedMessage(message: String): EvmTypedMessage
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import io.novafoundation.nova.common.base.errors.SigningCancelledException
|
||||
import io.novafoundation.nova.common.navigation.InterScreenRequester
|
||||
import io.novafoundation.nova.common.navigation.InterScreenResponder
|
||||
import io.novafoundation.nova.feature_external_sign_api.model.ExternalSignCommunicator.Response
|
||||
import io.novafoundation.nova.feature_external_sign_api.model.signPayload.ExternalSignPayload
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
interface ExternalSignRequester : InterScreenRequester<ExternalSignPayload, Response>
|
||||
|
||||
interface ExternalSignResponder : InterScreenResponder<ExternalSignPayload, Response>
|
||||
|
||||
interface ExternalSignCommunicator : ExternalSignRequester, ExternalSignResponder {
|
||||
|
||||
sealed class Response : Parcelable {
|
||||
|
||||
abstract val requestId: String
|
||||
|
||||
@Parcelize
|
||||
class Rejected(override val requestId: String) : Response()
|
||||
|
||||
@Parcelize
|
||||
class Signed(override val requestId: String, val signature: String, val modifiedTransaction: String? = null) : Response()
|
||||
|
||||
@Parcelize
|
||||
class Sent(override val requestId: String, val txHash: String) : Response()
|
||||
|
||||
@Parcelize
|
||||
class SigningFailed(override val requestId: String, val shouldPresent: Boolean = true) : Response()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun ExternalSignRequester.awaitConfirmation(request: ExternalSignPayload): Response {
|
||||
openRequest(request)
|
||||
|
||||
return responseFlow.first { it.requestId == request.signRequest.id }
|
||||
}
|
||||
|
||||
fun Throwable.failedSigningIfNotCancelled(requestId: String) = if (this is SigningCancelledException) {
|
||||
null
|
||||
} else {
|
||||
Response.SigningFailed(requestId)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload
|
||||
|
||||
import android.os.Parcelable
|
||||
import io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm.EvmSignPayload
|
||||
import io.novafoundation.nova.feature_external_sign_api.model.signPayload.polkadot.PolkadotSignPayload
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class ExternalSignPayload(
|
||||
val signRequest: ExternalSignRequest,
|
||||
val dappMetadata: SigningDappMetadata?,
|
||||
val wallet: ExternalSignWallet
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
class SigningDappMetadata(
|
||||
val icon: String?,
|
||||
val name: String?,
|
||||
val url: String
|
||||
) : Parcelable
|
||||
|
||||
sealed class ExternalSignWallet : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
object Current : ExternalSignWallet()
|
||||
|
||||
@Parcelize
|
||||
class WithId(val metaId: Long) : ExternalSignWallet()
|
||||
}
|
||||
|
||||
sealed interface ExternalSignRequest : Parcelable {
|
||||
|
||||
val id: String
|
||||
|
||||
@Parcelize
|
||||
class Polkadot(override val id: String, val payload: PolkadotSignPayload) : ExternalSignRequest
|
||||
|
||||
@Parcelize
|
||||
class Evm(override val id: String, val payload: EvmSignPayload) : ExternalSignRequest
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm
|
||||
|
||||
import android.os.Parcelable
|
||||
import io.novafoundation.nova.common.utils.Precision
|
||||
import io.novafoundation.nova.common.utils.TokenSymbol
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class EvmChain(
|
||||
val chainId: String,
|
||||
val chainName: String,
|
||||
val nativeCurrency: NativeCurrency,
|
||||
val rpcUrl: String,
|
||||
val iconUrl: String?
|
||||
) : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
class NativeCurrency(
|
||||
val name: String,
|
||||
val symbol: TokenSymbol,
|
||||
val decimals: Precision
|
||||
) : Parcelable
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class EvmPersonalSignMessage(
|
||||
val data: String
|
||||
) : Parcelable
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed class EvmSignPayload : Parcelable {
|
||||
|
||||
abstract val originAddress: String
|
||||
|
||||
@Parcelize
|
||||
class ConfirmTx(
|
||||
val transaction: EvmTransaction,
|
||||
override val originAddress: String,
|
||||
val chainSource: EvmChainSource,
|
||||
val action: Action,
|
||||
) : EvmSignPayload() {
|
||||
|
||||
enum class Action {
|
||||
SIGN, SEND
|
||||
}
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class SignTypedMessage(
|
||||
val message: EvmTypedMessage,
|
||||
override val originAddress: String,
|
||||
) : EvmSignPayload()
|
||||
|
||||
@Parcelize
|
||||
class PersonalSign(
|
||||
val message: EvmPersonalSignMessage,
|
||||
override val originAddress: String,
|
||||
) : EvmSignPayload()
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class EvmChainSource(val evmChainId: Int, val unknownChainOptions: UnknownChainOptions) : Parcelable {
|
||||
|
||||
sealed class UnknownChainOptions : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
object MustBeKnown : UnknownChainOptions()
|
||||
|
||||
@Parcelize
|
||||
class WithFallBack(val evmChain: EvmChain) : UnknownChainOptions()
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed class EvmTransaction : Parcelable {
|
||||
@Parcelize
|
||||
class Struct(
|
||||
val gas: String?,
|
||||
val gasPrice: String?,
|
||||
val from: String,
|
||||
val to: String,
|
||||
val data: String?,
|
||||
val value: String?,
|
||||
val nonce: String?,
|
||||
) : EvmTransaction()
|
||||
|
||||
@Parcelize
|
||||
class Raw(val rawContent: String) : EvmTransaction()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.evm
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class EvmTypedMessage(
|
||||
val data: String,
|
||||
val raw: String?
|
||||
) : Parcelable
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.polkadot
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
sealed class PolkadotSignPayload : Parcelable {
|
||||
|
||||
abstract val address: String
|
||||
|
||||
@Parcelize
|
||||
class Json(
|
||||
override val address: String,
|
||||
val blockHash: String,
|
||||
val blockNumber: String,
|
||||
val era: String,
|
||||
val genesisHash: String,
|
||||
val method: String,
|
||||
val nonce: String,
|
||||
val specVersion: String,
|
||||
val tip: String,
|
||||
val transactionVersion: String,
|
||||
val metadataHash: String?,
|
||||
val withSignedTransaction: Boolean?,
|
||||
val signedExtensions: List<String>,
|
||||
val assetId: String?,
|
||||
val version: Int
|
||||
) : PolkadotSignPayload()
|
||||
|
||||
@Parcelize
|
||||
class Raw(
|
||||
val data: String,
|
||||
override val address: String,
|
||||
val type: String?
|
||||
) : PolkadotSignPayload()
|
||||
}
|
||||
|
||||
fun PolkadotSignPayload.maybeSignExtrinsic(): PolkadotSignPayload.Json? = this as? PolkadotSignPayload.Json
|
||||
|
||||
fun PolkadotSignPayload.genesisHash(): String? = (this as? PolkadotSignPayload.Json)?.genesisHash
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.model.signPayload.polkadot
|
||||
|
||||
class PolkadotSignerResult(val id: String, val signature: String, val signedTransaction: String?)
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.presentation.dapp
|
||||
|
||||
import android.widget.ImageView
|
||||
import coil.ImageLoader
|
||||
import coil.load
|
||||
import io.novafoundation.nova.feature_external_sign_api.R
|
||||
|
||||
fun ImageView.showDAppIcon(
|
||||
url: String?,
|
||||
imageLoader: ImageLoader
|
||||
) {
|
||||
load(url, imageLoader) {
|
||||
fallback(R.drawable.ic_earth)
|
||||
error(R.drawable.ic_earth)
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.presentation.externalSign
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import coil.ImageLoader
|
||||
import io.novafoundation.nova.common.di.FeatureUtils
|
||||
import io.novafoundation.nova.common.utils.postToSelf
|
||||
import io.novafoundation.nova.common.utils.sequrity.AutomaticInteractionGate
|
||||
import io.novafoundation.nova.common.utils.sequrity.awaitInteractionAllowed
|
||||
import io.novafoundation.nova.feature_account_api.presenatation.account.wallet.WalletModel
|
||||
import io.novafoundation.nova.feature_account_api.view.showWallet
|
||||
import io.novafoundation.nova.feature_external_sign_api.databinding.BottomSheetConfirmAuthorizeBinding
|
||||
import io.novafoundation.nova.feature_external_sign_api.presentation.dapp.showDAppIcon
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AuthorizeDappBottomSheet(
|
||||
context: Context,
|
||||
private val payload: Payload,
|
||||
onConfirm: () -> Unit,
|
||||
onDeny: () -> Unit,
|
||||
) : ConfirmDAppActionBottomSheet(
|
||||
context = context,
|
||||
onConfirm = onConfirm,
|
||||
onDeny = onDeny
|
||||
) {
|
||||
|
||||
class Payload(
|
||||
val dAppUrl: String,
|
||||
val title: String,
|
||||
val dAppIconUrl: String?,
|
||||
val walletModel: WalletModel,
|
||||
)
|
||||
|
||||
private val interactionGate: AutomaticInteractionGate
|
||||
|
||||
private val imageLoader: ImageLoader
|
||||
|
||||
override val contentBinder = BottomSheetConfirmAuthorizeBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
init {
|
||||
FeatureUtils.getCommonApi(context).let { api ->
|
||||
interactionGate = api.automaticInteractionGate
|
||||
imageLoader = api.imageLoader()
|
||||
}
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
launch {
|
||||
interactionGate.awaitInteractionAllowed()
|
||||
|
||||
super.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
contentBinder.confirmAuthorizeDappIcon.showDAppIcon(payload.dAppIconUrl, imageLoader)
|
||||
contentBinder.confirmAuthorizeDappWallet.postToSelf { showWallet(payload.walletModel) }
|
||||
|
||||
contentBinder.confirmAuthorizeDappTitle.text = payload.title
|
||||
contentBinder.confirmAuthorizeDappDApp.postToSelf { showValue(payload.dAppUrl) }
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package io.novafoundation.nova.feature_external_sign_api.presentation.externalSign
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import io.novafoundation.nova.common.utils.DialogExtensions
|
||||
import io.novafoundation.nova.common.view.bottomSheet.BaseBottomSheet
|
||||
import io.novafoundation.nova.feature_external_sign_api.databinding.BottomSheetConfirmDappActionBinding
|
||||
|
||||
abstract class ConfirmDAppActionBottomSheet(
|
||||
context: Context,
|
||||
private val onConfirm: () -> Unit,
|
||||
private val onDeny: () -> Unit
|
||||
) : BaseBottomSheet<BottomSheetConfirmDappActionBinding>(context), DialogExtensions {
|
||||
|
||||
override val binder: BottomSheetConfirmDappActionBinding = BottomSheetConfirmDappActionBinding.inflate(LayoutInflater.from(context))
|
||||
|
||||
abstract val contentBinder: ViewBinding
|
||||
|
||||
@CallSuper
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setCancelable(false)
|
||||
|
||||
binder.confirmInnerContent.addView(contentBinder.root)
|
||||
|
||||
binder.confirmDAppActionAllow.setDismissingClickListener { onConfirm() }
|
||||
binder.confirmDAppActionReject.setDismissingClickListener { onDeny() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView 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:overScrollMode="never"
|
||||
android:scrollbars="none"
|
||||
android:layout_height="match_parent"
|
||||
tools:background="@color/bottom_sheet_background">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:background="@color/bottom_sheet_background">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/confirmAuthorizeNovaIcon"
|
||||
style="@style/Widget.Nova.Icon.Big"
|
||||
android:layout_marginTop="20dp"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_pezkuwi_logo"
|
||||
app:layout_constraintEnd_toStartOf="@+id/confirmAuthorizeNovaArrow"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/confirmAuthorizeNovaArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:src="@drawable/ic_bidirectonal"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/confirmAuthorizeNovaIcon"
|
||||
app:layout_constraintEnd_toStartOf="@+id/confirmAuthorizeDappIcon"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/confirmAuthorizeNovaIcon"
|
||||
app:layout_constraintTop_toTopOf="@+id/confirmAuthorizeNovaIcon"
|
||||
app:tint="@color/icon_secondary" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/confirmAuthorizeDappIcon"
|
||||
style="@style/Widget.Nova.Icon.Big"
|
||||
android:layout_marginTop="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/confirmAuthorizeNovaArrow"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:src="@drawable/ic_earth" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirmAuthorizeDappTitle"
|
||||
style="@style/TextAppearance.NovaFoundation.SemiBold.Title3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textColor="@color/text_primary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/confirmAuthorizeNovaIcon"
|
||||
tools:text="Allow “Polkadot.js” to access your account addresses?" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/confirmAuthorizeDappSubTitle"
|
||||
style="@style/TextAppearance.NovaFoundation.Regular.Footnote"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/dapp_confirm_authorize_subtitle"
|
||||
android:textColor="@color/text_secondary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/confirmAuthorizeDappTitle" />
|
||||
|
||||
<io.novafoundation.nova.common.view.TableCellView
|
||||
android:id="@+id/confirmAuthorizeDappWallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/confirmAuthorizeDappSubTitle"
|
||||
app:title="@string/tabbar_wallet_title" />
|
||||
|
||||
<io.novafoundation.nova.common.view.TableCellView
|
||||
android:id="@+id/confirmAuthorizeDappDApp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:dividerVisible="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/confirmAuthorizeDappWallet"
|
||||
app:title="@string/dapp_dapp" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
tools:background="@color/bottom_sheet_background">
|
||||
|
||||
<ImageView
|
||||
style="@style/Widget.Nova.Puller"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="6dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/confirmInnerContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
tools:layout_height="300dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<io.novafoundation.nova.common.view.PrimaryButton
|
||||
android:id="@+id/confirmDAppActionReject"
|
||||
style="@style/Widget.Nova.Button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/common_reject"
|
||||
app:appearance="secondary"
|
||||
android:theme="@style/NegativeAccent" />
|
||||
|
||||
<io.novafoundation.nova.common.view.PrimaryButton
|
||||
android:id="@+id/confirmDAppActionAllow"
|
||||
style="@style/Widget.Nova.Button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/common_allow"
|
||||
android:theme="@style/AccentBlue" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user