Skip to content

Commit

Permalink
support 12 words
Browse files Browse the repository at this point in the history
  • Loading branch information
polstianka committed Dec 4, 2024
1 parent c5eac75 commit 3fc77ec
Show file tree
Hide file tree
Showing 16 changed files with 581 additions and 273 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ data class TokenEntity(
address = jetton.metadata.address.toRawAddress(),
name = jetton.metadata.name,
symbol = jetton.metadata.symbol,
imageUri = Uri.parse(jetton.metadata.image),
imageUri = Uri.parse(jetton.preview ?: jetton.metadata.image),
decimals = jetton.metadata.decimals.toInt(),
verification = convertVerification(jetton.verification),
isRequestMinting = extensions?.contains(Extension.CustomPayload.value) == true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,22 +294,26 @@ class TonConnectManager(

val clientId = tonConnect.clientId
try {
val targetWallet = if (wallet != null && wallet.isTonConnectSupported) wallet else null

val app = readManifest(tonConnect.manifestUrl)
if (isScam(activity, targetWallet ?: WalletEntity.EMPTY, app.iconUrl.toUri(), app.url)) {
if (isScam(activity, wallet ?: WalletEntity.EMPTY, app.iconUrl.toUri(), app.url)) {
return@withContext JsonBuilder.connectEventError(BridgeError.badRequest("client error"))
}

val screen = TonConnectScreen.newInstance(
app = app,
proofPayload = tonConnect.proofPayload,
returnUri = tonConnect.returnUri,
wallet = targetWallet,
wallet = wallet,
fromPackageName = tonConnect.fromPackageName
)
val bundle = activity.addForResult(screen)
val response = screen.contract.parseResult(bundle)

if (wallet != null && !wallet.isTonConnectSupported) {
return@withContext JsonBuilder.connectEventError(BridgeError.methodNotSupported("Wallet not supported TonConnect"))
}

val connect = newConnect(
wallet = response.wallet,
keyPair = keyPair,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@ package com.tonapps.tonkeeper.ui.component

import android.content.Context
import android.util.AttributeSet
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebStorage
import android.webkit.WebView
import androidx.webkit.WebViewCompat
import com.tonapps.wallet.data.account.entities.WalletEntity
import uikit.widget.webview.bridge.BridgeWebView
import java.io.File

class TonConnectWebView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = android.R.attr.webViewStyle,
) : BridgeWebView(context, attrs, defStyle) {


fun setWallet(wallet: WalletEntity) {

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class DAppScreen(wallet: WalletEntity): InjectedTonConnectScreen(R.layout.fragme
closeView.setOnClickListener { finish() }

webView = view.findViewById(R.id.web_view)
webView.setWallet(wallet)
webView.settings.useWideViewPort = true
webView.settings.loadWithOverviewMode = true
webView.addCallback(webViewCallback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.RecyclerView
import com.tonapps.extensions.toUriOrNull
import com.tonapps.tonkeeper.helper.BrowserHelper
import com.tonapps.tonkeeper.ui.base.WalletContextScreen
import com.tonapps.tonkeeper.ui.screen.browser.dapp.DAppScreen
import com.tonapps.tonkeeper.ui.screen.browser.search.list.Adapter
Expand All @@ -37,12 +39,17 @@ class BrowserSearchScreen(wallet: WalletEntity): WalletContextScreen(R.layout.fr
override val viewModel: BrowserSearchViewModel by viewModel()

private val adapter = Adapter { title, url ->
navigation?.add(DAppScreen.newInstance(
wallet = screenContext.wallet,
title = title,
url = url.toUri(),
source = "browser_search"
))
val uri = url.toUriOrNull() ?: return@Adapter
if (uri.host?.endsWith("mercuryo.io") == true) {
BrowserHelper.open(requireContext(), url)
} else {
navigation?.add(DAppScreen.newInstance(
wallet = screenContext.wallet,
title = title,
url = url.toUri(),
source = "browser_search"
))
}
finish()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ package com.tonapps.tonkeeper.ui.screen.init.step

import android.os.Bundle
import android.text.Editable
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.Button
import android.widget.LinearLayout
import androidx.appcompat.widget.AppCompatTextView
import androidx.appcompat.widget.LinearLayoutCompat
import androidx.core.view.updatePadding
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
Expand All @@ -14,7 +20,9 @@ import com.tonapps.tonkeeper.ui.component.WordEditText
import com.tonapps.tonkeeper.ui.screen.init.InitViewModel
import com.tonapps.tonkeeperx.BuildConfig
import com.tonapps.tonkeeperx.R
import com.tonapps.uikit.color.backgroundContentTintColor
import com.tonapps.uikit.color.iconPrimaryColor
import com.tonapps.uikit.color.textPrimaryColor
import com.tonapps.wallet.localization.Localization
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
Expand All @@ -23,9 +31,11 @@ import kotlinx.coroutines.withContext
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.ton.mnemonic.Mnemonic
import uikit.base.BaseFragment
import uikit.drawable.FooterDrawable
import uikit.extensions.clear
import uikit.extensions.collectFlow
import uikit.extensions.doKeyboardAnimation
import uikit.extensions.dp
import uikit.extensions.getCurrentFocusEditText
import uikit.extensions.getViews
import uikit.extensions.hideKeyboard
Expand All @@ -35,28 +45,38 @@ import uikit.extensions.withAlpha
import uikit.navigation.Navigation.Companion.navigation
import uikit.widget.ColumnLayout
import uikit.widget.LoaderView
import uikit.widget.RowLayout

class WordsScreen: BaseFragment(R.layout.fragment_init_words) {

private val initViewModel: InitViewModel by viewModel(ownerProducer = { requireParentFragment() })

override val secure: Boolean = !BuildConfig.DEBUG

private var wordsCount = WORDS24

private lateinit var scrollView: NestedScrollView
private lateinit var contentView: ColumnLayout
private lateinit var button: Button
private lateinit var loaderView: LoaderView
private lateinit var suggestionsView: RowLayout
private lateinit var words24View: AppCompatTextView
private lateinit var words12View: AppCompatTextView

private val wordInputs: List<WordEditText> by lazy {
contentView.getViews().filterIsInstance<WordEditText>()
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

words24View = view.findViewById(R.id.words_24)
words24View.setOnClickListener { setWordsCount(WORDS24) }

words12View = view.findViewById(R.id.words_12)
words12View.setOnClickListener { setWordsCount(WORDS12) }

scrollView = view.findViewById(R.id.scroll)
scrollView.doKeyboardAnimation { offset, _, _ ->
scrollView.updatePadding(bottom = offset)
}

contentView = view.findViewById(R.id.content)

Expand Down Expand Up @@ -84,9 +104,44 @@ class WordsScreen: BaseFragment(R.layout.fragment_init_words) {
}
}

suggestionsView = view.findViewById(R.id.suggestions)
suggestionsView.background = FooterDrawable(requireContext()).apply {
setDivider(true)
setColor(requireContext().backgroundContentTintColor)
}

collectFlow(initViewModel.uiTopOffset) {
contentView.updatePadding(top = it)
}

scrollView.doKeyboardAnimation { offset, progress, isShowing ->
scrollView.updatePadding(bottom = offset)
suggestionsView.translationY = -offset.toFloat()
suggestionsView.alpha = progress
}
}

private fun setWordsCount(count: Int) {
if (wordsCount == count || count != WORDS24 && count != WORDS12) {
return
}
if (count == WORDS24) {
words24View.setBackgroundResource(uikit.R.drawable.bg_content_tint_16)
words12View.background = null
wordsCount = count
} else {
words12View.setBackgroundResource(uikit.R.drawable.bg_content_tint_16)
words24View.background = null
}
wordsCount = count
updateVisibleInputs()
}

private fun updateVisibleInputs() {
wordInputs.forEachIndexed { index, wordInput ->
wordInput.visibility = if (index < wordsCount) View.VISIBLE else View.GONE
}
postOnAnimation { checkWords() }
}

private fun nextInput(index: Int) {
Expand All @@ -98,7 +153,7 @@ class WordsScreen: BaseFragment(R.layout.fragment_init_words) {
private fun next() {
lifecycleScope.launch {
val words = getMnemonic()
if (words.isEmpty() || !Mnemonic.isValid(words)) {
if (words.isEmpty() || (wordsCount == WORDS24 && !Mnemonic.isValid(words))) {
if (TonMnemonic.isValidTONKeychain(words)) {
navigation?.toast(Localization.multi_account_secret_wrong)
} else {
Expand Down Expand Up @@ -132,16 +187,84 @@ class WordsScreen: BaseFragment(R.layout.fragment_init_words) {
private fun onTextChanged(index: Int, editable: Editable) {
if (index == 0) {
val words = TonMnemonic.parseMnemonic(editable.toString())
post {
applyWords(words)
postOnAnimation {
if (words.isNotEmpty()) {
applyWords(words)
} else {
checkSuggestions(index, editable.toString())
}
}
} else {
post {
postOnAnimation {
checkWords()
checkSuggestions(index, editable.toString())
}
}
}

private fun checkSuggestions(index: Int, text: String) {
if (!wordInputs[index].isFocused) {
suggestionsView.visibility = View.GONE
return
}
if (text.isEmpty()) {
suggestionsView.visibility = View.GONE
} else {
lifecycleScope.launch(Dispatchers.IO) {
val words = TonMnemonic.findWords(text).take(3)
setSuggestions(index, words)
}
}
}

private suspend fun setSuggestions(
index: Int,
words: List<String>
) = withContext(Dispatchers.Main) {
if (words.isEmpty()) {
suggestionsView.visibility = View.GONE
} else {
suggestionsView.removeAllViews()
for (word in words) {
val textView = AppCompatTextView(requireContext()).apply {
setTextAppearance(uikit.R.style.TextAppearance_Label2)
setTextColor(requireContext().textPrimaryColor)
text = word
gravity = Gravity.CENTER
setOnClickListener { setWord(index, word) }
}
suggestionsView.addView(textView, LinearLayoutCompat.LayoutParams(LinearLayoutCompat.LayoutParams.MATCH_PARENT, LinearLayoutCompat.LayoutParams.MATCH_PARENT, 1f))
}
suggestionsView.visibility = View.VISIBLE
}
}

private fun setWord(index: Int, value: String) {
if (suggestionsView.alpha == 0f || suggestionsView.visibility != View.VISIBLE) {
return
}
suggestionsView.visibility = View.GONE
val inputView = wordInputs.getOrNull(index) ?: return
inputView.setText(value)

val nextFocusInput = inputView.focusSearch(View.FOCUS_DOWN) as? WordEditText

if (nextFocusInput != null) {
nextFocusInput.requestFocus()
if (index > 20) {
scrollView.scrollDown(true)
} else {
scrollView.scrollView(
view = nextFocusInput,
smooth = true,
top = (-128).dp
)
}
} else {
next()
}
}

private fun applyWords(words: List<String>) {
if (words.size > 1) {
wordInputs.first().clear()
Expand All @@ -156,7 +279,7 @@ class WordsScreen: BaseFragment(R.layout.fragment_init_words) {
if (delay > 0) {
delay(delay)
}
button.isEnabled = getMnemonic().size == 24
button.isEnabled = getMnemonic().size == wordsCount
}
}

Expand All @@ -165,8 +288,9 @@ class WordsScreen: BaseFragment(R.layout.fragment_init_words) {
.map { it.text?.toString() }
.filter { TonMnemonic.isValid(it) }
.filterNotNull()
.take(wordsCount)

if (words.size == wordInputs.size) {
if (words.size == wordInputs.count { it.visibility == View.VISIBLE }) {
words
} else {
emptyList()
Expand All @@ -187,6 +311,9 @@ class WordsScreen: BaseFragment(R.layout.fragment_init_words) {

companion object {

private const val WORDS24 = 24
private const val WORDS12 = 12

private const val ARG_TESTNET = "testnet"

fun newInstance(testnet: Boolean): WordsScreen {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.webkit.WebView
import androidx.core.content.FileProvider
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
Expand Down Expand Up @@ -184,6 +185,11 @@ class RootViewModel(
ShortcutManagerCompat.removeAllDynamicShortcuts(context)
} else if (state is AccountRepository.SelectedState.Wallet) {
_hasWalletFlow.tryEmit(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
WebView.setDataDirectorySuffix("wallet_${state.wallet.id.replace("-", "")}")
} catch (ignored: Throwable) { }
}
}
}.flowOn(Dispatchers.IO).launchIn(viewModelScope)

Expand Down
Loading

0 comments on commit 3fc77ec

Please sign in to comment.