Skip to content

Commit

Permalink
Merge pull request #21 from MrStahlfelge/workbranch
Browse files Browse the repository at this point in the history
P2S, configurable API urls, Telegram help group, version bump
  • Loading branch information
MrStahlfelge authored Aug 3, 2021
2 parents a240493 + 68edc25 commit 0e4e10d
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 26 deletions.
6 changes: 3 additions & 3 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ android {
applicationId "org.ergoplatform.android"
minSdkVersion 24
targetSdkVersion 30
versionCode 2110
versionName "1.0.2110"
versionCode 2111
versionName "1.1.2111"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -103,7 +103,7 @@ dependencies {
implementation("androidx.biometric:biometric:1.1.0")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"

implementation ('org.ergoplatform:ergo-appkit_2.11:d4cf012d-SNAPSHOT') {
implementation ('org.ergoplatform:ergo-appkit_2.11:develop-a47bd4e8-SNAPSHOT') {
exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'
exclude group: 'org.bitbucket.inkytonik.kiama', module: 'kiama_2.11'
}
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/java/org/ergoplatform/android/ErgoFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,12 @@ fun sendErgoTx(
amountToSend: Long,
mnemonic: String,
mnemonicPass: String,
derivedKeyIndex: Int = 0,
nodeApiAddress: String = StageConstants.NODE_API_ADDRESS
derivedKeyIndex: Int,
nodeApiAddress: String,
explorerApiAddress: String
): TransactionResult {
try {
val ergoClient = RestApiErgoClient.create(nodeApiAddress, StageConstants.NETWORK_TYPE, "", StageConstants.EXPLORER_API_ADDRESS)
val ergoClient = RestApiErgoClient.create(nodeApiAddress, StageConstants.NETWORK_TYPE, "", explorerApiAddress)
return ergoClient.execute { ctx: BlockchainContext ->
val prover = ctx.newProverBuilder()
.withMnemonic(
Expand Down
28 changes: 19 additions & 9 deletions app/src/main/java/org/ergoplatform/android/NodeConnector.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,37 @@ class NodeConnector {
private set
var fiatCurrency: String = ""
private set
private val ergoApiService: DefaultApi
private var ergoApiService: DefaultApi? = null
private val coinGeckoApi: CoinGeckoApi

init {
val retrofit = Retrofit.Builder()
.baseUrl(StageConstants.EXPLORER_API_ADDRESS)
.addConverterFactory(GsonConverterFactory.create())
.build()

ergoApiService = retrofit.create(DefaultApi::class.java)

val retrofitCoinGecko = Retrofit.Builder().baseUrl("https://api.coingecko.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
coinGeckoApi = retrofitCoinGecko.create(CoinGeckoApi::class.java)
}

private fun getOrInitErgoApiService(context: Context): DefaultApi {
if (ergoApiService == null) {

val retrofit = Retrofit.Builder()
.baseUrl(getPrefExplorerApiUrl(context))
.addConverterFactory(GsonConverterFactory.create())
.build()

ergoApiService = retrofit.create(DefaultApi::class.java)
}
return ergoApiService!!
}

fun invalidateCache() {
lastRefreshMs = 0
}

fun resetApiService() {
ergoApiService = null
}

fun refreshByUser(context: Context): Boolean {
if (System.currentTimeMillis() - lastRefreshMs > 1000L * 10) {
refreshNow(context)
Expand Down Expand Up @@ -95,7 +105,7 @@ class NodeConnector {
walletDao.getAllSync().forEach { walletConfig ->
walletConfig.publicAddress?.let {
val transactionsInfo =
ergoApiService.getApiV1AddressesP1BalanceTotal(walletConfig.publicAddress)
getOrInitErgoApiService(context).getApiV1AddressesP1BalanceTotal(walletConfig.publicAddress)
.execute()
.body()

Expand Down
37 changes: 35 additions & 2 deletions app/src/main/java/org/ergoplatform/android/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,49 @@ import android.content.Context
import androidx.appcompat.app.AppCompatDelegate

const val NAME_SHAREDPREFS = "ergowallet"
const val KEY_FIAT_CURRENCY = "fiatCurrency"
const val KEY_NODE_URL = "nodeUrl"
const val KEY_EXPLORER_API_URL = "explorerApiUrl"

private fun getSharedPrefs(context: Context) =
context.getSharedPreferences(NAME_SHAREDPREFS, Context.MODE_PRIVATE)

fun getPrefDisplayCurrency(context: Context): String {
return getSharedPrefs(context).getString("fiatCurrency", "usd") ?: ""
return getSharedPrefs(context).getString(KEY_FIAT_CURRENCY, "usd") ?: ""
}

fun saveDisplayCurrency(context: Context, currency: String) {
getSharedPrefs(context).edit().putString("fiatCurrency", currency).apply()
getSharedPrefs(context).edit().putString(KEY_FIAT_CURRENCY, currency).apply()
}

fun getPrefNodeUrl(context: Context): String {
return getSharedPrefs(context).getString(KEY_NODE_URL, StageConstants.NODE_API_ADDRESS)!!
}

fun saveNodeUrl(context: Context, nodeUrl: String) {
var savedNodeUrl = nodeUrl
if (savedNodeUrl.isEmpty()) {
savedNodeUrl = StageConstants.NODE_API_ADDRESS
} else if (!savedNodeUrl.endsWith("/")) {
savedNodeUrl += "/"
}

getSharedPrefs(context).edit().putString(KEY_NODE_URL, savedNodeUrl).apply()
}

fun getPrefExplorerApiUrl(context: Context): String {
return getSharedPrefs(context).getString(KEY_EXPLORER_API_URL, StageConstants.EXPLORER_API_ADDRESS)!!
}

fun saveExplorerApiUrl(context: Context, explorerApiUrl: String) {
var savedExplorerApiUrl = explorerApiUrl
if (savedExplorerApiUrl.isEmpty()) {
savedExplorerApiUrl = StageConstants.EXPLORER_API_ADDRESS
} else if (!savedExplorerApiUrl.endsWith("/")) {
savedExplorerApiUrl += "/"
}

getSharedPrefs(context).edit().putString(KEY_EXPLORER_API_URL, savedExplorerApiUrl).apply()
}

fun getDayNightMode(context: Context): Int {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.ergoplatform.android.settings

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.ergoplatform.android.*
import org.ergoplatform.android.databinding.FragmentConnectionSettingsBinding

/**
*
* Shows connection settings for Explorer API URL and Node URL
*/
class ConnectionSettingsDialogFragment : BottomSheetDialogFragment() {

private var _binding: FragmentConnectionSettingsBinding? = null

// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {

_binding = FragmentConnectionSettingsBinding.inflate(inflater, container, false)
return binding.root

}

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

binding.editNodeUrl.editText?.setText(getPrefNodeUrl(requireContext()))
binding.editExplorerApiUrl.editText?.setText(getPrefExplorerApiUrl(requireContext()))
binding.buttonApply.setOnClickListener { buttonApply() }
binding.editNodeUrl.editText?.setOnEditorActionListener { _, _, _ ->
buttonApply()
true
}
binding.buttonDefaults.setOnClickListener {
binding.editNodeUrl.editText?.setText(StageConstants.NODE_API_ADDRESS)
binding.editExplorerApiUrl.editText?.setText(StageConstants.EXPLORER_API_ADDRESS)
}
}

private fun buttonApply() {
val nodeUrl = binding.editNodeUrl.editText?.text?.toString() ?: ""
val explorerApiUrl = binding.editExplorerApiUrl.editText?.text?.toString() ?: ""

saveExplorerApiUrl(requireContext(), explorerApiUrl)
saveNodeUrl(requireContext(), nodeUrl)

// reset api service of NodeConnector to load new settings
NodeConnector.getInstance().resetApiService()

dismiss()
}

override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ class SettingsFragment : Fragment() {
binding.darkModeSystem.setOnClickListener { changeDayNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) }
binding.darkModeDay.setOnClickListener { changeDayNightMode(AppCompatDelegate.MODE_NIGHT_NO) }
binding.darkModeNight.setOnClickListener { changeDayNightMode(AppCompatDelegate.MODE_NIGHT_YES) }

binding.buttonConnectionSettings.setOnClickListener {
ConnectionSettingsDialogFragment().show(childFragmentManager, null)
}
}

private fun changeDayNightMode(mode: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class SendFundsFragmentDialog : FullScreenFragmentDialog(), PasswordDialogCallba

override fun onPasswordEntered(password: String?): String? {
password?.let {
val success = viewModel.startPaymentWithPassword(password)
val success = viewModel.startPaymentWithPassword(password, requireContext())
if (!success) {
return getString(R.string.error_password_wrong)
} else
Expand All @@ -185,10 +185,12 @@ class SendFundsFragmentDialog : FullScreenFragmentDialog(), PasswordDialogCallba
.setDeviceCredentialAllowed(true)
.build()

val context = requireContext()

val callback = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
try {
viewModel.startPaymentUserAuth()
viewModel.startPaymentUserAuth(context)
} catch (t: Throwable) {
hideForcedSoftKeyboard(requireContext(), binding.amount.editText!!)
Snackbar.make(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class SendFundsViewModel : ViewModel() {
}
}

fun startPaymentWithPassword(password: String): Boolean {
fun startPaymentWithPassword(password: String, context: Context): Boolean {
wallet?.secretStorage?.let {
val mnemonic: String?
try {
Expand All @@ -106,7 +106,7 @@ class SendFundsViewModel : ViewModel() {
return false
}

startPaymentWithMnemonicAsync(mnemonic)
startPaymentWithMnemonicAsync(mnemonic, context)

_lockInterface.postValue(true)

Expand All @@ -116,7 +116,7 @@ class SendFundsViewModel : ViewModel() {
return false
}

fun startPaymentUserAuth() {
fun startPaymentUserAuth(context: Context) {
// we don't handle exceptions here by intention: we throw them back to the fragment which
// will show a snackbar to give the user a hint what went wrong
wallet?.secretStorage?.let {
Expand All @@ -125,20 +125,21 @@ class SendFundsViewModel : ViewModel() {
val decryptData = AesEncryptionManager.decryptDataWithDeviceKey(it)
mnemonic = deserializeSecrets(String(decryptData!!))

startPaymentWithMnemonicAsync(mnemonic!!)
startPaymentWithMnemonicAsync(mnemonic!!, context)

_lockInterface.postValue(true)

}
}

private fun startPaymentWithMnemonicAsync(mnemonic: String) {
private fun startPaymentWithMnemonicAsync(mnemonic: String, context: Context) {
viewModelScope.launch {
val ergoTxResult: TransactionResult
withContext(Dispatchers.IO) {
ergoTxResult = sendErgoTx(
Address.create(receiverAddress), ergsToNanoErgs(amountToSend),
mnemonic, ""
mnemonic, "", 0,
getPrefNodeUrl(context), getPrefExplorerApiUrl(context)
)
}
_lockInterface.postValue(false)
Expand Down
79 changes: 79 additions & 0 deletions app/src/main/res/layout/fragment_connection_settings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:background="?attr/colorSurface">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:ignore="UselessParent">

<TextView
style="@style/TextAppearance.App.Body1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:text="@string/button_connection_settings" />

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_explorer_api_url"
style="@style/Widget.App.TextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:hint="@string/label_explorer_api_url">

<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionNext"
android:inputType="textUri"
android:maxLines="1" />

</com.google.android.material.textfield.TextInputLayout>

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/edit_node_url"
style="@style/Widget.App.TextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin"
android:hint="@string/label_node_url">

<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textUri"
android:maxLines="1" />

</com.google.android.material.textfield.TextInputLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end">

<Button
android:id="@+id/button_defaults"
style="@style/Widget.App.Button.PrimaryTint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_and_half"
android:elegantTextHeight="true"
android:text="@string/button_reset_defaults" />

<Button
android:id="@+id/button_apply"
style="@style/Widget.App.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elegantTextHeight="true"
android:text="@string/button_apply" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
Loading

0 comments on commit 0e4e10d

Please sign in to comment.