Migrate app to ViewBinding. (#6)
* Enable ViewBinding - Migrate MainActivity to ViewBinding. - Migrate HomeFragment to ViewBinding. * Migrate a batch of Fragments to ViewBinding. - Migrate BalancesFragment to ViewBinding. - Migrate CreateAccountFragment to ViewBinding. - Migrate EReceiptFragment to ViewBinding. - Migrate FilterOptionsDialog to ViewBinding. - Migrate HomeFragment to ViewBinding. * Migrate another batch of Fragments to ViewBinding. - Migrate ImportBrainkeyFragment to ViewBinding. - Migrate LicenseFragment to ViewBinding. - Migrate MerchantsFragment to ViewBinding. - Migrate NetWorthFragment to ViewBinding. - Migrate PatternSecurityLockDialog to ViewBinding. * Migrate final batch of Fragments to ViewBinding. - Migrate PINSecurityLockScreen to ViewBinding. - Migrate ReceiveTransactionFragment to ViewBinding. - Migrate SendTransactionFragment to ViewBinding. - Migrate SettingsFragment to ViewBinding. - Migrate TransactionsFragment to ViewBinding. * Migrate to the kotlin-parcelize plugin. - Get rid of the deprecated kotlin-android-extensions plugin.
This commit is contained in:
parent
cc53526038
commit
0ec0457eb7
19 changed files with 1049 additions and 597 deletions
|
@ -1,16 +1,11 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-parcelize'
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
apply plugin: 'com.google.firebase.crashlytics'
|
apply plugin: 'com.google.firebase.crashlytics'
|
||||||
|
|
||||||
// Needed for Kotlin's @Parcelize annotation
|
|
||||||
androidExtensions {
|
|
||||||
experimental = true
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion 29
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
|
@ -31,6 +26,11 @@ android {
|
||||||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
viewBinding true
|
||||||
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
debug {
|
debug {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
|
|
|
@ -12,8 +12,8 @@ import androidx.navigation.ui.navigateUp
|
||||||
import androidx.navigation.ui.onNavDestinationSelected
|
import androidx.navigation.ui.onNavDestinationSelected
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.ActivityMainBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses the AAC Navigation Component with a NavHostFragment which is the place where all Fragments are shown,
|
* Uses the AAC Navigation Component with a NavHostFragment which is the place where all Fragments are shown,
|
||||||
|
@ -21,7 +21,8 @@ import kotlinx.android.synthetic.main.activity_main.*
|
||||||
*/
|
*/
|
||||||
class MainActivity : ConnectedActivity() {
|
class MainActivity : ConnectedActivity() {
|
||||||
|
|
||||||
private lateinit var appBarConfiguration : AppBarConfiguration
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
|
|
||||||
// Handler and Runnable used to add a timer for user inaction and close the app if enough time has passed
|
// Handler and Runnable used to add a timer for user inaction and close the app if enough time has passed
|
||||||
private lateinit var mHandler: Handler
|
private lateinit var mHandler: Handler
|
||||||
|
@ -31,12 +32,14 @@ class MainActivity : ConnectedActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
// Sets the theme to night mode if it has been selected by the user
|
// Sets the theme to night mode if it has been selected by the user
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
if (PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)) {
|
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
||||||
|
) {
|
||||||
setTheme(R.style.Theme_Bitsy_Dark)
|
setTheme(R.style.Theme_Bitsy_Dark)
|
||||||
}
|
}
|
||||||
setContentView(R.layout.activity_main)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
setSupportActionBar(toolbar)
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
val host: NavHostFragment = supportFragmentManager
|
val host: NavHostFragment = supportFragmentManager
|
||||||
.findFragmentById(R.id.navHostFragment) as NavHostFragment? ?: return
|
.findFragmentById(R.id.navHostFragment) as NavHostFragment? ?: return
|
||||||
|
@ -56,7 +59,8 @@ class MainActivity : ConnectedActivity() {
|
||||||
// closes the app, if not then it just restarts the Handler (timer)
|
// closes the app, if not then it just restarts the Handler (timer)
|
||||||
mRunnable = Runnable {
|
mRunnable = Runnable {
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
if (PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)) {
|
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)
|
||||||
|
) {
|
||||||
finish()
|
finish()
|
||||||
android.os.Process.killProcess(android.os.Process.myPid())
|
android.os.Process.killProcess(android.os.Process.myPid())
|
||||||
} else
|
} else
|
||||||
|
@ -107,8 +111,8 @@ class MainActivity : ConnectedActivity() {
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
// Trick used to avoid crashes when the user is in the License or ImportBrainkey and presses the back button
|
// Trick used to avoid crashes when the user is in the License or ImportBrainkey and presses the back button
|
||||||
val currentDestination=NavHostFragment.findNavController(navHostFragment).currentDestination
|
val currentDestination = binding.navHostFragment.findNavController().currentDestination
|
||||||
when(currentDestination?.id) {
|
when (currentDestination?.id) {
|
||||||
R.id.license_dest, R.id.import_brainkey_dest -> finish()
|
R.id.license_dest, R.id.import_brainkey_dest -> finish()
|
||||||
else -> super.onBackPressed()
|
else -> super.onBackPressed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,37 +9,49 @@ import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.BalancesAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.BalancesAdapter
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentBalancesBinding
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||||
import kotlinx.android.synthetic.main.fragment_balances.*
|
|
||||||
|
|
||||||
class BalancesFragment: Fragment() {
|
class BalancesFragment : Fragment() {
|
||||||
|
|
||||||
|
private var _binding: FragmentBalancesBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mBalanceDetailViewModel: BalanceDetailViewModel
|
private lateinit var mBalanceDetailViewModel: BalanceDetailViewModel
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_balances, container, false)
|
_binding = FragmentBalancesBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
// Configure BalanceDetailViewModel to show the current balances
|
// Configure BalanceDetailViewModel to show the current balances
|
||||||
mBalanceDetailViewModel = ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
mBalanceDetailViewModel =
|
||||||
|
ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
||||||
|
|
||||||
val balancesAdapter = BalancesAdapter(context!!)
|
val balancesAdapter = BalancesAdapter(context!!)
|
||||||
rvBalances.adapter = balancesAdapter
|
binding.rvBalances.adapter = balancesAdapter
|
||||||
rvBalances.layoutManager = LinearLayoutManager(context!!)
|
binding.rvBalances.layoutManager = LinearLayoutManager(context!!)
|
||||||
rvBalances.addItemDecoration(DividerItemDecoration(context!!, DividerItemDecoration.VERTICAL))
|
binding.rvBalances.addItemDecoration(
|
||||||
|
DividerItemDecoration(context!!, DividerItemDecoration.VERTICAL)
|
||||||
|
)
|
||||||
|
|
||||||
mBalanceDetailViewModel.getAll().observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
mBalanceDetailViewModel.getAll()
|
||||||
|
.observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||||
balancesAdapter.replaceAll(balancesDetails)
|
balancesAdapter.replaceAll(balancesDetails)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,15 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.collection.LongSparseArray
|
import androidx.collection.LongSparseArray
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentCreateAccountBinding
|
||||||
|
import cy.agorise.bitsybitshareswallet.models.FaucetRequest
|
||||||
|
import cy.agorise.bitsybitshareswallet.models.FaucetResponse
|
||||||
import cy.agorise.bitsybitshareswallet.network.FaucetService
|
import cy.agorise.bitsybitshareswallet.network.FaucetService
|
||||||
|
import cy.agorise.bitsybitshareswallet.network.ServiceGenerator
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.containsDigits
|
import cy.agorise.bitsybitshareswallet.utils.containsDigits
|
||||||
import cy.agorise.bitsybitshareswallet.utils.containsVowels
|
import cy.agorise.bitsybitshareswallet.utils.containsVowels
|
||||||
|
@ -22,20 +28,14 @@ import cy.agorise.graphenej.api.calls.GetAccountByName
|
||||||
import cy.agorise.graphenej.models.AccountProperties
|
import cy.agorise.graphenej.models.AccountProperties
|
||||||
import cy.agorise.graphenej.models.JsonRpcResponse
|
import cy.agorise.graphenej.models.JsonRpcResponse
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_create_account.*
|
|
||||||
import org.bitcoinj.core.ECKey
|
import org.bitcoinj.core.ECKey
|
||||||
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
import cy.agorise.bitsybitshareswallet.models.FaucetRequest
|
|
||||||
import cy.agorise.bitsybitshareswallet.models.FaucetResponse
|
|
||||||
import cy.agorise.bitsybitshareswallet.network.ServiceGenerator
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Response
|
|
||||||
|
|
||||||
|
|
||||||
class CreateAccountFragment : BaseAccountFragment() {
|
class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
@ -49,10 +49,14 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Used when trying to validate that the account name is available
|
// Used when trying to validate that the account name is available
|
||||||
private const val RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION = 1
|
private const val RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION = 1
|
||||||
|
|
||||||
// Used when trying to obtain the info of the newly created account
|
// Used when trying to obtain the info of the newly created account
|
||||||
private const val RESPONSE_GET_ACCOUNT_BY_NAME_CREATED = 2
|
private const val RESPONSE_GET_ACCOUNT_BY_NAME_CREATED = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentCreateAccountBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mAddress: String
|
private lateinit var mAddress: String
|
||||||
|
|
||||||
/** Variables used to store the validation status of the form fields */
|
/** Variables used to store the validation status of the form fields */
|
||||||
|
@ -63,10 +67,20 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
// Map used to keep track of request and response id pairs
|
// Map used to keep track of request and response id pairs
|
||||||
private val responseMap = LongSparseArray<Int>()
|
private val responseMap = LongSparseArray<Int>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_create_account, container, false)
|
_binding = FragmentCreateAccountBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -77,7 +91,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to check the validity and availability of the user's proposed account name
|
// Use RxJava Debounce to check the validity and availability of the user's proposed account name
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietAccountName.textChanges()
|
binding.tietAccountName.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(800, TimeUnit.MILLISECONDS)
|
.debounce(800, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
@ -89,7 +103,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietPin.textChanges()
|
binding.tietPin.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
@ -101,7 +115,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to update the PIN Confirmation error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the PIN Confirmation error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietPinConfirmation.textChanges()
|
binding.tietPinConfirmation.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
@ -111,10 +125,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
btnCancel.setOnClickListener { findNavController().navigateUp() }
|
binding.btnCancel.setOnClickListener { findNavController().navigateUp() }
|
||||||
|
|
||||||
btnCreate.isEnabled = false
|
binding.btnCreate.isEnabled = false
|
||||||
btnCreate.setOnClickListener { createAccount() }
|
binding.btnCreate.setOnClickListener { createAccount() }
|
||||||
|
|
||||||
// Generating BrainKey
|
// Generating BrainKey
|
||||||
generateKeys()
|
generateKeys()
|
||||||
|
@ -123,19 +137,23 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
private fun validateAccountName(accountName: String) {
|
private fun validateAccountName(accountName: String) {
|
||||||
isAccountValidAndAvailable = false
|
isAccountValidAndAvailable = false
|
||||||
|
|
||||||
if ( !isAccountLengthValid(accountName) ) {
|
if (!isAccountLengthValid(accountName)) {
|
||||||
tilAccountName.helperText = ""
|
binding.tilAccountName.helperText = ""
|
||||||
tilAccountName.error = getString(R.string.error__invalid_account_length)
|
binding.tilAccountName.error = getString(R.string.error__invalid_account_length)
|
||||||
} else if ( !isAccountStartValid(accountName) ) {
|
} else if (!isAccountStartValid(accountName)) {
|
||||||
tilAccountName.helperText = ""
|
binding.tilAccountName.helperText = ""
|
||||||
tilAccountName.error = getString(R.string.error__invalid_account_start)
|
binding.tilAccountName.error = getString(R.string.error__invalid_account_start)
|
||||||
} else if ( !isAccountNameValid(accountName) ) {
|
} else if (!isAccountNameValid(accountName)) {
|
||||||
tilAccountName.helperText = ""
|
binding.tilAccountName.helperText = ""
|
||||||
tilAccountName.error = getString(R.string.error__invalid_account_name)
|
binding.tilAccountName.error = getString(R.string.error__invalid_account_name)
|
||||||
} else {
|
} else {
|
||||||
tilAccountName.isErrorEnabled = false
|
binding.tilAccountName.isErrorEnabled = false
|
||||||
tilAccountName.helperText = getString(R.string.text__verifying_account_availability)
|
binding.tilAccountName.helperText =
|
||||||
val id = mNetworkService?.sendMessage(GetAccountByName(accountName), GetAccountByName.REQUIRED_API)
|
getString(R.string.text__verifying_account_availability)
|
||||||
|
val id = mNetworkService?.sendMessage(
|
||||||
|
GetAccountByName(accountName),
|
||||||
|
GetAccountByName.REQUIRED_API
|
||||||
|
)
|
||||||
|
|
||||||
if (id != null)
|
if (id != null)
|
||||||
responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION)
|
responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION)
|
||||||
|
@ -170,13 +188,13 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePIN() {
|
private fun validatePIN() {
|
||||||
val pin = tietPin.text.toString()
|
val pin = binding.tietPin.text.toString()
|
||||||
|
|
||||||
if (pin.length < Constants.MIN_PIN_LENGTH) {
|
if (pin.length < Constants.MIN_PIN_LENGTH) {
|
||||||
tilPin.error = getString(R.string.error__pin_too_short)
|
binding.tilPin.error = getString(R.string.error__pin_too_short)
|
||||||
isPINValid = false
|
isPINValid = false
|
||||||
} else {
|
} else {
|
||||||
tilPin.isErrorEnabled = false
|
binding.tilPin.isErrorEnabled = false
|
||||||
isPINValid = true
|
isPINValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,13 +202,13 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePINConfirmation() {
|
private fun validatePINConfirmation() {
|
||||||
val pinConfirmation = tietPinConfirmation.text.toString()
|
val pinConfirmation = binding.tietPinConfirmation.text.toString()
|
||||||
|
|
||||||
if (pinConfirmation != tietPin.text.toString()) {
|
if (pinConfirmation != binding.tietPin.text.toString()) {
|
||||||
tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
binding.tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
||||||
isPINConfirmationValid = false
|
isPINConfirmationValid = false
|
||||||
} else {
|
} else {
|
||||||
tilPinConfirmation.isErrorEnabled = false
|
binding.tilPinConfirmation.isErrorEnabled = false
|
||||||
isPINConfirmationValid = true
|
isPINConfirmationValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +216,8 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableDisableCreateButton() {
|
private fun enableDisableCreateButton() {
|
||||||
btnCreate.isEnabled = (isPINValid && isPINConfirmationValid && isAccountValidAndAvailable)
|
binding.btnCreate.isEnabled =
|
||||||
|
(isPINValid && isPINConfirmationValid && isAccountValidAndAvailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||||
|
@ -211,7 +230,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleConnectionStatusUpdate(connectionStatusUpdate: ConnectionStatusUpdate) { }
|
override fun handleConnectionStatusUpdate(connectionStatusUpdate: ConnectionStatusUpdate) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the response from the NetworkService's GetAccountByName call to decide if the user's suggested
|
* Handles the response from the NetworkService's GetAccountByName call to decide if the user's suggested
|
||||||
|
@ -219,12 +238,12 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
*/
|
*/
|
||||||
private fun handleAccountNameValidation(result: Any?) {
|
private fun handleAccountNameValidation(result: Any?) {
|
||||||
if (result is AccountProperties) {
|
if (result is AccountProperties) {
|
||||||
tilAccountName.helperText = ""
|
binding.tilAccountName.helperText = ""
|
||||||
tilAccountName.error = getString(R.string.error__account_not_available)
|
binding.tilAccountName.error = getString(R.string.error__account_not_available)
|
||||||
isAccountValidAndAvailable = false
|
isAccountValidAndAvailable = false
|
||||||
} else {
|
} else {
|
||||||
tilAccountName.isErrorEnabled = false
|
binding.tilAccountName.isErrorEnabled = false
|
||||||
tilAccountName.helperText = getString(R.string.text__account_is_available)
|
binding.tilAccountName.helperText = getString(R.string.text__account_is_available)
|
||||||
isAccountValidAndAvailable = true
|
isAccountValidAndAvailable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +256,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
*/
|
*/
|
||||||
private fun handleAccountNameCreated(result: Any?) {
|
private fun handleAccountNameCreated(result: Any?) {
|
||||||
if (result is AccountProperties) {
|
if (result is AccountProperties) {
|
||||||
onAccountSelected(result, tietPin.text.toString())
|
onAccountSelected(result, binding.tietPin.text.toString())
|
||||||
} else {
|
} else {
|
||||||
context?.toast(getString(R.string.error__created_account_not_found))
|
context?.toast(getString(R.string.error__created_account_not_found))
|
||||||
setStateError()
|
setStateError()
|
||||||
|
@ -248,9 +267,9 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
* Sets the state to Loading, when the app is trying to create an account and waiting for the response.
|
* Sets the state to Loading, when the app is trying to create an account and waiting for the response.
|
||||||
*/
|
*/
|
||||||
private fun setStateLoading() {
|
private fun setStateLoading() {
|
||||||
btnCancel.isEnabled = false
|
binding.btnCancel.isEnabled = false
|
||||||
btnCreate.isEnabled = false
|
binding.btnCreate.isEnabled = false
|
||||||
progressBar.visibility = View.VISIBLE
|
binding.progressBar.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -258,9 +277,9 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
* the information from the newly created account.
|
* the information from the newly created account.
|
||||||
*/
|
*/
|
||||||
private fun setStateError() {
|
private fun setStateError() {
|
||||||
btnCancel.isEnabled = true
|
binding.btnCancel.isEnabled = true
|
||||||
btnCreate.isEnabled = false
|
binding.btnCreate.isEnabled = false
|
||||||
progressBar.visibility = View.GONE
|
binding.progressBar.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -270,7 +289,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
private fun createAccount() {
|
private fun createAccount() {
|
||||||
setStateLoading()
|
setStateLoading()
|
||||||
|
|
||||||
val accountName = tietAccountName.text.toString()
|
val accountName = binding.tietAccountName.text.toString()
|
||||||
val faucetRequest = FaucetRequest(accountName, mAddress, Constants.FAUCET_REFERRER)
|
val faucetRequest = FaucetRequest(accountName, mAddress, Constants.FAUCET_REFERRER)
|
||||||
|
|
||||||
val sg = ServiceGenerator(Constants.FAUCET_URL)
|
val sg = ServiceGenerator(Constants.FAUCET_URL)
|
||||||
|
@ -280,7 +299,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Execute the call asynchronously. Get a positive or negative callback.
|
// Execute the call asynchronously. Get a positive or negative callback.
|
||||||
call?.enqueue(object : Callback<FaucetResponse> {
|
call?.enqueue(object : Callback<FaucetResponse> {
|
||||||
override fun onResponse(call: Call<FaucetResponse>, response: Response<FaucetResponse>) {
|
override fun onResponse(
|
||||||
|
call: Call<FaucetResponse>,
|
||||||
|
response: Response<FaucetResponse>
|
||||||
|
) {
|
||||||
// The network call was a success and we got a response, obtain the info of the newly created account
|
// The network call was a success and we got a response, obtain the info of the newly created account
|
||||||
// with a delay to let the nodes update their information
|
// with a delay to let the nodes update their information
|
||||||
val handler = Handler()
|
val handler = Handler()
|
||||||
|
@ -307,8 +329,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
private fun getCreatedAccountInfo(faucetResponse: FaucetResponse?) {
|
private fun getCreatedAccountInfo(faucetResponse: FaucetResponse?) {
|
||||||
if (faucetResponse?.account != null) {
|
if (faucetResponse?.account != null) {
|
||||||
val id = mNetworkService?.sendMessage(GetAccountByName(faucetResponse.account?.name),
|
val id = mNetworkService?.sendMessage(
|
||||||
GetAccountByName.REQUIRED_API)
|
GetAccountByName(faucetResponse.account?.name),
|
||||||
|
GetAccountByName.REQUIRED_API
|
||||||
|
)
|
||||||
|
|
||||||
if (id != null)
|
if (id != null)
|
||||||
responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME_CREATED)
|
responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME_CREATED)
|
||||||
|
@ -320,7 +344,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
getString(R.string.error__faucet_template, "None")
|
getString(R.string.error__faucet_template, "None")
|
||||||
}
|
}
|
||||||
|
|
||||||
context?.let {context ->
|
context?.let { context ->
|
||||||
MaterialDialog(context)
|
MaterialDialog(context)
|
||||||
.title(R.string.title_error)
|
.title(R.string.title_error)
|
||||||
.message(text = content)
|
.message(text = content)
|
||||||
|
@ -338,7 +362,8 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
var reader: BufferedReader? = null
|
var reader: BufferedReader? = null
|
||||||
val dictionary: String
|
val dictionary: String
|
||||||
try {
|
try {
|
||||||
reader = BufferedReader(InputStreamReader(context!!.assets.open(BRAINKEY_FILE), "UTF-8"))
|
reader =
|
||||||
|
BufferedReader(InputStreamReader(context!!.assets.open(BRAINKEY_FILE), "UTF-8"))
|
||||||
dictionary = reader.readLine()
|
dictionary = reader.readLine()
|
||||||
|
|
||||||
val brainKeySuggestion = BrainKey.suggest(dictionary)
|
val brainKeySuggestion = BrainKey.suggest(dictionary)
|
||||||
|
@ -347,7 +372,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
Log.d(TAG, "brain key: $brainKeySuggestion")
|
Log.d(TAG, "brain key: $brainKeySuggestion")
|
||||||
Log.d(TAG, "address would be: $address")
|
Log.d(TAG, "address would be: $address")
|
||||||
mAddress = address.toString()
|
mAddress = address.toString()
|
||||||
tvBrainKey.text = mBrainKey?.brainKey
|
binding.tvBrainKey.text = mBrainKey?.brainKey
|
||||||
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "IOException while trying to generate key. Msg: " + e.message)
|
Log.e(TAG, "IOException while trying to generate key. Msg: " + e.message)
|
||||||
|
@ -364,7 +389,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
||||||
try {
|
try {
|
||||||
reader.close()
|
reader.close()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "IOException while trying to close BufferedReader. Msg: " + e.message)
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"IOException while trying to close BufferedReader. Msg: " + e.message
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,13 @@ import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentEReceiptBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||||
import cy.agorise.bitsybitshareswallet.utils.toast
|
import cy.agorise.bitsybitshareswallet.utils.toast
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.EReceiptViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.EReceiptViewModel
|
||||||
import kotlinx.android.synthetic.main.fragment_e_receipt.*
|
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.DecimalFormatSymbols
|
import java.text.DecimalFormatSymbols
|
||||||
|
@ -39,15 +38,28 @@ class EReceiptFragment : Fragment() {
|
||||||
private const val REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION = 100
|
private const val REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentEReceiptBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val args: EReceiptFragmentArgs by navArgs()
|
private val args: EReceiptFragmentArgs by navArgs()
|
||||||
|
|
||||||
private lateinit var mEReceiptViewModel: EReceiptViewModel
|
private lateinit var mEReceiptViewModel: EReceiptViewModel
|
||||||
private lateinit var mLocale: Locale
|
private lateinit var mLocale: Locale
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_e_receipt, container, false)
|
_binding = FragmentEReceiptBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -65,28 +77,30 @@ class EReceiptFragment : Fragment() {
|
||||||
|
|
||||||
mEReceiptViewModel = ViewModelProviders.of(this).get(EReceiptViewModel::class.java)
|
mEReceiptViewModel = ViewModelProviders.of(this).get(EReceiptViewModel::class.java)
|
||||||
|
|
||||||
mEReceiptViewModel.get(userId, transferId).observe(this, Observer<TransferDetail> { transferDetail ->
|
mEReceiptViewModel.get(userId, transferId)
|
||||||
|
.observe(this, Observer<TransferDetail> { transferDetail ->
|
||||||
bindTransferDetail(transferDetail)
|
bindTransferDetail(transferDetail)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindTransferDetail(transferDetail: TransferDetail) {
|
private fun bindTransferDetail(transferDetail: TransferDetail) {
|
||||||
context?.let { vPaymentDirection.setBackgroundColor(ContextCompat.getColor(it,
|
context?.let { context ->
|
||||||
if(transferDetail.direction) R.color.colorReceive else R.color.colorSend
|
val colorRes = if (transferDetail.direction) R.color.colorReceive else R.color.colorSend
|
||||||
))}
|
binding.vPaymentDirection.setBackgroundColor(ContextCompat.getColor(context, colorRes))
|
||||||
|
}
|
||||||
|
|
||||||
tvFrom.text = transferDetail.from ?: ""
|
binding.tvFrom.text = transferDetail.from ?: ""
|
||||||
tvTo.text = transferDetail.to ?: ""
|
binding.tvTo.text = transferDetail.to ?: ""
|
||||||
|
|
||||||
// Show the crypto amount correctly formatted
|
// Show the crypto amount correctly formatted
|
||||||
val df = DecimalFormat("####."+("#".repeat(transferDetail.assetPrecision)))
|
val df = DecimalFormat("####." + ("#".repeat(transferDetail.assetPrecision)))
|
||||||
df.roundingMode = RoundingMode.CEILING
|
df.roundingMode = RoundingMode.CEILING
|
||||||
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
||||||
|
|
||||||
val amount = transferDetail.assetAmount.toDouble() /
|
val amount = transferDetail.assetAmount.toDouble() /
|
||||||
Math.pow(10.toDouble(), transferDetail.assetPrecision.toDouble())
|
Math.pow(10.toDouble(), transferDetail.assetPrecision.toDouble())
|
||||||
val assetAmount = "${df.format(amount)} ${transferDetail.getUIAssetSymbol()}"
|
val assetAmount = "${df.format(amount)} ${transferDetail.getUIAssetSymbol()}"
|
||||||
tvAmount.text = assetAmount
|
binding.tvAmount.text = assetAmount
|
||||||
|
|
||||||
// Fiat equivalent
|
// Fiat equivalent
|
||||||
if (transferDetail.fiatAmount != null && transferDetail.fiatSymbol != null) {
|
if (transferDetail.fiatAmount != null && transferDetail.fiatSymbol != null) {
|
||||||
|
@ -96,20 +110,21 @@ class EReceiptFragment : Fragment() {
|
||||||
Math.pow(10.0, currency.defaultFractionDigits.toDouble())
|
Math.pow(10.0, currency.defaultFractionDigits.toDouble())
|
||||||
|
|
||||||
val equivalentValue = "${numberFormat.format(fiatEquivalent)} ${currency.currencyCode}"
|
val equivalentValue = "${numberFormat.format(fiatEquivalent)} ${currency.currencyCode}"
|
||||||
tvEquivalentValue.text = equivalentValue
|
binding.tvEquivalentValue.text = equivalentValue
|
||||||
} else {
|
} else {
|
||||||
tvEquivalentValue.text = "-"
|
binding.tvEquivalentValue.text = "-"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memo
|
// Memo
|
||||||
if (transferDetail.memo != "")
|
if (transferDetail.memo != "")
|
||||||
tvMemo.text = getString(R.string.template__memo, transferDetail.memo)
|
binding.tvMemo.text = getString(R.string.template__memo, transferDetail.memo)
|
||||||
else
|
else
|
||||||
tvMemo.visibility = View.GONE
|
binding.tvMemo.visibility = View.GONE
|
||||||
|
|
||||||
// Date
|
// Date
|
||||||
val dateFormat = SimpleDateFormat("dd MMM HH:mm:ss z", mLocale)
|
val dateFormat = SimpleDateFormat("dd MMM HH:mm:ss z", mLocale)
|
||||||
tvDate.text = getString(R.string.template__date, dateFormat.format(transferDetail.date * 1000))
|
binding.tvDate.text =
|
||||||
|
getString(R.string.template__date, dateFormat.format(transferDetail.date * 1000))
|
||||||
|
|
||||||
// Transaction #
|
// Transaction #
|
||||||
formatTransferTextView(transferDetail.id)
|
formatTransferTextView(transferDetail.id)
|
||||||
|
@ -118,11 +133,14 @@ class EReceiptFragment : Fragment() {
|
||||||
/** Formats the transfer TextView to show a link to explore the given transfer
|
/** Formats the transfer TextView to show a link to explore the given transfer
|
||||||
* in a BitShares explorer */
|
* in a BitShares explorer */
|
||||||
private fun formatTransferTextView(transferId: String) {
|
private fun formatTransferTextView(transferId: String) {
|
||||||
val tx = Html.fromHtml(getString(R.string.template__tx,
|
val tx = Html.fromHtml(
|
||||||
|
getString(
|
||||||
|
R.string.template__tx,
|
||||||
"<a href=\"http://bitshares-explorer.io/#/operations/$transferId\">$transferId</a>"
|
"<a href=\"http://bitshares-explorer.io/#/operations/$transferId\">$transferId</a>"
|
||||||
))
|
)
|
||||||
tvTransferID.text = tx
|
)
|
||||||
tvTransferID.movementMethod = LinkMovementMethod.getInstance()
|
binding.tvTransferID.text = tx
|
||||||
|
binding.tvTransferID.movementMethod = LinkMovementMethod.getInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -146,18 +164,26 @@ class EReceiptFragment : Fragment() {
|
||||||
/** Verifies if the storage permission is already granted, if that is the case then it takes the screenshot and
|
/** Verifies if the storage permission is already granted, if that is the case then it takes the screenshot and
|
||||||
* shares it but if it is not then it asks the user for that permission */
|
* shares it but if it is not then it asks the user for that permission */
|
||||||
private fun verifyStoragePermission() {
|
private fun verifyStoragePermission() {
|
||||||
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
if (ContextCompat
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// Permission is not already granted
|
// Permission is not already granted
|
||||||
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
requestPermissions(
|
||||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
|
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||||
|
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Permission is already granted
|
// Permission is already granted
|
||||||
shareEReceiptScreenshot()
|
shareEReceiptScreenshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<out String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
||||||
|
@ -174,8 +200,8 @@ class EReceiptFragment : Fragment() {
|
||||||
* sends an intent so the user can select the desired method to share the image. */
|
* sends an intent so the user can select the desired method to share the image. */
|
||||||
private fun shareEReceiptScreenshot() {
|
private fun shareEReceiptScreenshot() {
|
||||||
// Get Screenshot
|
// Get Screenshot
|
||||||
tvTransferID.text = getString(R.string.template__tx, args.transferId)
|
binding.tvTransferID.text = getString(R.string.template__tx, args.transferId)
|
||||||
val screenshot = Helper.loadBitmapFromView(container)
|
val screenshot = Helper.loadBitmapFromView(binding.container)
|
||||||
formatTransferTextView(args.transferId)
|
formatTransferTextView(args.transferId)
|
||||||
val imageUri = context?.let { Helper.saveTemporalBitmap(it, screenshot) }
|
val imageUri = context?.let { Helper.saveTemporalBitmap(it, screenshot) }
|
||||||
|
|
||||||
|
|
|
@ -6,25 +6,24 @@ import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.DialogFragment
|
import android.widget.TextView
|
||||||
import android.widget.*
|
|
||||||
import androidx.core.os.ConfigurationCompat
|
import androidx.core.os.ConfigurationCompat
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.DialogFilterOptionsBinding
|
||||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||||
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
|
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
|
||||||
import kotlinx.android.synthetic.main.dialog_filter_options.*
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.ClassCastException
|
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,12 +42,17 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
const val END_DATE_PICKER = 1
|
const val END_DATE_PICKER = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: DialogFilterOptionsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mFilterOptions: FilterOptions
|
private lateinit var mFilterOptions: FilterOptions
|
||||||
|
|
||||||
private var mCallback: OnFilterOptionsSelectedListener? = null
|
private var mCallback: OnFilterOptionsSelectedListener? = null
|
||||||
|
|
||||||
private var dateFormat: SimpleDateFormat = SimpleDateFormat("d/MMM/yyyy",
|
private var dateFormat: SimpleDateFormat = SimpleDateFormat(
|
||||||
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0])
|
"d/MMM/yyyy",
|
||||||
|
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0]
|
||||||
|
)
|
||||||
|
|
||||||
private var mBalanceDetails = ArrayList<BalanceDetail>()
|
private var mBalanceDetails = ArrayList<BalanceDetail>()
|
||||||
|
|
||||||
|
@ -59,7 +63,7 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
private lateinit var mCurrency: Currency
|
private lateinit var mCurrency: Currency
|
||||||
|
|
||||||
override fun onDateSet(which: Int, timestamp: Long) {
|
override fun onDateSet(which: Int, timestamp: Long) {
|
||||||
when(which) {
|
when (which) {
|
||||||
START_DATE_PICKER -> {
|
START_DATE_PICKER -> {
|
||||||
mFilterOptions.startDate = timestamp
|
mFilterOptions.startDate = timestamp
|
||||||
|
|
||||||
|
@ -85,10 +89,10 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
|
|
||||||
private fun updateDateTextViews() {
|
private fun updateDateTextViews() {
|
||||||
var date = Date(mFilterOptions.startDate)
|
var date = Date(mFilterOptions.startDate)
|
||||||
tvStartDate.text = dateFormat.format(date)
|
binding.tvStartDate.text = dateFormat.format(date)
|
||||||
|
|
||||||
date = Date(mFilterOptions.endDate)
|
date = Date(mFilterOptions.endDate)
|
||||||
tvEndDate.text = dateFormat.format(date)
|
binding.tvEndDate.text = dateFormat.format(date)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container Fragment must implement this interface
|
// Container Fragment must implement this interface
|
||||||
|
@ -96,8 +100,18 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
fun onFilterOptionsSelected(filterOptions: FilterOptions)
|
fun onFilterOptionsSelected(filterOptions: FilterOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
return inflater.inflate(R.layout.dialog_filter_options, container, false)
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
_binding = DialogFilterOptionsBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -112,73 +126,81 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
|
|
||||||
// Initialize Transactions direction
|
// Initialize Transactions direction
|
||||||
when (mFilterOptions.transactionsDirection) {
|
when (mFilterOptions.transactionsDirection) {
|
||||||
0 -> rbTransactionAll.isChecked = true
|
0 -> binding.rbTransactionAll.isChecked = true
|
||||||
1 -> rbTransactionSent.isChecked = true
|
1 -> binding.rbTransactionSent.isChecked = true
|
||||||
2 -> rbTransactionReceived.isChecked = true
|
2 -> binding.rbTransactionReceived.isChecked = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Date range
|
// Initialize Date range
|
||||||
cbDateRange.setOnCheckedChangeListener { _, isChecked ->
|
binding.cbDateRange.setOnCheckedChangeListener { _, isChecked ->
|
||||||
llDateRange.visibility = if(isChecked) View.GONE else View.VISIBLE }
|
binding.llDateRange.visibility = if (isChecked) View.GONE else View.VISIBLE
|
||||||
cbDateRange.isChecked = mFilterOptions.dateRangeAll
|
}
|
||||||
|
binding.cbDateRange.isChecked = mFilterOptions.dateRangeAll
|
||||||
|
|
||||||
tvStartDate.setOnClickListener(mDateClickListener)
|
binding.tvStartDate.setOnClickListener(mDateClickListener)
|
||||||
|
|
||||||
tvEndDate.setOnClickListener(mDateClickListener)
|
binding.tvEndDate.setOnClickListener(mDateClickListener)
|
||||||
|
|
||||||
updateDateTextViews()
|
updateDateTextViews()
|
||||||
|
|
||||||
// Initialize Asset
|
// Initialize Asset
|
||||||
cbAsset.setOnCheckedChangeListener { _, isChecked ->
|
binding.cbAsset.setOnCheckedChangeListener { _, isChecked ->
|
||||||
sAsset.visibility = if(isChecked) View.GONE else View.VISIBLE
|
binding.sAsset.visibility = if (isChecked) View.GONE else View.VISIBLE
|
||||||
}
|
}
|
||||||
cbAsset.isChecked = mFilterOptions.assetAll
|
binding.cbAsset.isChecked = mFilterOptions.assetAll
|
||||||
|
|
||||||
// Configure BalanceDetailViewModel to obtain the user's Balances
|
// Configure BalanceDetailViewModel to obtain the user's Balances
|
||||||
mBalanceDetailViewModel = ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
mBalanceDetailViewModel =
|
||||||
|
ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
||||||
|
|
||||||
mBalanceDetailViewModel.getAll().observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
mBalanceDetailViewModel.getAll()
|
||||||
|
.observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||||
mBalanceDetails.clear()
|
mBalanceDetails.clear()
|
||||||
mBalanceDetails.addAll(balancesDetails)
|
mBalanceDetails.addAll(balancesDetails)
|
||||||
mBalanceDetails.sortWith(
|
mBalanceDetails.sortWith(
|
||||||
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
||||||
)
|
)
|
||||||
mBalancesDetailsAdapter = BalancesDetailsAdapter(context!!, android.R.layout.simple_spinner_item, mBalanceDetails)
|
mBalancesDetailsAdapter = BalancesDetailsAdapter(
|
||||||
sAsset.adapter = mBalancesDetailsAdapter
|
context!!,
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
mBalanceDetails
|
||||||
|
)
|
||||||
|
binding.sAsset.adapter = mBalancesDetailsAdapter
|
||||||
|
|
||||||
// Try to select the selectedAssetSymbol
|
// Try to select the selectedAssetSymbol
|
||||||
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
||||||
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == mFilterOptions.asset) {
|
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == mFilterOptions.asset) {
|
||||||
sAsset.setSelection(i)
|
binding.sAsset.setSelection(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Initialize Equivalent Value
|
// Initialize Equivalent Value
|
||||||
cbEquivalentValue.setOnCheckedChangeListener { _, isChecked ->
|
binding.cbEquivalentValue.setOnCheckedChangeListener { _, isChecked ->
|
||||||
llEquivalentValue.visibility = if(isChecked) View.GONE else View.VISIBLE }
|
binding.llEquivalentValue.visibility = if (isChecked) View.GONE else View.VISIBLE
|
||||||
cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
}
|
||||||
|
binding.cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
||||||
|
|
||||||
val currencyCode = Helper.getCoingeckoSupportedCurrency(Locale.getDefault())
|
val currencyCode = Helper.getCoingeckoSupportedCurrency(Locale.getDefault())
|
||||||
mCurrency = Currency.getInstance(currencyCode)
|
mCurrency = Currency.getInstance(currencyCode)
|
||||||
|
|
||||||
val fromEquivalentValue = mFilterOptions.fromEquivalentValue /
|
val fromEquivalentValue = mFilterOptions.fromEquivalentValue /
|
||||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||||
etFromEquivalentValue.setText("$fromEquivalentValue", TextView.BufferType.EDITABLE)
|
binding.etFromEquivalentValue.setText("$fromEquivalentValue", TextView.BufferType.EDITABLE)
|
||||||
|
|
||||||
val toEquivalentValue = mFilterOptions.toEquivalentValue /
|
val toEquivalentValue = mFilterOptions.toEquivalentValue /
|
||||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||||
etToEquivalentValue.setText("$toEquivalentValue", TextView.BufferType.EDITABLE)
|
binding.etToEquivalentValue.setText("$toEquivalentValue", TextView.BufferType.EDITABLE)
|
||||||
|
|
||||||
tvEquivalentValueSymbol.text = currencyCode.toUpperCase(Locale.getDefault())
|
binding.tvEquivalentValueSymbol.text = currencyCode.toUpperCase(Locale.getDefault())
|
||||||
|
|
||||||
// Initialize transaction network fees
|
// Initialize transaction network fees
|
||||||
switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
binding.switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
||||||
|
|
||||||
// Setup cancel and filter buttons
|
// Setup cancel and filter buttons
|
||||||
btnCancel.setOnClickListener { dismiss() }
|
binding.btnCancel.setOnClickListener { dismiss() }
|
||||||
btnFilter.setOnClickListener { validateFields() }
|
binding.btnFilter.setOnClickListener { validateFields() }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -187,7 +209,10 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
// Force dialog fragment to use the full width of the screen
|
// Force dialog fragment to use the full width of the screen
|
||||||
// TODO use the same width as standard fragments
|
// TODO use the same width as standard fragments
|
||||||
val dialogWindow = dialog?.window
|
val dialogWindow = dialog?.window
|
||||||
dialogWindow?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
dialogWindow?.setLayout(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -227,17 +252,19 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
|
|
||||||
private fun validateFields() {
|
private fun validateFields() {
|
||||||
mFilterOptions.transactionsDirection = when {
|
mFilterOptions.transactionsDirection = when {
|
||||||
rbTransactionAll.isChecked -> 0
|
binding.rbTransactionAll.isChecked -> 0
|
||||||
rbTransactionSent.isChecked -> 1
|
binding.rbTransactionSent.isChecked -> 1
|
||||||
rbTransactionReceived.isChecked -> 2
|
binding.rbTransactionReceived.isChecked -> 2
|
||||||
else -> { 0 }
|
else -> {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mFilterOptions.dateRangeAll = cbDateRange.isChecked
|
mFilterOptions.dateRangeAll = binding.cbDateRange.isChecked
|
||||||
|
|
||||||
mFilterOptions.assetAll = cbAsset.isChecked
|
mFilterOptions.assetAll = binding.cbAsset.isChecked
|
||||||
|
|
||||||
val symbol = (sAsset.selectedItem as BalanceDetail?)?.symbol
|
val symbol = (binding.sAsset.selectedItem as BalanceDetail?)?.symbol
|
||||||
// If there are no assets in the spinner (the account has 0 balances or the app has not yet
|
// If there are no assets in the spinner (the account has 0 balances or the app has not yet
|
||||||
// fetched the account balances) symbol will be null, make sure that does not create a crash.
|
// fetched the account balances) symbol will be null, make sure that does not create a crash.
|
||||||
if (symbol != null)
|
if (symbol != null)
|
||||||
|
@ -245,19 +272,20 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
||||||
else
|
else
|
||||||
mFilterOptions.assetAll = true
|
mFilterOptions.assetAll = true
|
||||||
|
|
||||||
mFilterOptions.equivalentValueAll = cbEquivalentValue.isChecked
|
mFilterOptions.equivalentValueAll = binding.cbEquivalentValue.isChecked
|
||||||
|
|
||||||
mFilterOptions.fromEquivalentValue = etFromEquivalentValue.text.toString().toLong() *
|
mFilterOptions.fromEquivalentValue =
|
||||||
|
binding.etFromEquivalentValue.text.toString().toLong() *
|
||||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||||
|
|
||||||
mFilterOptions.toEquivalentValue = etToEquivalentValue.text.toString().toLong() *
|
mFilterOptions.toEquivalentValue = binding.etToEquivalentValue.text.toString().toLong() *
|
||||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||||
|
|
||||||
// Make sure ToEquivalentValue is at least 50 units bigger than FromEquivalentValue
|
// Make sure ToEquivalentValue is at least 50 units bigger than FromEquivalentValue
|
||||||
mFilterOptions.toEquivalentValue =
|
mFilterOptions.toEquivalentValue =
|
||||||
Math.max(mFilterOptions.toEquivalentValue, mFilterOptions.fromEquivalentValue + 50)
|
Math.max(mFilterOptions.toEquivalentValue, mFilterOptions.fromEquivalentValue + 50)
|
||||||
|
|
||||||
mFilterOptions.agoriseFees = switchAgoriseFees.isChecked
|
mFilterOptions.agoriseFees = binding.switchAgoriseFees.isChecked
|
||||||
|
|
||||||
mCallback!!.onFilterOptionsSelected(mFilterOptions)
|
mCallback!!.onFilterOptionsSelected(mFilterOptions)
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
@ -1,36 +1,42 @@
|
||||||
package cy.agorise.bitsybitshareswallet.fragments
|
package cy.agorise.bitsybitshareswallet.fragments
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModelProviders
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.FragmentPagerAdapter
|
import androidx.fragment.app.FragmentPagerAdapter
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.database.entities.UserAccount
|
import cy.agorise.bitsybitshareswallet.database.entities.UserAccount
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentHomeBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.UserAccountViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.UserAccountViewModel
|
||||||
import kotlinx.android.synthetic.main.fragment_home.*
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|
||||||
|
|
||||||
|
|
||||||
class HomeFragment : Fragment() {
|
class HomeFragment : Fragment() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG ="HomeFragment"
|
private const val TAG = "HomeFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentHomeBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mUserAccountViewModel: UserAccountViewModel
|
private lateinit var mUserAccountViewModel: UserAccountViewModel
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -52,13 +58,21 @@ class HomeFragment : Fragment() {
|
||||||
window?.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
window?.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||||
// Sets the status and navigation bars background color to a dark blue or just dark
|
// Sets the status and navigation bars background color to a dark blue or just dark
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
val statusBarColor = ContextCompat.getColor(context,
|
val statusBarColor = ContextCompat.getColor(
|
||||||
if (!nightMode) R.color.colorPrimaryVariant else R.color.colorStatusBarDark)
|
context,
|
||||||
|
if (!nightMode) R.color.colorPrimaryVariant else R.color.colorStatusBarDark
|
||||||
|
)
|
||||||
window?.statusBarColor = statusBarColor
|
window?.statusBarColor = statusBarColor
|
||||||
window?.navigationBarColor = statusBarColor
|
window?.navigationBarColor = statusBarColor
|
||||||
}
|
}
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_home, container, false)
|
_binding = FragmentHomeBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -82,48 +96,49 @@ class HomeFragment : Fragment() {
|
||||||
// Configure UserAccountViewModel to show the current account
|
// Configure UserAccountViewModel to show the current account
|
||||||
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
|
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
|
||||||
|
|
||||||
mUserAccountViewModel.getUserAccount(userId).observe(this, Observer<UserAccount>{ userAccount ->
|
mUserAccountViewModel.getUserAccount(userId)
|
||||||
|
.observe(this, Observer<UserAccount> { userAccount ->
|
||||||
if (userAccount != null) {
|
if (userAccount != null) {
|
||||||
tvAccountName.text = userAccount.name
|
binding.tvAccountName.text = userAccount.name
|
||||||
if (userAccount.isLtm) {
|
if (userAccount.isLtm) {
|
||||||
// Add the lightning bolt to the start of the account name if it is LTM
|
// Add the lightning bolt to the start of the account name if it is LTM
|
||||||
tvAccountName.setCompoundDrawablesWithIntrinsicBounds(
|
binding.tvAccountName.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
R.drawable.ic_ltm_account, 0, 0, 0
|
R.drawable.ic_ltm_account, 0, 0, 0
|
||||||
)
|
)
|
||||||
// Add some padding so that the lightning bolt icon is not too close to the account name text
|
// Add some padding so that the lightning bolt icon is not too close to the account name text
|
||||||
tvAccountName.compoundDrawablePadding = 12
|
binding.tvAccountName.compoundDrawablePadding = 12
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Navigate to the Receive Transaction Fragment
|
// Navigate to the Receive Transaction Fragment
|
||||||
fabReceiveTransaction.setOnClickListener (
|
binding.fabReceiveTransaction.setOnClickListener(
|
||||||
Navigation.createNavigateOnClickListener(R.id.receive_action)
|
Navigation.createNavigateOnClickListener(R.id.receive_action)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Navigate to the Send Transaction Fragment without activating the camera
|
// Navigate to the Send Transaction Fragment without activating the camera
|
||||||
fabSendTransaction.setOnClickListener(
|
binding.fabSendTransaction.setOnClickListener(
|
||||||
Navigation.createNavigateOnClickListener(R.id.send_action)
|
Navigation.createNavigateOnClickListener(R.id.send_action)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Navigate to the Send Transaction Fragment using Navigation's SafeArgs to activate the camera
|
// Navigate to the Send Transaction Fragment using Navigation's SafeArgs to activate the camera
|
||||||
fabSendTransactionCamera.setOnClickListener {
|
binding.fabSendTransactionCamera.setOnClickListener {
|
||||||
val action = HomeFragmentDirections.sendAction(true)
|
val action = HomeFragmentDirections.sendAction(true)
|
||||||
findNavController().navigate(action)
|
findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure ViewPager with PagerAdapter and TabLayout to display the Balances/NetWorth section
|
// Configure ViewPager with PagerAdapter and TabLayout to display the Balances/NetWorth section
|
||||||
val pagerAdapter = PagerAdapter(childFragmentManager)
|
val pagerAdapter = PagerAdapter(childFragmentManager)
|
||||||
viewPager.adapter = pagerAdapter
|
binding.viewPager.adapter = pagerAdapter
|
||||||
tabLayout.setupWithViewPager(viewPager)
|
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||||
// Set the pie chart icon for the third tab
|
// Set the pie chart icon for the third tab
|
||||||
tabLayout.getTabAt(2)?.setIcon(R.drawable.ic_pie_chart)
|
binding.tabLayout.getTabAt(2)?.setIcon(R.drawable.ic_pie_chart)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pager adapter to create the placeholder fragments
|
* Pager adapter to create the placeholder fragments
|
||||||
*/
|
*/
|
||||||
private inner class PagerAdapter internal constructor(fm: FragmentManager) : FragmentPagerAdapter(fm) {
|
private inner class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
|
||||||
|
|
||||||
override fun getItem(position: Int): Fragment {
|
override fun getItem(position: Int): Fragment {
|
||||||
// getItem is called to instantiate the fragment for the given page.
|
// getItem is called to instantiate the fragment for the given page.
|
||||||
|
@ -133,8 +148,12 @@ class HomeFragment : Fragment() {
|
||||||
NetWorthFragment()
|
NetWorthFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPageTitle(position: Int): CharSequence? {
|
override fun getPageTitle(position: Int): CharSequence {
|
||||||
return listOf(getString(R.string.title_balances), getString(R.string.title_net_worth), "")[position]
|
return listOf(
|
||||||
|
getString(R.string.title_balances),
|
||||||
|
getString(R.string.title_net_worth),
|
||||||
|
""
|
||||||
|
)[position]
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.navigation.Navigation
|
import androidx.navigation.Navigation
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
@ -19,9 +20,12 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.BuildConfig
|
import cy.agorise.bitsybitshareswallet.BuildConfig
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentImportBrainkeyBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.toast
|
import cy.agorise.bitsybitshareswallet.utils.toast
|
||||||
import cy.agorise.graphenej.*
|
import cy.agorise.graphenej.Address
|
||||||
|
import cy.agorise.graphenej.BrainKey
|
||||||
|
import cy.agorise.graphenej.UserAccount
|
||||||
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
||||||
import cy.agorise.graphenej.api.calls.GetAccounts
|
import cy.agorise.graphenej.api.calls.GetAccounts
|
||||||
import cy.agorise.graphenej.api.calls.GetDynamicGlobalProperties
|
import cy.agorise.graphenej.api.calls.GetDynamicGlobalProperties
|
||||||
|
@ -30,7 +34,6 @@ import cy.agorise.graphenej.models.AccountProperties
|
||||||
import cy.agorise.graphenej.models.DynamicGlobalProperties
|
import cy.agorise.graphenej.models.DynamicGlobalProperties
|
||||||
import cy.agorise.graphenej.models.JsonRpcResponse
|
import cy.agorise.graphenej.models.JsonRpcResponse
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_import_brainkey.*
|
|
||||||
import org.bitcoinj.core.ECKey
|
import org.bitcoinj.core.ECKey
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -42,6 +45,9 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
private const val TAG = "ImportBrainkeyFragment"
|
private const val TAG = "ImportBrainkeyFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentImportBrainkeyBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
/** User account associated with the key derived from the brainkey that the user just typed in */
|
/** User account associated with the key derived from the brainkey that the user just typed in */
|
||||||
private var mUserAccount: UserAccount? = null
|
private var mUserAccount: UserAccount? = null
|
||||||
|
|
||||||
|
@ -72,12 +78,22 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
/** Handler that will be used to make recurrent calls to get the latest BitShares block number*/
|
/** Handler that will be used to make recurrent calls to get the latest BitShares block number*/
|
||||||
private val mHandler = Handler()
|
private val mHandler = Handler()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
// Remove up navigation icon from the toolbar
|
// Remove up navigation icon from the toolbar
|
||||||
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||||
toolbar?.navigationIcon = null
|
toolbar?.navigationIcon = null
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_import_brainkey, container, false)
|
_binding = FragmentImportBrainkeyBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -88,7 +104,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietPin.textChanges()
|
binding.tietPin.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
@ -100,7 +116,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to update the PIN Confirmation error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the PIN Confirmation error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietPinConfirmation.textChanges()
|
binding.tietPinConfirmation.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
@ -112,7 +128,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to update the BrainKey error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the BrainKey error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietBrainKey.textChanges()
|
binding.tietBrainKey.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.map { it.toString().trim() }
|
.map { it.toString().trim() }
|
||||||
|
@ -123,14 +139,14 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
btnImport.isEnabled = false
|
binding.btnImport.isEnabled = false
|
||||||
btnImport.setOnClickListener { verifyBrainKey(false) }
|
binding.btnImport.setOnClickListener { verifyBrainKey(false) }
|
||||||
|
|
||||||
btnCreate.setOnClickListener (
|
binding.btnCreate.setOnClickListener(
|
||||||
Navigation.createNavigateOnClickListener(R.id.create_account_action)
|
Navigation.createNavigateOnClickListener(R.id.create_account_action)
|
||||||
)
|
)
|
||||||
|
|
||||||
tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
binding.tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNodesDialog(v: View) {
|
private fun showNodesDialog(v: View) {
|
||||||
|
@ -160,7 +176,13 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
)
|
)
|
||||||
|
|
||||||
mNodesDialog = MaterialDialog(v.context).show {
|
mNodesDialog = MaterialDialog(v.context).show {
|
||||||
title(text = String.format("%s v%s", getString(R.string.app_name), BuildConfig.VERSION_NAME))
|
title(
|
||||||
|
text = String.format(
|
||||||
|
"%s v%s",
|
||||||
|
getString(R.string.app_name),
|
||||||
|
BuildConfig.VERSION_NAME
|
||||||
|
)
|
||||||
|
)
|
||||||
message(text = getString(R.string.title__bitshares_nodes_dialog, "-------"))
|
message(text = getString(R.string.title__bitshares_nodes_dialog, "-------"))
|
||||||
customListAdapter(nodesAdapter as FullNodesAdapter)
|
customListAdapter(nodesAdapter as FullNodesAdapter)
|
||||||
negativeButton(android.R.string.ok)
|
negativeButton(android.R.string.ok)
|
||||||
|
@ -170,7 +192,8 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mNodesDialogLinearLayoutManager = (mNodesDialog?.getRecyclerView()?.layoutManager as LinearLayoutManager)
|
mNodesDialogLinearLayoutManager =
|
||||||
|
(mNodesDialog?.getRecyclerView()?.layoutManager as LinearLayoutManager)
|
||||||
|
|
||||||
// Registering a recurrent task used to poll for dynamic global properties requests
|
// Registering a recurrent task used to poll for dynamic global properties requests
|
||||||
mHandler.post(mRequestDynamicGlobalPropertiesTask)
|
mHandler.post(mRequestDynamicGlobalPropertiesTask)
|
||||||
|
@ -178,13 +201,13 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePIN() {
|
private fun validatePIN() {
|
||||||
val pin = tietPin.text.toString()
|
val pin = binding.tietPin.text.toString()
|
||||||
|
|
||||||
if (pin.length < Constants.MIN_PIN_LENGTH) {
|
if (pin.length < Constants.MIN_PIN_LENGTH) {
|
||||||
tilPin.error = getString(R.string.error__pin_too_short)
|
binding.tilPin.error = getString(R.string.error__pin_too_short)
|
||||||
isPINValid = false
|
isPINValid = false
|
||||||
} else {
|
} else {
|
||||||
tilPin.isErrorEnabled = false
|
binding.tilPin.isErrorEnabled = false
|
||||||
isPINValid = true
|
isPINValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,13 +215,13 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validatePINConfirmation() {
|
private fun validatePINConfirmation() {
|
||||||
val pinConfirmation = tietPinConfirmation.text.toString()
|
val pinConfirmation = binding.tietPinConfirmation.text.toString()
|
||||||
|
|
||||||
if (pinConfirmation != tietPin.text.toString()) {
|
if (pinConfirmation != binding.tietPin.text.toString()) {
|
||||||
tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
binding.tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
||||||
isPINConfirmationValid = false
|
isPINConfirmationValid = false
|
||||||
} else {
|
} else {
|
||||||
tilPinConfirmation.isErrorEnabled = false
|
binding.tilPinConfirmation.isErrorEnabled = false
|
||||||
isPINConfirmationValid = true
|
isPINConfirmationValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,10 +230,10 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
|
|
||||||
private fun validateBrainKey(brainKey: String) {
|
private fun validateBrainKey(brainKey: String) {
|
||||||
if (brainKey.isEmpty() || !brainKey.contains(" ") || brainKey.split(" ").size !in 12..16) {
|
if (brainKey.isEmpty() || !brainKey.contains(" ") || brainKey.split(" ").size !in 12..16) {
|
||||||
tilBrainKey.error = getString(R.string.error__enter_correct_brainkey)
|
binding.tilBrainKey.error = getString(R.string.error__enter_correct_brainkey)
|
||||||
isBrainKeyValid = false
|
isBrainKeyValid = false
|
||||||
} else {
|
} else {
|
||||||
tilBrainKey.isErrorEnabled = false
|
binding.tilBrainKey.isErrorEnabled = false
|
||||||
isBrainKeyValid = true
|
isBrainKeyValid = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +241,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun enableDisableImportButton() {
|
private fun enableDisableImportButton() {
|
||||||
btnImport.isEnabled = (isPINValid && isPINConfirmationValid && isBrainKeyValid)
|
binding.btnImport.isEnabled = (isPINValid && isPINConfirmationValid && isBrainKeyValid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,7 +265,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
*/
|
*/
|
||||||
private fun verifyBrainKey(switchCase: Boolean) {
|
private fun verifyBrainKey(switchCase: Boolean) {
|
||||||
//showDialog("", getString(R.string.importing_your_wallet))
|
//showDialog("", getString(R.string.importing_your_wallet))
|
||||||
val brainKey = tietBrainKey.text.toString().trim()
|
val brainKey = binding.tietBrainKey.text.toString().trim()
|
||||||
// Should we switch the brainkey case?
|
// Should we switch the brainkey case?
|
||||||
if (switchCase) {
|
if (switchCase) {
|
||||||
if (Character.isUpperCase(brainKey.toCharArray()[brainKey.length - 1])) {
|
if (Character.isUpperCase(brainKey.toCharArray()[brainKey.length - 1])) {
|
||||||
|
@ -270,7 +293,8 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
mBrainKey = BrainKey(brainKey, 0)
|
mBrainKey = BrainKey(brainKey, 0)
|
||||||
val address = Address(ECKey.fromPublicOnly(mBrainKey!!.privateKey.pubKey))
|
val address = Address(ECKey.fromPublicOnly(mBrainKey!!.privateKey.pubKey))
|
||||||
Log.d(TAG, String.format("Brainkey would generate address: %s", address.toString()))
|
Log.d(TAG, String.format("Brainkey would generate address: %s", address.toString()))
|
||||||
keyReferencesRequestId = mNetworkService?.sendMessage(GetKeyReferences(address), GetKeyReferences.REQUIRED_API)
|
keyReferencesRequestId =
|
||||||
|
mNetworkService?.sendMessage(GetKeyReferences(address), GetKeyReferences.REQUIRED_API)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -290,8 +314,14 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
} else if (response.result is DynamicGlobalProperties) {
|
} else if (response.result is DynamicGlobalProperties) {
|
||||||
val dynamicGlobalProperties = response.result as DynamicGlobalProperties
|
val dynamicGlobalProperties = response.result as DynamicGlobalProperties
|
||||||
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
||||||
val blockNumber = NumberFormat.getInstance().format(dynamicGlobalProperties.head_block_number)
|
val blockNumber =
|
||||||
mNodesDialog?.message(text = getString(R.string.title__bitshares_nodes_dialog, blockNumber))
|
NumberFormat.getInstance().format(dynamicGlobalProperties.head_block_number)
|
||||||
|
mNodesDialog?.message(
|
||||||
|
text = getString(
|
||||||
|
R.string.title__bitshares_nodes_dialog,
|
||||||
|
blockNumber
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,13 +338,17 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showConnectedState() {
|
private fun showConnectedState() {
|
||||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
resources.getDrawable(R.drawable.ic_connected, null), null)
|
null, null,
|
||||||
|
ResourcesCompat.getDrawable(resources, R.drawable.ic_connected, null), null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showDisconnectedState() {
|
private fun showDisconnectedState() {
|
||||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
resources.getDrawable(R.drawable.ic_disconnected, null), null)
|
null, null,
|
||||||
|
ResourcesCompat.getDrawable(resources, R.drawable.ic_disconnected, null), null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -371,12 +405,18 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
MaterialDialog(context!!)
|
MaterialDialog(context!!)
|
||||||
.title(R.string.dialog__account_candidates_title)
|
.title(R.string.dialog__account_candidates_title)
|
||||||
.message(R.string.dialog__account_candidates_content)
|
.message(R.string.dialog__account_candidates_content)
|
||||||
.listItemsSingleChoice (items = candidates, initialSelection = -1) { _, index, _ ->
|
.listItemsSingleChoice(
|
||||||
|
items = candidates,
|
||||||
|
initialSelection = -1
|
||||||
|
) { _, index, _ ->
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
// If one account was selected, we keep a reference to it and
|
// If one account was selected, we keep a reference to it and
|
||||||
// store the account properties
|
// store the account properties
|
||||||
mUserAccount = mUserAccountCandidates!![index]
|
mUserAccount = mUserAccountCandidates!![index]
|
||||||
onAccountSelected(accountPropertiesList[index], tietPin.text.toString())
|
onAccountSelected(
|
||||||
|
accountPropertiesList[index],
|
||||||
|
binding.tietPin.text.toString()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.positiveButton(android.R.string.ok)
|
.positiveButton(android.R.string.ok)
|
||||||
|
@ -386,7 +426,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
.cancelable(false)
|
.cancelable(false)
|
||||||
.show()
|
.show()
|
||||||
} else if (accountPropertiesList.size == 1) {
|
} else if (accountPropertiesList.size == 1) {
|
||||||
onAccountSelected(accountPropertiesList[0], tietPin.text.toString())
|
onAccountSelected(accountPropertiesList[0], binding.tietPin.text.toString())
|
||||||
} else {
|
} else {
|
||||||
context?.toast(getString(R.string.error__try_again))
|
context?.toast(getString(R.string.error__try_again))
|
||||||
}
|
}
|
||||||
|
@ -400,7 +440,10 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
if (mNetworkService != null) {
|
if (mNetworkService != null) {
|
||||||
if (mNetworkService?.isConnected == true) {
|
if (mNetworkService?.isConnected == true) {
|
||||||
mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
mNetworkService?.sendMessage(
|
||||||
|
GetDynamicGlobalProperties(),
|
||||||
|
GetDynamicGlobalProperties.REQUIRED_API
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "NetworkService exists but is not connected")
|
Log.d(TAG, "NetworkService exists but is not connected")
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentLicenseBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import kotlinx.android.synthetic.main.fragment_license.*
|
|
||||||
|
|
||||||
class LicenseFragment : Fragment() {
|
class LicenseFragment : Fragment() {
|
||||||
|
|
||||||
|
@ -19,12 +19,25 @@ class LicenseFragment : Fragment() {
|
||||||
private const val TAG = "LicenseFragment"
|
private const val TAG = "LicenseFragment"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
private var _binding: FragmentLicenseBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
// Remove up navigation icon from the toolbar
|
// Remove up navigation icon from the toolbar
|
||||||
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||||
toolbar?.navigationIcon = null
|
toolbar?.navigationIcon = null
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_license, container, false)
|
_binding = FragmentLicenseBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -41,11 +54,11 @@ class LicenseFragment : Fragment() {
|
||||||
if (agreedLicenseVersion == Constants.CURRENT_LICENSE_VERSION) {
|
if (agreedLicenseVersion == Constants.CURRENT_LICENSE_VERSION) {
|
||||||
agree()
|
agree()
|
||||||
} else {
|
} else {
|
||||||
wbLA.loadUrl("file:///android_asset/eula.html")
|
binding.wbLA.loadUrl("file:///android_asset/eula.html")
|
||||||
|
|
||||||
btnDisagree.setOnClickListener { activity?.finish() }
|
binding.btnDisagree.setOnClickListener { activity?.finish() }
|
||||||
|
|
||||||
btnAgree.setOnClickListener { agree() }
|
binding.btnAgree.setOnClickListener { agree() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +68,8 @@ class LicenseFragment : Fragment() {
|
||||||
*/
|
*/
|
||||||
private fun agree() {
|
private fun agree() {
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||||
.putInt(Constants.KEY_LAST_AGREED_LICENSE_VERSION, Constants.CURRENT_LICENSE_VERSION).apply()
|
.putInt(Constants.KEY_LAST_AGREED_LICENSE_VERSION, Constants.CURRENT_LICENSE_VERSION)
|
||||||
|
.apply()
|
||||||
|
|
||||||
findNavController().navigate(R.id.import_brainkey_action)
|
findNavController().navigate(R.id.import_brainkey_action)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import com.jakewharton.rxbinding3.appcompat.queryTextChangeEvents
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.database.entities.Merchant
|
import cy.agorise.bitsybitshareswallet.database.entities.Merchant
|
||||||
import cy.agorise.bitsybitshareswallet.database.entities.Teller
|
import cy.agorise.bitsybitshareswallet.database.entities.Teller
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentMerchantsBinding
|
||||||
import cy.agorise.bitsybitshareswallet.models.MapObject
|
import cy.agorise.bitsybitshareswallet.models.MapObject
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.MerchantClusterRenderer
|
import cy.agorise.bitsybitshareswallet.utils.MerchantClusterRenderer
|
||||||
|
@ -50,10 +51,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import io.reactivex.functions.BiFunction
|
import io.reactivex.functions.BiFunction
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_merchants.*
|
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
|
|
||||||
class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestionListener {
|
class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestionListener {
|
||||||
|
@ -74,6 +73,9 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
private const val SUGGEST_COLUMN_IMAGE_RESOURCE = "suggest_image_resource"
|
private const val SUGGEST_COLUMN_IMAGE_RESOURCE = "suggest_image_resource"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentMerchantsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private var mMap: GoogleMap? = null
|
private var mMap: GoogleMap? = null
|
||||||
|
|
||||||
private lateinit var mMerchantViewModel: MerchantViewModel
|
private lateinit var mMerchantViewModel: MerchantViewModel
|
||||||
|
@ -108,17 +110,33 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
private var statusBarSize = 0
|
private var statusBarSize = 0
|
||||||
private var navigationBarSize = 0
|
private var navigationBarSize = 0
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
// Hide the activity's Toolbar so that we can make the trick of the translucent navigation and status bars
|
// Hide the activity's Toolbar so that we can make the trick of the translucent navigation and status bars
|
||||||
val activityToolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
val activityToolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||||
activityToolbar?.visibility = View.GONE
|
activityToolbar?.visibility = View.GONE
|
||||||
|
|
||||||
// Sets the Navigation and Status bars translucent so that the map can be viewed through them
|
// Sets the Navigation and Status bars translucent so that the map can be viewed through them
|
||||||
val window = activity?.window
|
val window = activity?.window
|
||||||
window?.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
window?.setFlags(
|
||||||
window?.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
|
||||||
|
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
|
||||||
|
)
|
||||||
|
window?.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
|
||||||
|
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
|
||||||
|
)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_merchants, container, false)
|
_binding = FragmentMerchantsBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -132,17 +150,17 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
view.setOnApplyWindowInsetsListener { v, insets ->
|
view.setOnApplyWindowInsetsListener { v, insets ->
|
||||||
statusBarSize = insets.systemWindowInsetTop
|
statusBarSize = insets.systemWindowInsetTop
|
||||||
navigationBarSize = insets.systemWindowInsetBottom
|
navigationBarSize = insets.systemWindowInsetBottom
|
||||||
val layoutParams = toolbar.layoutParams as ViewGroup.MarginLayoutParams
|
val layoutParams = binding.toolbar.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
layoutParams.topMargin = statusBarSize
|
layoutParams.topMargin = statusBarSize
|
||||||
insets
|
insets
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the fragment's toolbar as the activity toolbar just for this fragment
|
// Set the fragment's toolbar as the activity toolbar just for this fragment
|
||||||
(activity as AppCompatActivity).setSupportActionBar(toolbar)
|
(activity as AppCompatActivity).setSupportActionBar(binding.toolbar)
|
||||||
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
toolbar?.setOnClickListener { dismissPopupWindow() }
|
binding.toolbar.setOnClickListener { dismissPopupWindow() }
|
||||||
|
|
||||||
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
|
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
|
||||||
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||||
|
@ -183,7 +201,11 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mPopupWindow = PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
mPopupWindow = PopupWindow(
|
||||||
|
popupView,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -192,7 +214,8 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
// Adds listener for the SearchView
|
// Adds listener for the SearchView
|
||||||
val searchItem = menu.findItem(R.id.menu_search)
|
val searchItem = menu.findItem(R.id.menu_search)
|
||||||
mSearchView = searchItem.actionView as SearchView
|
mSearchView = searchItem.actionView as SearchView
|
||||||
mSearchView?.suggestionsAdapter = SimpleCursorAdapter(context, R.layout.item_merchant_suggestion, null,
|
mSearchView?.suggestionsAdapter = SimpleCursorAdapter(
|
||||||
|
context, R.layout.item_merchant_suggestion, null,
|
||||||
arrayOf(SUGGEST_COLUMN_NAME, SUGGEST_COLUMN_ADDRESS, SUGGEST_COLUMN_IMAGE_RESOURCE),
|
arrayOf(SUGGEST_COLUMN_NAME, SUGGEST_COLUMN_ADDRESS, SUGGEST_COLUMN_IMAGE_RESOURCE),
|
||||||
intArrayOf(R.id.tvName, R.id.tvAddress, R.id.ivMarkerPin)
|
intArrayOf(R.id.tvName, R.id.tvAddress, R.id.ivMarkerPin)
|
||||||
)
|
)
|
||||||
|
@ -268,24 +291,40 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
|
|
||||||
mapObjects
|
mapObjects
|
||||||
}
|
}
|
||||||
).subscribe({mapObjects ->
|
).subscribe({ mapObjects ->
|
||||||
run {
|
run {
|
||||||
Log.d(TAG, "list with ${mapObjects.size} elements")
|
Log.d(TAG, "list with ${mapObjects.size} elements")
|
||||||
val cursor = MatrixCursor(
|
val cursor = MatrixCursor(
|
||||||
arrayOf(
|
arrayOf(
|
||||||
SUGGEST_COLUMN_ID, SUGGEST_COLUMN_LAT, SUGGEST_COLUMN_LON, SUGGEST_COLUMN_NAME,
|
SUGGEST_COLUMN_ID,
|
||||||
SUGGEST_COLUMN_ADDRESS, SUGGEST_COLUMN_IS_MERCHANT, SUGGEST_COLUMN_IMAGE_RESOURCE
|
SUGGEST_COLUMN_LAT,
|
||||||
|
SUGGEST_COLUMN_LON,
|
||||||
|
SUGGEST_COLUMN_NAME,
|
||||||
|
SUGGEST_COLUMN_ADDRESS,
|
||||||
|
SUGGEST_COLUMN_IS_MERCHANT,
|
||||||
|
SUGGEST_COLUMN_IMAGE_RESOURCE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for (mapObject in mapObjects) {
|
for (mapObject in mapObjects) {
|
||||||
cursor.addRow(arrayOf(BigInteger(mapObject._id, 16).toLong(), mapObject.lat, mapObject.lon,
|
cursor.addRow(
|
||||||
mapObject.name, mapObject.address, mapObject.isMerchant,
|
arrayOf(
|
||||||
if (mapObject.isMerchant == 1) R.drawable.ic_merchant_pin else R.drawable.ic_teller_pin))
|
BigInteger(mapObject._id, 16).toLong(),
|
||||||
|
mapObject.lat,
|
||||||
|
mapObject.lon,
|
||||||
|
mapObject.name,
|
||||||
|
mapObject.address,
|
||||||
|
mapObject.isMerchant,
|
||||||
|
if (mapObject.isMerchant == 1) R.drawable.ic_merchant_pin else R.drawable.ic_teller_pin
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
mSearchView?.suggestionsAdapter?.changeCursor(cursor)
|
mSearchView?.suggestionsAdapter?.changeCursor(cursor)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{error -> Log.e(TAG, "Error while retrieving autocomplete suggestions. Msg: $error")})
|
{ error ->
|
||||||
|
val message = "Error while retrieving autocomplete suggestions. Msg: $error"
|
||||||
|
Log.e(TAG, message)
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,7 +358,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
if (item.itemId == R.id.menu_filter) {
|
if (item.itemId == R.id.menu_filter) {
|
||||||
// Try to show or dismiss the custom popup window with the merchants and tellers switches
|
// Try to show or dismiss the custom popup window with the merchants and tellers switches
|
||||||
if (mPopupWindow?.isShowing == false) {
|
if (mPopupWindow?.isShowing == false) {
|
||||||
mPopupWindow?.showAsDropDown(toolbar, screenWidth, 8)
|
mPopupWindow?.showAsDropDown(binding.toolbar, screenWidth, 8)
|
||||||
if (mMap?.isMyLocationEnabled == true)
|
if (mMap?.isMyLocationEnabled == true)
|
||||||
mMap?.uiSettings?.isMyLocationButtonEnabled = false
|
mMap?.uiSettings?.isMyLocationButtonEnabled = false
|
||||||
} else
|
} else
|
||||||
|
@ -331,7 +370,11 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
|
|
||||||
/** Handles the result from the location permission request */
|
/** Handles the result from the location permission request */
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<out String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
if (requestCode == REQUEST_LOCATION_PERMISSION) {
|
if (requestCode == REQUEST_LOCATION_PERMISSION) {
|
||||||
|
@ -357,7 +400,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
mMap = googleMap
|
mMap = googleMap
|
||||||
|
|
||||||
// Add padding to move the controls out of the toolbar/status bar and navigation bar.
|
// Add padding to move the controls out of the toolbar/status bar and navigation bar.
|
||||||
mMap?.setPadding(0, toolbar.height + statusBarSize, 0, navigationBarSize)
|
mMap?.setPadding(0, binding.toolbar.height + statusBarSize, 0, navigationBarSize)
|
||||||
|
|
||||||
applyMapTheme()
|
applyMapTheme()
|
||||||
|
|
||||||
|
@ -407,9 +450,13 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
|
|
||||||
private fun verifyLocationPermission() {
|
private fun verifyLocationPermission() {
|
||||||
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.ACCESS_FINE_LOCATION)
|
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// Permission is not already granted
|
// Permission is not already granted
|
||||||
requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION)
|
requestPermissions(
|
||||||
|
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
|
||||||
|
REQUEST_LOCATION_PERMISSION
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Permission is already granted
|
// Permission is already granted
|
||||||
mMap?.isMyLocationEnabled = true
|
mMap?.isMyLocationEnabled = true
|
||||||
|
@ -435,7 +482,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
// Force marker to use a custom info window
|
// Force marker to use a custom info window
|
||||||
mMerchantClusterManager?.markerCollection?.setOnInfoWindowAdapter(MerchantInfoWindowAdapter())
|
mMerchantClusterManager?.markerCollection?.setOnInfoWindowAdapter(MerchantInfoWindowAdapter())
|
||||||
|
|
||||||
mMerchantViewModel.getAllMerchants().observe(this, Observer<List<Merchant>> {merchants ->
|
mMerchantViewModel.getAllMerchants().observe(this, Observer<List<Merchant>> { merchants ->
|
||||||
this.merchants.clear()
|
this.merchants.clear()
|
||||||
this.merchants.addAll(merchants)
|
this.merchants.addAll(merchants)
|
||||||
showHideMerchantsMarkers()
|
showHideMerchantsMarkers()
|
||||||
|
@ -461,7 +508,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
// Force marker to use a custom info window
|
// Force marker to use a custom info window
|
||||||
mTellerClusterManager?.markerCollection?.setOnInfoWindowAdapter(TellerInfoWindowAdapter())
|
mTellerClusterManager?.markerCollection?.setOnInfoWindowAdapter(TellerInfoWindowAdapter())
|
||||||
|
|
||||||
mMerchantViewModel.getAllTellers().observe(this, Observer<List<Teller>> {tellers ->
|
mMerchantViewModel.getAllTellers().observe(this, Observer<List<Teller>> { tellers ->
|
||||||
this.tellers.clear()
|
this.tellers.clear()
|
||||||
this.tellers.addAll(tellers)
|
this.tellers.addAll(tellers)
|
||||||
showHideTellersMarkers()
|
showHideTellersMarkers()
|
||||||
|
@ -512,8 +559,9 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
inner class MerchantInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
|
inner class MerchantInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
|
||||||
|
|
||||||
override fun getInfoWindow(marker: Marker?): View {
|
override fun getInfoWindow(marker: Marker?): View {
|
||||||
val infoWindowLayout: View = LayoutInflater.from(context).inflate(
|
val infoWindowLayout: View = LayoutInflater.from(context)
|
||||||
R.layout.marker_merch_info_window, null)
|
.inflate(R.layout.marker_merch_info_window, null)
|
||||||
|
|
||||||
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
||||||
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
||||||
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
||||||
|
@ -557,8 +605,9 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
||||||
inner class TellerInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
|
inner class TellerInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
|
||||||
|
|
||||||
override fun getInfoWindow(marker: Marker?): View {
|
override fun getInfoWindow(marker: Marker?): View {
|
||||||
val infoWindowLayout: View = LayoutInflater.from(context).inflate(
|
val infoWindowLayout: View = LayoutInflater.from(context)
|
||||||
R.layout.marker_teller_info_window, null)
|
.inflate(R.layout.marker_teller_info_window, null)
|
||||||
|
|
||||||
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
||||||
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
||||||
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
||||||
|
|
|
@ -5,16 +5,25 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentNetWorthBinding
|
||||||
|
|
||||||
class NetWorthFragment: Fragment() {
|
class NetWorthFragment : Fragment() {
|
||||||
|
|
||||||
|
private var _binding: FragmentNetWorthBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_net_worth, container, false)
|
_binding = FragmentNetWorthBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -10,11 +10,11 @@ import android.view.inputmethod.EditorInfo
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import com.jakewharton.rxbinding3.widget.textChanges
|
import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.DialogPinSecurityLockBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||||
import cy.agorise.bitsybitshareswallet.utils.hideKeyboard
|
import cy.agorise.bitsybitshareswallet.utils.hideKeyboard
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.dialog_pin_security_lock.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all the specific logic to create and confirm a new PIN or verifying the validity of the current one.
|
* Contains all the specific logic to create and confirm a new PIN or verifying the validity of the current one.
|
||||||
|
@ -25,9 +25,22 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
const val TAG = "PINSecurityLockDialog"
|
const val TAG = "PINSecurityLockDialog"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
private var _binding: DialogPinSecurityLockBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
return inflater.inflate(R.layout.dialog_pin_security_lock, container, false)
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
|
||||||
|
_binding = DialogPinSecurityLockBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private var newPIN = ""
|
private var newPIN = ""
|
||||||
|
@ -39,13 +52,13 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
crashlytics.setCustomKey(Constants.CRASHLYTICS_KEY_LAST_SCREEN, TAG)
|
crashlytics.setCustomKey(Constants.CRASHLYTICS_KEY_LAST_SCREEN, TAG)
|
||||||
|
|
||||||
// Request focus to the PIN EditText and automatically show the keyboard when the dialog appears.
|
// Request focus to the PIN EditText and automatically show the keyboard when the dialog appears.
|
||||||
tietPIN.requestFocus()
|
binding.tietPIN.requestFocus()
|
||||||
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
|
|
||||||
setupScreen()
|
setupScreen()
|
||||||
|
|
||||||
// Listens to the event when the user clicks the 'Enter' button in the keyboard and acts accordingly
|
// Listens to the event when the user clicks the 'Enter' button in the keyboard and acts accordingly
|
||||||
tietPIN.setOnEditorActionListener { v, actionId, _ ->
|
binding.tietPIN.setOnEditorActionListener { v, actionId, _ ->
|
||||||
var handled = false
|
var handled = false
|
||||||
if (actionId == EditorInfo.IME_ACTION_GO) {
|
if (actionId == EditorInfo.IME_ACTION_GO) {
|
||||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||||
|
@ -56,8 +69,8 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
if (hashedPIN == currentHashedPINPattern) {
|
if (hashedPIN == currentHashedPINPattern) {
|
||||||
// PIN is correct, proceed
|
// PIN is correct, proceed
|
||||||
resetIncorrectSecurityLockAttemptsAndTime()
|
resetIncorrectSecurityLockAttemptsAndTime()
|
||||||
tietPIN.hideKeyboard()
|
binding.tietPIN.hideKeyboard()
|
||||||
rootView.requestFocus()
|
binding.rootView.requestFocus()
|
||||||
dismiss()
|
dismiss()
|
||||||
mCallback?.onPINPatternEntered(actionIdentifier)
|
mCallback?.onPINPatternEntered(actionIdentifier)
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,7 +78,7 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
if (incorrectSecurityLockAttempts < Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
if (incorrectSecurityLockAttempts < Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
||||||
// Show the error only when the user has not reached the max attempts limit, because if that
|
// Show the error only when the user has not reached the max attempts limit, because if that
|
||||||
// is the case another error is gonna be shown in the setupScreen() method
|
// is the case another error is gonna be shown in the setupScreen() method
|
||||||
tilPIN.error = getString(R.string.error__wrong_pin)
|
binding.tilPIN.error = getString(R.string.error__wrong_pin)
|
||||||
}
|
}
|
||||||
setupScreen()
|
setupScreen()
|
||||||
}
|
}
|
||||||
|
@ -81,7 +94,7 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
} else if (currentStep == STEP_SECURITY_LOCK_CONFIRM) {
|
} else if (currentStep == STEP_SECURITY_LOCK_CONFIRM) {
|
||||||
val pinConfirm = v.text.toString().trim()
|
val pinConfirm = v.text.toString().trim()
|
||||||
if (pinConfirm != newPIN) {
|
if (pinConfirm != newPIN) {
|
||||||
tvTitle.text = getString(R.string.title__pins_dont_match)
|
binding.tvTitle.text = getString(R.string.title__pins_dont_match)
|
||||||
} else {
|
} else {
|
||||||
val salt = CryptoUtils.generateSalt()
|
val salt = CryptoUtils.generateSalt()
|
||||||
val hashedPIN = CryptoUtils.createSHA256Hash(salt + pinConfirm)
|
val hashedPIN = CryptoUtils.createSHA256Hash(salt + pinConfirm)
|
||||||
|
@ -103,20 +116,21 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
}
|
}
|
||||||
|
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietPIN.textChanges()
|
binding.tietPIN.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe {
|
.subscribe {
|
||||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY &&
|
if (currentStep == STEP_SECURITY_LOCK_VERIFY &&
|
||||||
incorrectSecurityLockAttempts < Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
incorrectSecurityLockAttempts < Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS
|
||||||
|
) {
|
||||||
// Make sure the error is removed when the user types again
|
// Make sure the error is removed when the user types again
|
||||||
tilPIN.isErrorEnabled = false
|
binding.tilPIN.isErrorEnabled = false
|
||||||
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
||||||
// Show the min length requirement for the PIN only when it has not been fulfilled
|
// Show the min length requirement for the PIN only when it has not been fulfilled
|
||||||
if (it.trim().length >= Constants.MIN_PIN_LENGTH) {
|
if (it.trim().length >= Constants.MIN_PIN_LENGTH) {
|
||||||
tilPIN.helperText = ""
|
binding.tilPIN.helperText = ""
|
||||||
} else {
|
} else {
|
||||||
tilPIN.helperText = getString(R.string.msg__min_pin_length)
|
binding.tilPIN.helperText = getString(R.string.msg__min_pin_length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,15 +140,15 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
private fun setupScreen() {
|
private fun setupScreen() {
|
||||||
when (currentStep) {
|
when (currentStep) {
|
||||||
STEP_SECURITY_LOCK_VERIFY -> {
|
STEP_SECURITY_LOCK_VERIFY -> {
|
||||||
tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
binding.tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
||||||
tvSubTitle.text = getString(R.string.msg__enter_your_pin)
|
binding.tvSubTitle.text = getString(R.string.msg__enter_your_pin)
|
||||||
tietPIN.isEnabled = true
|
binding.tietPIN.isEnabled = true
|
||||||
if (incorrectSecurityLockAttempts >= Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
if (incorrectSecurityLockAttempts >= Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
||||||
// User has entered the PIN incorrectly too many times
|
// User has entered the PIN incorrectly too many times
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now <= incorrectSecurityLockTime + Constants.INCORRECT_SECURITY_LOCK_COOLDOWN) {
|
if (now <= incorrectSecurityLockTime + Constants.INCORRECT_SECURITY_LOCK_COOLDOWN) {
|
||||||
tietPIN.setText("")
|
binding.tietPIN.setText("")
|
||||||
tietPIN.isEnabled = false
|
binding.tietPIN.isEnabled = false
|
||||||
startContDownTimer()
|
startContDownTimer()
|
||||||
} else {
|
} else {
|
||||||
resetIncorrectSecurityLockAttemptsAndTime()
|
resetIncorrectSecurityLockAttemptsAndTime()
|
||||||
|
@ -142,28 +156,28 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STEP_SECURITY_LOCK_CREATE -> {
|
STEP_SECURITY_LOCK_CREATE -> {
|
||||||
tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
binding.tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
||||||
tvSubTitle.text = getString(R.string.msg__set_a_pin)
|
binding.tvSubTitle.text = getString(R.string.msg__set_a_pin)
|
||||||
tilPIN.helperText = getString(R.string.msg__min_pin_length)
|
binding.tilPIN.helperText = getString(R.string.msg__min_pin_length)
|
||||||
tilPIN.isErrorEnabled = false
|
binding.tilPIN.isErrorEnabled = false
|
||||||
}
|
}
|
||||||
STEP_SECURITY_LOCK_CONFIRM -> {
|
STEP_SECURITY_LOCK_CONFIRM -> {
|
||||||
tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
binding.tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
||||||
tvSubTitle.text = ""
|
binding.tvSubTitle.text = ""
|
||||||
tvSubTitle.visibility = View.GONE
|
binding.tvSubTitle.visibility = View.GONE
|
||||||
tietPIN.setText("")
|
binding.tietPIN.setText("")
|
||||||
tilPIN.helperText = ""
|
binding.tilPIN.helperText = ""
|
||||||
tilPIN.isErrorEnabled = false
|
binding.tilPIN.isErrorEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTimerSecondPassed(errorMessage: String) {
|
override fun onTimerSecondPassed(errorMessage: String) {
|
||||||
tilPIN.error = errorMessage
|
binding.tilPIN.error = errorMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTimerFinished() {
|
override fun onTimerFinished() {
|
||||||
setupScreen()
|
setupScreen()
|
||||||
tilPIN.isErrorEnabled = false
|
binding.tilPIN.isErrorEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,11 +5,11 @@ import android.preference.PreferenceManager
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
|
||||||
import kotlinx.android.synthetic.main.dialog_pattern_security_lock.*
|
|
||||||
import com.andrognito.patternlockview.PatternLockView
|
import com.andrognito.patternlockview.PatternLockView
|
||||||
import com.andrognito.patternlockview.listener.PatternLockViewListener
|
import com.andrognito.patternlockview.listener.PatternLockViewListener
|
||||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.DialogPatternSecurityLockBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||||
|
|
||||||
|
@ -23,9 +23,22 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
const val TAG = "PatternSecurityLockDialog"
|
const val TAG = "PatternSecurityLockDialog"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
private var _binding: DialogPatternSecurityLockBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
return inflater.inflate(R.layout.dialog_pattern_security_lock, container, false)
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
|
||||||
|
_binding = DialogPatternSecurityLockBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private var newPattern = ""
|
private var newPattern = ""
|
||||||
|
@ -38,9 +51,9 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
|
|
||||||
setupScreen()
|
setupScreen()
|
||||||
|
|
||||||
patternLockView.addPatternLockListener(mPatternLockViewListener)
|
binding.patternLockView.addPatternLockListener(mPatternLockViewListener)
|
||||||
|
|
||||||
btnClear.setOnClickListener { setupScreen() }
|
binding.btnClear.setOnClickListener { setupScreen() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val mPatternLockViewListener = object : PatternLockViewListener {
|
private val mPatternLockViewListener = object : PatternLockViewListener {
|
||||||
|
@ -51,11 +64,11 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
setMessage("")
|
setMessage("")
|
||||||
}
|
}
|
||||||
STEP_SECURITY_LOCK_CREATE -> {
|
STEP_SECURITY_LOCK_CREATE -> {
|
||||||
btnClear.visibility = View.INVISIBLE
|
binding.btnClear.visibility = View.INVISIBLE
|
||||||
setMessage(getString(R.string.msg__release_finger))
|
setMessage(getString(R.string.msg__release_finger))
|
||||||
}
|
}
|
||||||
STEP_SECURITY_LOCK_CONFIRM -> {
|
STEP_SECURITY_LOCK_CONFIRM -> {
|
||||||
btnClear.visibility = View.INVISIBLE
|
binding.btnClear.visibility = View.INVISIBLE
|
||||||
setMessage(getString(R.string.msg__release_finger))
|
setMessage(getString(R.string.msg__release_finger))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,8 +80,10 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
|
|
||||||
override fun onComplete(pattern: List<PatternLockView.Dot>) {
|
override fun onComplete(pattern: List<PatternLockView.Dot>) {
|
||||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||||
val hashedPattern = CryptoUtils.createSHA256Hash(currentPINPatternSalt +
|
val hashedPattern = CryptoUtils.createSHA256Hash(
|
||||||
getStringPattern(pattern))
|
currentPINPatternSalt +
|
||||||
|
getStringPattern(pattern)
|
||||||
|
)
|
||||||
if (hashedPattern == currentHashedPINPattern) {
|
if (hashedPattern == currentHashedPINPattern) {
|
||||||
// Pattern is correct, proceed
|
// Pattern is correct, proceed
|
||||||
resetIncorrectSecurityLockAttemptsAndTime()
|
resetIncorrectSecurityLockAttemptsAndTime()
|
||||||
|
@ -84,17 +99,17 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
setupScreen()
|
setupScreen()
|
||||||
}
|
}
|
||||||
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
||||||
btnClear.visibility = View.VISIBLE
|
binding.btnClear.visibility = View.VISIBLE
|
||||||
if (pattern.size < 4) {
|
if (pattern.size < 4) {
|
||||||
setError(getString(R.string.error__connect_at_least_4_dots))
|
setError(getString(R.string.error__connect_at_least_4_dots))
|
||||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||||
} else {
|
} else {
|
||||||
setMessage(getString(R.string.text__pattern_recorded))
|
setMessage(getString(R.string.text__pattern_recorded))
|
||||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
||||||
patternLockView.isInputEnabled = false
|
binding.patternLockView.isInputEnabled = false
|
||||||
btnNext.isEnabled = true
|
binding.btnNext.isEnabled = true
|
||||||
newPattern = getStringPattern(pattern)
|
newPattern = getStringPattern(pattern)
|
||||||
btnNext.setOnClickListener {
|
binding.btnNext.setOnClickListener {
|
||||||
currentStep = STEP_SECURITY_LOCK_CONFIRM
|
currentStep = STEP_SECURITY_LOCK_CONFIRM
|
||||||
setupScreen()
|
setupScreen()
|
||||||
}
|
}
|
||||||
|
@ -103,14 +118,14 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
val patternConfirm = getStringPattern(pattern)
|
val patternConfirm = getStringPattern(pattern)
|
||||||
if (patternConfirm != newPattern) {
|
if (patternConfirm != newPattern) {
|
||||||
setError(getString(R.string.error__wront_pattern))
|
setError(getString(R.string.error__wront_pattern))
|
||||||
btnNext.isEnabled = false
|
binding.btnNext.isEnabled = false
|
||||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||||
} else {
|
} else {
|
||||||
setMessage(getString(R.string.msg__your_new_unlock_pattern))
|
setMessage(getString(R.string.msg__your_new_unlock_pattern))
|
||||||
patternLockView.isEnabled = false
|
binding.patternLockView.isEnabled = false
|
||||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
||||||
btnNext.isEnabled = true
|
binding.btnNext.isEnabled = true
|
||||||
btnNext.setOnClickListener {
|
binding.btnNext.setOnClickListener {
|
||||||
context?.let {
|
context?.let {
|
||||||
val salt = CryptoUtils.generateSalt()
|
val salt = CryptoUtils.generateSalt()
|
||||||
val hashedPattern = CryptoUtils.createSHA256Hash(salt + patternConfirm)
|
val hashedPattern = CryptoUtils.createSHA256Hash(salt + patternConfirm)
|
||||||
|
@ -119,7 +134,8 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
PreferenceManager.getDefaultSharedPreferences(it).edit()
|
PreferenceManager.getDefaultSharedPreferences(it).edit()
|
||||||
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPattern)
|
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPattern)
|
||||||
.putString(Constants.KEY_PIN_PATTERN_SALT, salt)
|
.putString(Constants.KEY_PIN_PATTERN_SALT, salt)
|
||||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 2).apply() // 2 -> Pattern
|
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 2)
|
||||||
|
.apply() // 2 -> Pattern
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
mCallback?.onPINPatternChanged()
|
mCallback?.onPINPatternChanged()
|
||||||
|
@ -148,17 +164,17 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
private fun setupScreen() {
|
private fun setupScreen() {
|
||||||
when (currentStep) {
|
when (currentStep) {
|
||||||
STEP_SECURITY_LOCK_VERIFY -> {
|
STEP_SECURITY_LOCK_VERIFY -> {
|
||||||
tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
binding.tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
||||||
tvSubTitle.text = getString(R.string.msg__enter_your_pattern)
|
binding.tvSubTitle.text = getString(R.string.msg__enter_your_pattern)
|
||||||
btnClear.visibility = View.GONE
|
binding.btnClear.visibility = View.GONE
|
||||||
btnNext.visibility = View.GONE
|
binding.btnNext.visibility = View.GONE
|
||||||
patternLockView.isInputEnabled = true
|
binding.patternLockView.isInputEnabled = true
|
||||||
patternLockView.isInStealthMode = true
|
binding.patternLockView.isInStealthMode = true
|
||||||
if (incorrectSecurityLockAttempts >= Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
if (incorrectSecurityLockAttempts >= Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
||||||
// User has entered the Pattern incorrectly too many times
|
// User has entered the Pattern incorrectly too many times
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now <= incorrectSecurityLockTime + Constants.INCORRECT_SECURITY_LOCK_COOLDOWN) {
|
if (now <= incorrectSecurityLockTime + Constants.INCORRECT_SECURITY_LOCK_COOLDOWN) {
|
||||||
patternLockView.isInputEnabled = false
|
binding.patternLockView.isInputEnabled = false
|
||||||
startContDownTimer()
|
startContDownTimer()
|
||||||
} else {
|
} else {
|
||||||
resetIncorrectSecurityLockAttemptsAndTime()
|
resetIncorrectSecurityLockAttemptsAndTime()
|
||||||
|
@ -166,24 +182,24 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
STEP_SECURITY_LOCK_CREATE -> {
|
STEP_SECURITY_LOCK_CREATE -> {
|
||||||
tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
binding.tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
||||||
tvSubTitle.text = getString(R.string.msg__set_a_pattern)
|
binding.tvSubTitle.text = getString(R.string.msg__set_a_pattern)
|
||||||
setMessage(getString(R.string.text__draw_an_unlock_pattern))
|
setMessage(getString(R.string.text__draw_an_unlock_pattern))
|
||||||
patternLockView.clearPattern()
|
binding.patternLockView.clearPattern()
|
||||||
patternLockView.isInputEnabled = true
|
binding.patternLockView.isInputEnabled = true
|
||||||
btnClear.visibility = View.INVISIBLE
|
binding.btnClear.visibility = View.INVISIBLE
|
||||||
btnNext.isEnabled = false
|
binding.btnNext.isEnabled = false
|
||||||
}
|
}
|
||||||
STEP_SECURITY_LOCK_CONFIRM -> {
|
STEP_SECURITY_LOCK_CONFIRM -> {
|
||||||
tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
binding.tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
||||||
tvSubTitle.text = ""
|
binding.tvSubTitle.text = ""
|
||||||
setMessage(getString(R.string.msg__draw_pattern_confirm))
|
setMessage(getString(R.string.msg__draw_pattern_confirm))
|
||||||
tvSubTitle.visibility = View.GONE
|
binding.tvSubTitle.visibility = View.GONE
|
||||||
patternLockView.clearPattern()
|
binding.patternLockView.clearPattern()
|
||||||
patternLockView.isInputEnabled = true
|
binding.patternLockView.isInputEnabled = true
|
||||||
btnClear.visibility = View.INVISIBLE
|
binding.btnClear.visibility = View.INVISIBLE
|
||||||
btnNext.isEnabled = false
|
binding.btnNext.isEnabled = false
|
||||||
btnNext.text = getString(R.string.button__confirm)
|
binding.btnNext.text = getString(R.string.button__confirm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,21 +207,21 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun setMessage(message: String) {
|
private fun setMessage(message: String) {
|
||||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||||
tvMessage.setTextAppearance(context, R.style.TextAppearance_Bitsy_Body2)
|
binding.tvMessage.setTextAppearance(context, R.style.TextAppearance_Bitsy_Body2)
|
||||||
} else {
|
} else {
|
||||||
tvMessage.setTextAppearance(R.style.TextAppearance_Bitsy_Body2)
|
binding.tvMessage.setTextAppearance(R.style.TextAppearance_Bitsy_Body2)
|
||||||
}
|
}
|
||||||
tvMessage.text = message
|
binding.tvMessage.text = message
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun setError(error: String) {
|
private fun setError(error: String) {
|
||||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
|
||||||
tvMessage.setTextAppearance(context, R.style.TextAppearance_Bitsy_Body2_Error)
|
binding.tvMessage.setTextAppearance(context, R.style.TextAppearance_Bitsy_Body2_Error)
|
||||||
} else {
|
} else {
|
||||||
tvMessage.setTextAppearance(R.style.TextAppearance_Bitsy_Body2_Error)
|
binding.tvMessage.setTextAppearance(R.style.TextAppearance_Bitsy_Body2_Error)
|
||||||
}
|
}
|
||||||
tvMessage.text = error
|
binding.tvMessage.text = error
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTimerSecondPassed(errorMessage: String) {
|
override fun onTimerSecondPassed(errorMessage: String) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.AutoSuggestAssetAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.AutoSuggestAssetAdapter
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentReceiveTransactionBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||||
import cy.agorise.bitsybitshareswallet.utils.showKeyboard
|
import cy.agorise.bitsybitshareswallet.utils.showKeyboard
|
||||||
|
@ -29,8 +30,6 @@ import cy.agorise.graphenej.api.ConnectionStatusUpdate
|
||||||
import cy.agorise.graphenej.api.calls.ListAssets
|
import cy.agorise.graphenej.api.calls.ListAssets
|
||||||
import cy.agorise.graphenej.models.JsonRpcResponse
|
import cy.agorise.graphenej.models.JsonRpcResponse
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_receive_transaction.*
|
|
||||||
import java.lang.Exception
|
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
import java.text.DecimalFormatSymbols
|
import java.text.DecimalFormatSymbols
|
||||||
|
@ -53,6 +52,9 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
private const val OTHER_ASSET = "other_asset"
|
private const val OTHER_ASSET = "other_asset"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentReceiveTransactionBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mViewModel: ReceiveTransactionViewModel
|
private lateinit var mViewModel: ReceiveTransactionViewModel
|
||||||
|
|
||||||
/** Current user account */
|
/** Current user account */
|
||||||
|
@ -75,7 +77,11 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
// Map used to keep track of request and response id pairs
|
// Map used to keep track of request and response id pairs
|
||||||
private val responseMap = LongSparseArray<Int>()
|
private val responseMap = LongSparseArray<Int>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -88,13 +94,21 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
// Sets the status and navigation bars background color to a dark green or just dark
|
// Sets the status and navigation bars background color to a dark green or just dark
|
||||||
val window = activity?.window
|
val window = activity?.window
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
val statusBarColor = ContextCompat.getColor(context,
|
val statusBarColor = ContextCompat.getColor(
|
||||||
if (!nightMode) R.color.colorReceiveDark else R.color.colorStatusBarDark)
|
context,
|
||||||
|
if (!nightMode) R.color.colorReceiveDark else R.color.colorStatusBarDark
|
||||||
|
)
|
||||||
window?.statusBarColor = statusBarColor
|
window?.statusBarColor = statusBarColor
|
||||||
window?.navigationBarColor = statusBarColor
|
window?.navigationBarColor = statusBarColor
|
||||||
}
|
}
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_receive_transaction, container, false)
|
_binding = FragmentReceiveTransactionBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -110,7 +124,7 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "")
|
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "")
|
||||||
|
|
||||||
mViewModel.getUserAccount(userId!!).observe(this,
|
mViewModel.getUserAccount(userId!!).observe(this,
|
||||||
Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount>{ user ->
|
Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount> { user ->
|
||||||
mUserAccount = UserAccount(user.id, user.name)
|
mUserAccount = UserAccount(user.id, user.name)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -121,8 +135,10 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
|
|
||||||
// Add BTS to always show a QR
|
// Add BTS to always show a QR
|
||||||
if (mAssets.isEmpty())
|
if (mAssets.isEmpty())
|
||||||
mAssets.add(cy.agorise.bitsybitshareswallet.database.entities.Asset(
|
mAssets.add(
|
||||||
"1.3.0", "BTS", 5, "", "")
|
cy.agorise.bitsybitshareswallet.database.entities.Asset(
|
||||||
|
"1.3.0", "BTS", 5, "", ""
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
mAssets.sortWith(
|
mAssets.sortWith(
|
||||||
|
@ -135,33 +151,39 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
)
|
)
|
||||||
mAssets.add(asset)
|
mAssets.add(asset)
|
||||||
|
|
||||||
mAssetsAdapter = AssetsAdapter(context!!, android.R.layout.simple_spinner_item, mAssets)
|
mAssetsAdapter =
|
||||||
spAsset.adapter = mAssetsAdapter
|
AssetsAdapter(context!!, android.R.layout.simple_spinner_item, mAssets)
|
||||||
|
binding.spAsset.adapter = mAssetsAdapter
|
||||||
|
|
||||||
// Try to select the selectedAssetSymbol
|
// Try to select the selectedAssetSymbol
|
||||||
for (i in 0 until mAssetsAdapter!!.count) {
|
for (i in 0 until mAssetsAdapter!!.count) {
|
||||||
if (mAssetsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
if (mAssetsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
||||||
spAsset.setSelection(i)
|
binding.spAsset.setSelection(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mViewModel.qrCodeBitmap.observe(this, Observer { bitmap ->
|
mViewModel.qrCodeBitmap.observe(this, Observer { bitmap ->
|
||||||
ivQR.setImageBitmap(bitmap)
|
binding.ivQR.setImageBitmap(bitmap)
|
||||||
})
|
})
|
||||||
|
|
||||||
spAsset.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
|
binding.spAsset.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) { }
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
|
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(
|
||||||
|
parent: AdapterView<*>?,
|
||||||
|
view: View?,
|
||||||
|
position: Int,
|
||||||
|
id: Long
|
||||||
|
) {
|
||||||
mAssetsAdapter?.getItem(position)?.let { asset ->
|
mAssetsAdapter?.getItem(position)?.let { asset ->
|
||||||
if (asset.id == OTHER_ASSET) {
|
if (asset.id == OTHER_ASSET) {
|
||||||
tilAsset.visibility = View.VISIBLE
|
binding.tilAsset.visibility = View.VISIBLE
|
||||||
actvAsset.showKeyboard()
|
binding.actvAsset.showKeyboard()
|
||||||
mAsset = null
|
mAsset = null
|
||||||
} else {
|
} else {
|
||||||
tilAsset.visibility = View.GONE
|
binding.tilAsset.visibility = View.GONE
|
||||||
selectedAssetSymbol = asset.symbol
|
selectedAssetSymbol = asset.symbol
|
||||||
|
|
||||||
mAsset = Asset(asset.id, asset.toString(), asset.precision)
|
mAsset = Asset(asset.id, asset.toString(), asset.precision)
|
||||||
|
@ -174,25 +196,26 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
|
|
||||||
// Use RxJava Debounce to create QR code only after the user stopped typing an amount
|
// Use RxJava Debounce to create QR code only after the user stopped typing an amount
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietAmount.textChanges()
|
binding.tietAmount.textChanges()
|
||||||
.debounce(1000, TimeUnit.MILLISECONDS)
|
.debounce(1000, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe( { updateQR() }, { mAsset = null} )
|
.subscribe({ updateQR() }, { mAsset = null })
|
||||||
)
|
)
|
||||||
|
|
||||||
// Add adapter to the Assets AutoCompleteTextView
|
// Add adapter to the Assets AutoCompleteTextView
|
||||||
mAutoSuggestAssetAdapter = AutoSuggestAssetAdapter(context!!, android.R.layout.simple_dropdown_item_1line)
|
mAutoSuggestAssetAdapter =
|
||||||
actvAsset.setAdapter(mAutoSuggestAssetAdapter)
|
AutoSuggestAssetAdapter(context!!, android.R.layout.simple_dropdown_item_1line)
|
||||||
|
binding.actvAsset.setAdapter(mAutoSuggestAssetAdapter)
|
||||||
|
|
||||||
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event and also avoid
|
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event and also avoid
|
||||||
// the first call when the View is created
|
// the first call when the View is created
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
actvAsset.textChanges()
|
binding.actvAsset.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.map { it.toString().trim().toUpperCase() }
|
.map { it.toString().trim().toUpperCase() }
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe( {
|
.subscribe({
|
||||||
if (!selectedInAutoCompleteTextView) {
|
if (!selectedInAutoCompleteTextView) {
|
||||||
mAsset = null
|
mAsset = null
|
||||||
updateQR()
|
updateQR()
|
||||||
|
@ -201,15 +224,18 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
|
|
||||||
// Get a list of assets that match the already typed string by the user
|
// Get a list of assets that match the already typed string by the user
|
||||||
if (it.length > 1 && mNetworkService != null) {
|
if (it.length > 1 && mNetworkService != null) {
|
||||||
val id = mNetworkService?.sendMessage(ListAssets(it, AUTO_SUGGEST_ASSET_LIMIT),
|
val id = mNetworkService?.sendMessage(
|
||||||
ListAssets.REQUIRED_API)
|
ListAssets(it, AUTO_SUGGEST_ASSET_LIMIT),
|
||||||
|
ListAssets.REQUIRED_API
|
||||||
|
)
|
||||||
if (id != null) responseMap.append(id, RESPONSE_LIST_ASSETS)
|
if (id != null) responseMap.append(id, RESPONSE_LIST_ASSETS)
|
||||||
}
|
}
|
||||||
}, { mAsset = null } )
|
}, { mAsset = null })
|
||||||
)
|
)
|
||||||
|
|
||||||
actvAsset.setOnItemClickListener { parent, _, position, _ ->
|
binding.actvAsset.setOnItemClickListener { parent, _, position, _ ->
|
||||||
val asset = parent.adapter.getItem(position) as cy.agorise.bitsybitshareswallet.database.entities.Asset
|
val asset =
|
||||||
|
parent.adapter.getItem(position) as cy.agorise.bitsybitshareswallet.database.entities.Asset
|
||||||
mAsset = Asset(asset.id, asset.toString(), asset.precision)
|
mAsset = Asset(asset.id, asset.toString(), asset.precision)
|
||||||
selectedInAutoCompleteTextView = true
|
selectedInAutoCompleteTextView = true
|
||||||
updateQR()
|
updateQR()
|
||||||
|
@ -261,7 +287,7 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
|
|
||||||
private fun updateQR() {
|
private fun updateQR() {
|
||||||
if (mAsset == null) {
|
if (mAsset == null) {
|
||||||
ivQR.setImageDrawable(null)
|
binding.ivQR.setImageDrawable(null)
|
||||||
// TODO clean the please pay and to text at the bottom too
|
// TODO clean the please pay and to text at the bottom too
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -270,20 +296,22 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
|
|
||||||
// Try to obtain the amount from the Amount Text Field or make it zero otherwise
|
// Try to obtain the amount from the Amount Text Field or make it zero otherwise
|
||||||
val amount: Long = try {
|
val amount: Long = try {
|
||||||
val tmpAmount = tietAmount.text.toString().toDouble()
|
val tmpAmount = binding.tietAmount.text.toString().toDouble()
|
||||||
(tmpAmount * Math.pow(10.0, asset.precision.toDouble())).toLong()
|
(tmpAmount * Math.pow(10.0, asset.precision.toDouble())).toLong()
|
||||||
}catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
val total = AssetAmount(UnsignedLong.valueOf(amount), asset)
|
val total = AssetAmount(UnsignedLong.valueOf(amount), asset)
|
||||||
val totalInDouble = Util.fromBase(total)
|
val totalInDouble = Util.fromBase(total)
|
||||||
val items = arrayOf(LineItem("transfer", 1, totalInDouble))
|
val items = arrayOf(LineItem("transfer", 1, totalInDouble))
|
||||||
val invoice = Invoice(mUserAccount?.name, "", "",
|
val invoice = Invoice(
|
||||||
asset.symbol.replaceFirst("bit", ""), items, "", "")
|
mUserAccount?.name, "", "",
|
||||||
|
asset.symbol.replaceFirst("bit", ""), items, "", ""
|
||||||
|
)
|
||||||
Log.d(TAG, "invoice: " + invoice.toJsonString())
|
Log.d(TAG, "invoice: " + invoice.toJsonString())
|
||||||
try {
|
try {
|
||||||
mViewModel.updateInvoice(invoice, min(ivQR.width, ivQR.height))
|
mViewModel.updateInvoice(invoice, min(binding.ivQR.width, binding.ivQR.height))
|
||||||
updateAmountAddressUI(amount, asset.symbol, asset.precision, mUserAccount!!.name)
|
updateAmountAddressUI(amount, asset.symbol, asset.precision, mUserAccount!!.name)
|
||||||
} catch (e: NullPointerException) {
|
} catch (e: NullPointerException) {
|
||||||
Log.e(TAG, "NullPointerException. Msg: " + e.message)
|
Log.e(TAG, "NullPointerException. Msg: " + e.message)
|
||||||
|
@ -294,11 +322,16 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
/**
|
/**
|
||||||
* Updates the UI to show the amount and account to send the payment
|
* Updates the UI to show the amount and account to send the payment
|
||||||
*/
|
*/
|
||||||
private fun updateAmountAddressUI(assetAmount: Long, assetSymbol: String, assetPrecision: Int, account: String) {
|
private fun updateAmountAddressUI(
|
||||||
|
assetAmount: Long,
|
||||||
|
assetSymbol: String,
|
||||||
|
assetPrecision: Int,
|
||||||
|
account: String
|
||||||
|
) {
|
||||||
val txtAmount: String = if (assetAmount == 0L) {
|
val txtAmount: String = if (assetAmount == 0L) {
|
||||||
getString(R.string.template__please_send, getString(R.string.text__any_amount), " ")
|
getString(R.string.template__please_send, getString(R.string.text__any_amount), " ")
|
||||||
} else {
|
} else {
|
||||||
val df = DecimalFormat("####."+("#".repeat(assetPrecision)))
|
val df = DecimalFormat("####." + ("#".repeat(assetPrecision)))
|
||||||
df.roundingMode = RoundingMode.CEILING
|
df.roundingMode = RoundingMode.CEILING
|
||||||
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
||||||
|
|
||||||
|
@ -309,8 +342,8 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
|
|
||||||
val txtAccount = getString(R.string.template__to, account)
|
val txtAccount = getString(R.string.template__to, account)
|
||||||
|
|
||||||
tvPleasePay.text = txtAmount
|
binding.tvPleasePay.text = txtAmount
|
||||||
tvTo.text = txtAccount
|
binding.tvTo.text = txtAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -332,18 +365,28 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun verifyStoragePermission() {
|
private fun verifyStoragePermission() {
|
||||||
if (ContextCompat.checkSelfPermission(activity!!, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
if (ContextCompat.checkSelfPermission(
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
activity!!,
|
||||||
|
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// Permission is not already granted
|
// Permission is not already granted
|
||||||
requestPermissions(arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
requestPermissions(
|
||||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
|
arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||||
|
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Permission is already granted
|
// Permission is already granted
|
||||||
shareQRScreenshot()
|
shareQRScreenshot()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<out String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
||||||
|
@ -367,13 +410,13 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
||||||
return
|
return
|
||||||
|
|
||||||
// Get Screenshot
|
// Get Screenshot
|
||||||
val screenshot = Helper.loadBitmapFromView(container)
|
val screenshot = Helper.loadBitmapFromView(binding.container)
|
||||||
val imageUri = Helper.saveTemporalBitmap(context!!, screenshot)
|
val imageUri = Helper.saveTemporalBitmap(context!!, screenshot)
|
||||||
|
|
||||||
// Prepare information for share intent
|
// Prepare information for share intent
|
||||||
val subject = getString(R.string.msg__invoice_subject, mUserAccount?.name)
|
val subject = getString(R.string.msg__invoice_subject, mUserAccount?.name)
|
||||||
val content = tvPleasePay.text.toString() + "\n" +
|
val content = binding.tvPleasePay.text.toString() + "\n" +
|
||||||
tvTo.text.toString()
|
binding.tvTo.text.toString()
|
||||||
|
|
||||||
// Create share intent and call it
|
// Create share intent and call it
|
||||||
val shareIntent = Intent()
|
val shareIntent = Intent()
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentSendTransactionBinding
|
||||||
import cy.agorise.bitsybitshareswallet.utils.*
|
import cy.agorise.bitsybitshareswallet.utils.*
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.SendTransactionViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.SendTransactionViewModel
|
||||||
|
@ -43,7 +44,6 @@ import cy.agorise.graphenej.models.JsonRpcResponse
|
||||||
import cy.agorise.graphenej.operations.TransferOperation
|
import cy.agorise.graphenej.operations.TransferOperation
|
||||||
import cy.agorise.graphenej.operations.TransferOperationBuilder
|
import cy.agorise.graphenej.operations.TransferOperationBuilder
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_send_transaction.*
|
|
||||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||||
import org.bitcoinj.core.DumpedPrivateKey
|
import org.bitcoinj.core.DumpedPrivateKey
|
||||||
import org.bitcoinj.core.ECKey
|
import org.bitcoinj.core.ECKey
|
||||||
|
@ -74,6 +74,9 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
private const val ACTION_SEND_TRANSFER = 1
|
private const val ACTION_SEND_TRANSFER = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentSendTransactionBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
// Navigation AAC Safe Args
|
// Navigation AAC Safe Args
|
||||||
private val args: SendTransactionFragmentArgs by navArgs()
|
private val args: SendTransactionFragmentArgs by navArgs()
|
||||||
|
|
||||||
|
@ -112,7 +115,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
/** This is one of the recipient account's public key, it will be used for memo encoding */
|
/** This is one of the recipient account's public key, it will be used for memo encoding */
|
||||||
private var destinationPublicKey: PublicKey? = null
|
private var destinationPublicKey: PublicKey? = null
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -125,13 +132,21 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
// Sets the status and navigation bars background color to a dark red or just dark
|
// Sets the status and navigation bars background color to a dark red or just dark
|
||||||
val window = activity?.window
|
val window = activity?.window
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
val statusBarColor = ContextCompat.getColor(context,
|
val statusBarColor = ContextCompat.getColor(
|
||||||
if (!nightMode) R.color.colorSendDark else R.color.colorStatusBarDark)
|
context,
|
||||||
|
if (!nightMode) R.color.colorSendDark else R.color.colorStatusBarDark
|
||||||
|
)
|
||||||
window?.statusBarColor = statusBarColor
|
window?.statusBarColor = statusBarColor
|
||||||
window?.navigationBarColor = statusBarColor
|
window?.navigationBarColor = statusBarColor
|
||||||
}
|
}
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_send_transaction, container, false)
|
_binding = FragmentSendTransactionBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -147,7 +162,7 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
mUserAccount = UserAccount(userId)
|
mUserAccount = UserAccount(userId)
|
||||||
|
|
||||||
// Configure ViewModel
|
// Configure ViewModel
|
||||||
mViewModel= ViewModelProviders.of(this).get(SendTransactionViewModel::class.java)
|
mViewModel = ViewModelProviders.of(this).get(SendTransactionViewModel::class.java)
|
||||||
|
|
||||||
mViewModel.getWIF(userId, AuthorityType.ACTIVE.ordinal).observe(this,
|
mViewModel.getWIF(userId, AuthorityType.ACTIVE.ordinal).observe(this,
|
||||||
Observer<String> { encryptedWIF ->
|
Observer<String> { encryptedWIF ->
|
||||||
|
@ -168,37 +183,43 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
fabOpenCamera.setOnClickListener { if (isCameraPreviewVisible) stopCameraPreview() else verifyCameraPermission() }
|
binding.fabOpenCamera.setOnClickListener { if (isCameraPreviewVisible) stopCameraPreview() else verifyCameraPermission() }
|
||||||
|
|
||||||
// Configure BalanceDetailViewModel to show the current balances
|
// Configure BalanceDetailViewModel to show the current balances
|
||||||
mBalanceDetailViewModel = ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
mBalanceDetailViewModel =
|
||||||
|
ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
||||||
|
|
||||||
mBalanceDetailViewModel.getAll().observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
mBalanceDetailViewModel.getAll()
|
||||||
|
.observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||||
mBalancesDetails.clear()
|
mBalancesDetails.clear()
|
||||||
mBalancesDetails.addAll(balancesDetails)
|
mBalancesDetails.addAll(balancesDetails)
|
||||||
mBalancesDetails.sortWith(
|
mBalancesDetails.sortWith(
|
||||||
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
||||||
)
|
)
|
||||||
mBalancesDetailsAdapter = BalancesDetailsAdapter(context!!, android.R.layout.simple_spinner_item, mBalancesDetails)
|
mBalancesDetailsAdapter = BalancesDetailsAdapter(
|
||||||
spAsset.adapter = mBalancesDetailsAdapter
|
context!!,
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
mBalancesDetails
|
||||||
|
)
|
||||||
|
binding.spAsset.adapter = mBalancesDetailsAdapter
|
||||||
|
|
||||||
// Try to select the selectedAssetSymbol
|
// Try to select the selectedAssetSymbol
|
||||||
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
||||||
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
||||||
spAsset.setSelection(i)
|
binding.spAsset.setSelection(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
spAsset.onItemSelectedListener = assetItemSelectedListener
|
binding.spAsset.onItemSelectedListener = assetItemSelectedListener
|
||||||
|
|
||||||
fabSendTransaction.setOnClickListener { verifySecurityLockSendTransfer() }
|
binding.fabSendTransaction.setOnClickListener { verifySecurityLockSendTransfer() }
|
||||||
fabSendTransaction.disable(R.color.lightGray)
|
binding.fabSendTransaction.disable(R.color.lightGray)
|
||||||
|
|
||||||
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event
|
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietTo.textChanges()
|
binding.tietTo.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.map { it.toString().trim() }
|
.map { it.toString().trim() }
|
||||||
|
@ -209,7 +230,7 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
// Use RxJava Debounce to update the Amount error only after the user stops writing for > 500 ms
|
// Use RxJava Debounce to update the Amount error only after the user stops writing for > 500 ms
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietAmount.textChanges()
|
binding.tietAmount.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
@ -218,13 +239,13 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
// Use RxJava Debounce to update the Memo error, to make sure it has the correct length
|
// Use RxJava Debounce to update the Memo error, to make sure it has the correct length
|
||||||
mDisposables.add(
|
mDisposables.add(
|
||||||
tietMemo.textChanges()
|
binding.tietMemo.textChanges()
|
||||||
.skipInitialValue()
|
.skipInitialValue()
|
||||||
.debounce(500, TimeUnit.MILLISECONDS)
|
.debounce(500, TimeUnit.MILLISECONDS)
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.map { it.toString().trim() }
|
.map { it.toString().trim() }
|
||||||
.subscribe {
|
.subscribe {
|
||||||
isMemoCorrect = it.length <= tilMemo.counterMaxLength
|
isMemoCorrect = it.length <= binding.tilMemo.counterMaxLength
|
||||||
enableDisableSendFAB()
|
enableDisableSendFAB()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -243,8 +264,8 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
/** Handles the selection of items in the Asset spinner, to keep track of the selectedAssetSymbol and show the
|
/** Handles the selection of items in the Asset spinner, to keep track of the selectedAssetSymbol and show the
|
||||||
* current user's balance of the selected asset. */
|
* current user's balance of the selected asset. */
|
||||||
private val assetItemSelectedListener = object : AdapterView.OnItemSelectedListener{
|
private val assetItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onNothingSelected(parent: AdapterView<*>?) { }
|
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||||
|
|
||||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||||
val balance = mBalancesDetailsAdapter!!.getItem(position)!!
|
val balance = mBalancesDetailsAdapter!!.getItem(position)!!
|
||||||
|
@ -252,8 +273,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
val amount = balance.amount.toDouble() / Math.pow(10.0, balance.precision.toDouble())
|
val amount = balance.amount.toDouble() / Math.pow(10.0, balance.precision.toDouble())
|
||||||
|
|
||||||
tvAvailableAssetAmount.text =
|
binding.tvAvailableAssetAmount.text =
|
||||||
String.format("%." + Math.min(balance.precision, 8) + "f %s", amount, balance.toString())
|
String.format(
|
||||||
|
"%." + Math.min(balance.precision, 8) + "f %s",
|
||||||
|
amount,
|
||||||
|
balance.toString()
|
||||||
|
)
|
||||||
|
|
||||||
validateAmount()
|
validateAmount()
|
||||||
}
|
}
|
||||||
|
@ -285,12 +310,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
if (result is AccountProperties) {
|
if (result is AccountProperties) {
|
||||||
mSelectedUserAccount = UserAccount(result.id, result.name)
|
mSelectedUserAccount = UserAccount(result.id, result.name)
|
||||||
destinationPublicKey = result.active.keyAuths.keys.iterator().next()
|
destinationPublicKey = result.active.keyAuths.keys.iterator().next()
|
||||||
tilTo.isErrorEnabled = false
|
binding.tilTo.isErrorEnabled = false
|
||||||
isToAccountCorrect = true
|
isToAccountCorrect = true
|
||||||
} else {
|
} else {
|
||||||
mSelectedUserAccount = null
|
mSelectedUserAccount = null
|
||||||
destinationPublicKey = null
|
destinationPublicKey = null
|
||||||
tilTo.error = getString(R.string.error__invalid_account)
|
binding.tilTo.error = getString(R.string.error__invalid_account)
|
||||||
isToAccountCorrect = false
|
isToAccountCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,7 +345,10 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
val feeAsset = Asset(Constants.CORE_ASSET)
|
val feeAsset = Asset(Constants.CORE_ASSET)
|
||||||
|
|
||||||
val id = mNetworkService?.sendMessage(GetRequiredFees(transaction!!, feeAsset), GetRequiredFees.REQUIRED_API)
|
val id = mNetworkService?.sendMessage(
|
||||||
|
GetRequiredFees(transaction!!, feeAsset),
|
||||||
|
GetRequiredFees.REQUIRED_API
|
||||||
|
)
|
||||||
if (id != null) responseMap.append(id, RESPONSE_GET_REQUIRED_FEES)
|
if (id != null) responseMap.append(id, RESPONSE_GET_REQUIRED_FEES)
|
||||||
} else {
|
} else {
|
||||||
context?.toast(getString(R.string.msg__transaction_not_sent))
|
context?.toast(getString(R.string.msg__transaction_not_sent))
|
||||||
|
@ -334,7 +362,10 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
Log.d(TAG, "GetRequiredFees: " + transaction.toString())
|
Log.d(TAG, "GetRequiredFees: " + transaction.toString())
|
||||||
transaction!!.setFees(result as List<AssetAmount>) // TODO find how to remove this warning
|
transaction!!.setFees(result as List<AssetAmount>) // TODO find how to remove this warning
|
||||||
|
|
||||||
val id = mNetworkService?.sendMessage(BroadcastTransaction(transaction), BroadcastTransaction.REQUIRED_API)
|
val id = mNetworkService?.sendMessage(
|
||||||
|
BroadcastTransaction(transaction),
|
||||||
|
BroadcastTransaction.REQUIRED_API
|
||||||
|
)
|
||||||
if (id != null) responseMap.append(id, RESPONSE_BROADCAST_TRANSACTION)
|
if (id != null) responseMap.append(id, RESPONSE_BROADCAST_TRANSACTION)
|
||||||
} else {
|
} else {
|
||||||
context?.toast(getString(R.string.msg__transaction_not_sent))
|
context?.toast(getString(R.string.msg__transaction_not_sent))
|
||||||
|
@ -357,9 +388,13 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
/** Verifies if the user has already granted the Camera permission, if not the asks for it */
|
/** Verifies if the user has already granted the Camera permission, if not the asks for it */
|
||||||
private fun verifyCameraPermission() {
|
private fun verifyCameraPermission() {
|
||||||
if (ContextCompat.checkSelfPermission(activity!!, android.Manifest.permission.CAMERA)
|
if (ContextCompat.checkSelfPermission(activity!!, android.Manifest.permission.CAMERA)
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// Permission is not already granted
|
// Permission is not already granted
|
||||||
requestPermissions(arrayOf(android.Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
|
requestPermissions(
|
||||||
|
arrayOf(android.Manifest.permission.CAMERA),
|
||||||
|
REQUEST_CAMERA_PERMISSION
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Permission is already granted
|
// Permission is already granted
|
||||||
startCameraPreview()
|
startCameraPreview()
|
||||||
|
@ -367,7 +402,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handles the result from the camera permission request */
|
/** Handles the result from the camera permission request */
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<out String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
if (requestCode == REQUEST_CAMERA_PERMISSION) {
|
if (requestCode == REQUEST_CAMERA_PERMISSION) {
|
||||||
|
@ -381,27 +420,27 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startCameraPreview() {
|
private fun startCameraPreview() {
|
||||||
cameraPreview.visibility = View.VISIBLE
|
binding.cameraPreview.visibility = View.VISIBLE
|
||||||
fabOpenCamera.setImageResource(R.drawable.ic_close)
|
binding.fabOpenCamera.setImageResource(R.drawable.ic_close)
|
||||||
isCameraPreviewVisible = true
|
isCameraPreviewVisible = true
|
||||||
|
|
||||||
// Configure QR scanner
|
// Configure QR scanner
|
||||||
cameraPreview.setFormats(listOf(BarcodeFormat.QR_CODE))
|
binding.cameraPreview.setFormats(listOf(BarcodeFormat.QR_CODE))
|
||||||
cameraPreview.setAspectTolerance(0.5f)
|
binding.cameraPreview.setAspectTolerance(0.5f)
|
||||||
cameraPreview.setAutoFocus(true)
|
binding.cameraPreview.setAutoFocus(true)
|
||||||
cameraPreview.setLaserColor(R.color.colorSecondary)
|
binding.cameraPreview.setLaserColor(R.color.colorSecondary)
|
||||||
cameraPreview.setMaskColor(R.color.colorSecondary)
|
binding.cameraPreview.setMaskColor(R.color.colorSecondary)
|
||||||
cameraPreview.setResultHandler(this)
|
binding.cameraPreview.setResultHandler(this)
|
||||||
cameraPreview.startCamera()
|
binding.cameraPreview.startCamera()
|
||||||
|
|
||||||
cameraPreview.scrollY = holderCamera.width / 6
|
binding.cameraPreview.scrollY = binding.holderCamera.width / 6
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopCameraPreview() {
|
private fun stopCameraPreview() {
|
||||||
cameraPreview.visibility = View.INVISIBLE
|
binding.cameraPreview.visibility = View.INVISIBLE
|
||||||
fabOpenCamera.setImageResource(R.drawable.ic_camera)
|
binding.fabOpenCamera.setImageResource(R.drawable.ic_camera)
|
||||||
isCameraPreviewVisible = false
|
isCameraPreviewVisible = false
|
||||||
cameraPreview.stopCamera()
|
binding.cameraPreview.stopCamera()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handles the result of the QR code read from the camera **/
|
/** Handles the result of the QR code read from the camera **/
|
||||||
|
@ -417,12 +456,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
Log.d(TAG, "QR Code read: " + invoice.toJsonString())
|
Log.d(TAG, "QR Code read: " + invoice.toJsonString())
|
||||||
|
|
||||||
tietTo.setText(invoice.to)
|
binding.tietTo.setText(invoice.to)
|
||||||
|
|
||||||
if (invoice.memo != null) {
|
if (invoice.memo != null) {
|
||||||
tietMemo.setText(invoice.memo)
|
binding.tietMemo.setText(invoice.memo)
|
||||||
if (invoice.memo.startsWith("PP"))
|
if (invoice.memo.startsWith("PP"))
|
||||||
tietMemo.isEnabled = false
|
binding.tietMemo.isEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
var balanceDetail: BalanceDetail? = null
|
var balanceDetail: BalanceDetail? = null
|
||||||
|
@ -432,8 +471,9 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
if (mBalancesDetailsAdapter?.getItem(i)?.symbol == invoice.currency.toUpperCase() ||
|
if (mBalancesDetailsAdapter?.getItem(i)?.symbol == invoice.currency.toUpperCase() ||
|
||||||
(invoice.currency.startsWith("bit", true) &&
|
(invoice.currency.startsWith("bit", true) &&
|
||||||
invoice.currency.replaceFirst("bit", "").toUpperCase() ==
|
invoice.currency.replaceFirst("bit", "").toUpperCase() ==
|
||||||
mBalancesDetailsAdapter?.getItem(i)?.symbol)) {
|
mBalancesDetailsAdapter?.getItem(i)?.symbol)
|
||||||
spAsset.setSelection(i)
|
) {
|
||||||
|
binding.spAsset.setSelection(i)
|
||||||
balanceDetail = mBalancesDetailsAdapter?.getItem(i)
|
balanceDetail = mBalancesDetailsAdapter?.getItem(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -442,8 +482,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
// If the user does not own any of the requested asset then show a SnackBar to explain the issue and
|
// If the user does not own any of the requested asset then show a SnackBar to explain the issue and
|
||||||
// return early to avoid filling the asset field
|
// return early to avoid filling the asset field
|
||||||
if (balanceDetail == null) {
|
if (balanceDetail == null) {
|
||||||
Snackbar.make(rootView, getString(R.string.error__you_dont_own_asset, invoice.currency.toUpperCase()),
|
Snackbar.make(
|
||||||
Snackbar.LENGTH_INDEFINITE).setAction(android.R.string.ok) { }.show()
|
binding.rootView,
|
||||||
|
getString(R.string.error__you_dont_own_asset, invoice.currency.toUpperCase()),
|
||||||
|
Snackbar.LENGTH_INDEFINITE
|
||||||
|
).setAction(android.R.string.ok) { }.show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,12 +500,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
val df = DecimalFormat("####." + "#".repeat(balanceDetail.precision))
|
val df = DecimalFormat("####." + "#".repeat(balanceDetail.precision))
|
||||||
df.roundingMode = RoundingMode.CEILING
|
df.roundingMode = RoundingMode.CEILING
|
||||||
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
||||||
tietAmount.setText(df.format(amount))
|
binding.tietAmount.setText(df.format(amount))
|
||||||
} else {
|
} else {
|
||||||
tietAmount.setText("")
|
binding.tietAmount.setText("")
|
||||||
}
|
}
|
||||||
|
|
||||||
}catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d(TAG, "Invoice error: " + e.message)
|
Log.d(TAG, "Invoice error: " + e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,15 +516,19 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
*/
|
*/
|
||||||
private fun validateAccount(accountName: String) {
|
private fun validateAccount(accountName: String) {
|
||||||
isToAccountCorrect = false
|
isToAccountCorrect = false
|
||||||
val id = mNetworkService?.sendMessage(GetAccountByName(accountName), GetAccountByName.REQUIRED_API)
|
val id = mNetworkService?.sendMessage(
|
||||||
|
GetAccountByName(accountName),
|
||||||
|
GetAccountByName.REQUIRED_API
|
||||||
|
)
|
||||||
if (id != null) responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME)
|
if (id != null) responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateAmount() {
|
private fun validateAmount() {
|
||||||
val txtAmount = tietAmount.text.toString().replace(",", ".")
|
val txtAmount = binding.tietAmount.text.toString().replace(",", ".")
|
||||||
|
|
||||||
if (mBalancesDetailsAdapter?.isEmpty != false) return
|
if (mBalancesDetailsAdapter?.isEmpty != false) return
|
||||||
val balance = mBalancesDetailsAdapter?.getItem(spAsset.selectedItemPosition) ?: return
|
val balance =
|
||||||
|
mBalancesDetailsAdapter?.getItem(binding.spAsset.selectedItemPosition) ?: return
|
||||||
val currentAmount = balance.amount.toDouble() / Math.pow(10.0, balance.precision.toDouble())
|
val currentAmount = balance.amount.toDouble() / Math.pow(10.0, balance.precision.toDouble())
|
||||||
|
|
||||||
val amount: Double = try {
|
val amount: Double = try {
|
||||||
|
@ -492,15 +539,15 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
when {
|
when {
|
||||||
currentAmount < amount -> {
|
currentAmount < amount -> {
|
||||||
tilAmount.error = getString(R.string.error__not_enough_funds)
|
binding.tilAmount.error = getString(R.string.error__not_enough_funds)
|
||||||
isAmountCorrect = false
|
isAmountCorrect = false
|
||||||
}
|
}
|
||||||
amount == 0.0 -> {
|
amount == 0.0 -> {
|
||||||
tilAmount.isErrorEnabled = false
|
binding.tilAmount.isErrorEnabled = false
|
||||||
isAmountCorrect = false
|
isAmountCorrect = false
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
tilAmount.isErrorEnabled = false
|
binding.tilAmount.isErrorEnabled = false
|
||||||
isAmountCorrect = true
|
isAmountCorrect = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -510,11 +557,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
private fun enableDisableSendFAB() {
|
private fun enableDisableSendFAB() {
|
||||||
if (isToAccountCorrect && isAmountCorrect && isMemoCorrect) {
|
if (isToAccountCorrect && isAmountCorrect && isMemoCorrect) {
|
||||||
fabSendTransaction.enable(R.color.colorSend)
|
binding.fabSendTransaction.enable(R.color.colorSend)
|
||||||
vSend.setBackgroundResource(R.drawable.send_fab_background)
|
binding.vSend.setBackgroundResource(R.drawable.send_fab_background)
|
||||||
} else {
|
} else {
|
||||||
fabSendTransaction.disable(R.color.lightGray)
|
binding.fabSendTransaction.disable(R.color.lightGray)
|
||||||
vSend.setBackgroundResource(R.drawable.send_fab_background_disabled)
|
binding.vSend.setBackgroundResource(R.drawable.send_fab_background_disabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,8 +575,10 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
|
|
||||||
// Args used for both PIN and Pattern options
|
// Args used for both PIN and Pattern options
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
args.putInt(
|
||||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY)
|
BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||||
|
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY
|
||||||
|
)
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_SEND_TRANSFER)
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_SEND_TRANSFER)
|
||||||
|
|
||||||
when (securityLockSelected) {
|
when (securityLockSelected) {
|
||||||
|
@ -556,15 +605,16 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPINPatternChanged() { /* Do nothing */ }
|
override fun onPINPatternChanged() { /* Do nothing */
|
||||||
|
}
|
||||||
|
|
||||||
/** Starts the Send Transfer operation procedure, creating a [TransferOperation] and sending a call to the
|
/** Starts the Send Transfer operation procedure, creating a [TransferOperation] and sending a call to the
|
||||||
* NetworkService to obtain the [DynamicGlobalProperties] object needed to successfully send a Transfer */
|
* NetworkService to obtain the [DynamicGlobalProperties] object needed to successfully send a Transfer */
|
||||||
private fun startSendTransferOperation() {
|
private fun startSendTransferOperation() {
|
||||||
// Create TransferOperation
|
// Create TransferOperation
|
||||||
if (mNetworkService?.isConnected == true) {
|
if (mNetworkService?.isConnected == true) {
|
||||||
val balance = mBalancesDetailsAdapter!!.getItem(spAsset.selectedItemPosition)!!
|
val balance = mBalancesDetailsAdapter!!.getItem(binding.spAsset.selectedItemPosition)!!
|
||||||
val amount = (tietAmount.text.toString().replace(",", ".").toDouble()
|
val amount = (binding.tietAmount.text.toString().replace(",", ".").toDouble()
|
||||||
* Math.pow(10.0, balance.precision.toDouble())).toLong()
|
* Math.pow(10.0, balance.precision.toDouble())).toLong()
|
||||||
|
|
||||||
val transferAmount = AssetAmount(UnsignedLong.valueOf(amount), Asset(balance.id))
|
val transferAmount = AssetAmount(UnsignedLong.valueOf(amount), Asset(balance.id))
|
||||||
|
@ -574,13 +624,16 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
.setDestination(mSelectedUserAccount)
|
.setDestination(mSelectedUserAccount)
|
||||||
.setTransferAmount(transferAmount)
|
.setTransferAmount(transferAmount)
|
||||||
|
|
||||||
val privateKey = ECKey.fromPrivate(DumpedPrivateKey.fromBase58(null, wifKey).key.privKeyBytes)
|
val privateKey =
|
||||||
|
ECKey.fromPrivate(DumpedPrivateKey.fromBase58(null, wifKey).key.privKeyBytes)
|
||||||
|
|
||||||
// Add memo if it is not empty
|
// Add memo if it is not empty
|
||||||
val memoMsg = tietMemo.text.toString()
|
val memoMsg = binding.tietMemo.text.toString()
|
||||||
if (memoMsg.isNotEmpty()) {
|
if (memoMsg.isNotEmpty()) {
|
||||||
val nonce = Math.abs(SecureRandomGenerator.getSecureRandom().nextLong()).toBigInteger()
|
val nonce =
|
||||||
val encryptedMemo = Memo.encryptMessage(privateKey, destinationPublicKey!!, nonce, memoMsg)
|
Math.abs(SecureRandomGenerator.getSecureRandom().nextLong()).toBigInteger()
|
||||||
|
val encryptedMemo =
|
||||||
|
Memo.encryptMessage(privateKey, destinationPublicKey!!, nonce, memoMsg)
|
||||||
val from = Address(ECKey.fromPublicOnly(privateKey.pubKey))
|
val from = Address(ECKey.fromPublicOnly(privateKey.pubKey))
|
||||||
val to = Address(destinationPublicKey!!.key)
|
val to = Address(destinationPublicKey!!.key)
|
||||||
val memo = Memo(from, to, nonce, encryptedMemo)
|
val memo = Memo(from, to, nonce, encryptedMemo)
|
||||||
|
@ -601,9 +654,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
transaction = Transaction(privateKey, null, operations)
|
transaction = Transaction(privateKey, null, operations)
|
||||||
|
|
||||||
// Start the send transaction procedure which includes a series of calls
|
// Start the send transaction procedure which includes a series of calls
|
||||||
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(),
|
val id = mNetworkService?.sendMessage(
|
||||||
GetDynamicGlobalProperties.REQUIRED_API)
|
GetDynamicGlobalProperties(),
|
||||||
if (id != null ) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES)
|
GetDynamicGlobalProperties.REQUIRED_API
|
||||||
|
)
|
||||||
|
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES)
|
||||||
} else
|
} else
|
||||||
Log.d(TAG, "Network Service is not connected")
|
Log.d(TAG, "Network Service is not connected")
|
||||||
}
|
}
|
||||||
|
@ -634,8 +689,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
||||||
return null
|
return null
|
||||||
|
|
||||||
// Verify that the current Asset is either BTS or a SmartCoin
|
// Verify that the current Asset is either BTS or a SmartCoin
|
||||||
if (Constants.assetsWhichSendFeeToAgorise.contains(transferOperation.assetAmount?.asset?.objectId ?: "")) {
|
if (Constants.assetsWhichSendFeeToAgorise.contains(
|
||||||
val fee = transferOperation.assetAmount?.multiplyBy(Constants.FEE_PERCENTAGE) ?: return null
|
transferOperation.assetAmount?.asset?.objectId ?: ""
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
val fee =
|
||||||
|
transferOperation.assetAmount?.multiplyBy(Constants.FEE_PERCENTAGE) ?: return null
|
||||||
|
|
||||||
return TransferOperationBuilder()
|
return TransferOperationBuilder()
|
||||||
.setSource(mUserAccount)
|
.setSource(mUserAccount)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package cy.agorise.bitsybitshareswallet.fragments
|
package cy.agorise.bitsybitshareswallet.fragments
|
||||||
|
|
||||||
import android.content.*
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
|
@ -10,6 +10,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import androidx.collection.LongSparseArray
|
import androidx.collection.LongSparseArray
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.lifecycle.ViewModelProviders
|
import androidx.lifecycle.ViewModelProviders
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
@ -22,6 +23,7 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||||
import cy.agorise.bitsybitshareswallet.BuildConfig
|
import cy.agorise.bitsybitshareswallet.BuildConfig
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentSettingsBinding
|
||||||
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
|
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
|
||||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||||
|
@ -36,7 +38,6 @@ import cy.agorise.graphenej.models.JsonRpcResponse
|
||||||
import cy.agorise.graphenej.operations.AccountUpgradeOperationBuilder
|
import cy.agorise.graphenej.operations.AccountUpgradeOperationBuilder
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.schedulers.Schedulers
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.synthetic.main.fragment_settings.*
|
|
||||||
import org.bitcoinj.core.DumpedPrivateKey
|
import org.bitcoinj.core.DumpedPrivateKey
|
||||||
import org.bitcoinj.core.ECKey
|
import org.bitcoinj.core.ECKey
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
|
@ -59,6 +60,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
private const val RESPONSE_BROADCAST_TRANSACTION = 3
|
private const val RESPONSE_BROADCAST_TRANSACTION = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentSettingsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mViewModel: SettingsFragmentViewModel
|
private lateinit var mViewModel: SettingsFragmentViewModel
|
||||||
|
|
||||||
private var mUserAccount: UserAccount? = null
|
private var mUserAccount: UserAccount? = null
|
||||||
|
@ -82,7 +86,11 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
|
|
||||||
private val mHandler = Handler()
|
private val mHandler = Handler()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -92,7 +100,13 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||||
toolbar?.setBackgroundResource(if (!nightMode) R.color.colorPrimary else R.color.colorToolbarDark)
|
toolbar?.setBackgroundResource(if (!nightMode) R.color.colorPrimary else R.color.colorToolbarDark)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_settings, container, false)
|
_binding = FragmentSettingsBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -105,13 +119,14 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
||||||
|
|
||||||
// Configure ViewModel
|
// Configure ViewModel
|
||||||
mViewModel= ViewModelProviders.of(this).get(SettingsFragmentViewModel::class.java)
|
mViewModel = ViewModelProviders.of(this).get(SettingsFragmentViewModel::class.java)
|
||||||
|
|
||||||
mViewModel.getUserAccount(userId).observe(this,
|
mViewModel.getUserAccount(userId).observe(this,
|
||||||
androidx.lifecycle.Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount>{ userAccount ->
|
androidx.lifecycle.Observer<cy.agorise.bitsybitshareswallet.database.entities.UserAccount> { userAccount ->
|
||||||
if (userAccount != null) {
|
if (userAccount != null) {
|
||||||
mUserAccount = UserAccount(userAccount.id, userAccount.name)
|
mUserAccount = UserAccount(userAccount.id, userAccount.name)
|
||||||
btnUpgradeToLTM.isEnabled = !userAccount.isLtm // Disable button if already LTM
|
binding.btnUpgradeToLTM.isEnabled =
|
||||||
|
!userAccount.isLtm // Disable button if already LTM
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -121,7 +136,10 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
try {
|
try {
|
||||||
privateKey = CryptoUtils.decrypt(it, encryptedWIF)
|
privateKey = CryptoUtils.decrypt(it, encryptedWIF)
|
||||||
} catch (e: AEADBadTagException) {
|
} catch (e: AEADBadTagException) {
|
||||||
Log.e(TAG, "AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message)
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"AEADBadTagException. Class: " + e.javaClass + ", Msg: " + e.message
|
||||||
|
)
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: IllegalStateException) {
|
||||||
crashlytics.recordException(e)
|
crashlytics.recordException(e)
|
||||||
}
|
}
|
||||||
|
@ -132,7 +150,7 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
|
|
||||||
initNightModeSwitch()
|
initNightModeSwitch()
|
||||||
|
|
||||||
tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
binding.tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
||||||
|
|
||||||
// Obtain the current Security Lock Option selected and display it in the screen
|
// Obtain the current Security Lock Option selected and display it in the screen
|
||||||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -142,12 +160,13 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
// 1 -> PIN
|
// 1 -> PIN
|
||||||
// 2 -> Pattern
|
// 2 -> Pattern
|
||||||
|
|
||||||
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
binding.tvSecurityLockSelected.text =
|
||||||
|
resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
||||||
|
|
||||||
tvSecurityLock.setOnClickListener { onSecurityLockTextSelected() }
|
binding.tvSecurityLock.setOnClickListener { onSecurityLockTextSelected() }
|
||||||
tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected() }
|
binding.tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected() }
|
||||||
|
|
||||||
btnViewBrainKey.setOnClickListener { onShowBrainKeyButtonSelected() }
|
binding.btnViewBrainKey.setOnClickListener { onShowBrainKeyButtonSelected() }
|
||||||
|
|
||||||
val lastAccountBackup = PreferenceManager.getDefaultSharedPreferences(context)
|
val lastAccountBackup = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getLong(Constants.KEY_LAST_ACCOUNT_BACKUP, 0L)
|
.getLong(Constants.KEY_LAST_ACCOUNT_BACKUP, 0L)
|
||||||
|
@ -155,11 +174,11 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
|
|
||||||
if (lastAccountBackup + Constants.ACCOUNT_BACKUP_PERIOD < now)
|
if (lastAccountBackup + Constants.ACCOUNT_BACKUP_PERIOD < now)
|
||||||
tvBackupWarning.visibility = View.VISIBLE
|
binding.tvBackupWarning.visibility = View.VISIBLE
|
||||||
|
|
||||||
btnUpgradeToLTM.setOnClickListener { onUpgradeToLTMButtonSelected() }
|
binding.btnUpgradeToLTM.setOnClickListener { onUpgradeToLTMButtonSelected() }
|
||||||
|
|
||||||
btnRemoveAccount.setOnClickListener { onRemoveAccountButtonSelected() }
|
binding.btnRemoveAccount.setOnClickListener { onRemoveAccountButtonSelected() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showNodesDialog(v: View) {
|
private fun showNodesDialog(v: View) {
|
||||||
|
@ -191,7 +210,13 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
mNodesDialogLinearLayoutManager = LinearLayoutManager(v.context)
|
mNodesDialogLinearLayoutManager = LinearLayoutManager(v.context)
|
||||||
|
|
||||||
mNodesDialog = MaterialDialog(v.context).show {
|
mNodesDialog = MaterialDialog(v.context).show {
|
||||||
title(text = String.format("%s v%s", getString(R.string.app_name), BuildConfig.VERSION_NAME))
|
title(
|
||||||
|
text = String.format(
|
||||||
|
"%s v%s",
|
||||||
|
getString(R.string.app_name),
|
||||||
|
BuildConfig.VERSION_NAME
|
||||||
|
)
|
||||||
|
)
|
||||||
message(text = getString(R.string.title__bitshares_nodes_dialog, "-------"))
|
message(text = getString(R.string.title__bitshares_nodes_dialog, "-------"))
|
||||||
customListAdapter(nodesAdapter as FullNodesAdapter, mNodesDialogLinearLayoutManager)
|
customListAdapter(nodesAdapter as FullNodesAdapter, mNodesDialogLinearLayoutManager)
|
||||||
negativeButton(android.R.string.ok)
|
negativeButton(android.R.string.ok)
|
||||||
|
@ -218,8 +243,12 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||||
if (responseMap.containsKey(response.id)) {
|
if (responseMap.containsKey(response.id)) {
|
||||||
when (responseMap[response.id]) {
|
when (responseMap[response.id]) {
|
||||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES -> handleDynamicGlobalPropertiesNodes(response.result)
|
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES -> handleDynamicGlobalPropertiesNodes(
|
||||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM -> handleDynamicGlobalPropertiesLTM(response.result)
|
response.result
|
||||||
|
)
|
||||||
|
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM -> handleDynamicGlobalPropertiesLTM(
|
||||||
|
response.result
|
||||||
|
)
|
||||||
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
||||||
}
|
}
|
||||||
responseMap.remove(response.id)
|
responseMap.remove(response.id)
|
||||||
|
@ -238,13 +267,17 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showConnectedState() {
|
private fun showConnectedState() {
|
||||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
resources.getDrawable(R.drawable.ic_connected, null), null)
|
null, null,
|
||||||
|
ResourcesCompat.getDrawable(resources, R.drawable.ic_connected, null), null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showDisconnectedState() {
|
private fun showDisconnectedState() {
|
||||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||||
resources.getDrawable(R.drawable.ic_disconnected, null), null)
|
null, null,
|
||||||
|
ResourcesCompat.getDrawable(resources, R.drawable.ic_disconnected, null), null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handles the result of the [GetDynamicGlobalProperties] api call to obtain the current block number and update
|
/** Handles the result of the [GetDynamicGlobalProperties] api call to obtain the current block number and update
|
||||||
|
@ -253,7 +286,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
if (result is DynamicGlobalProperties) {
|
if (result is DynamicGlobalProperties) {
|
||||||
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
||||||
val blockNumber = NumberFormat.getInstance().format(result.head_block_number)
|
val blockNumber = NumberFormat.getInstance().format(result.head_block_number)
|
||||||
mNodesDialog?.message(text = getString(R.string.title__bitshares_nodes_dialog, blockNumber))
|
mNodesDialog?.message(
|
||||||
|
text = getString(R.string.title__bitshares_nodes_dialog, blockNumber)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +301,10 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
|
|
||||||
ltmTransaction?.blockData = BlockData(headBlockNumber, headBlockId, expirationTime)
|
ltmTransaction?.blockData = BlockData(headBlockNumber, headBlockId, expirationTime)
|
||||||
|
|
||||||
val id = mNetworkService?.sendMessage(BroadcastTransaction(ltmTransaction), BroadcastTransaction.REQUIRED_API)
|
val id = mNetworkService?.sendMessage(
|
||||||
|
BroadcastTransaction(ltmTransaction),
|
||||||
|
BroadcastTransaction.REQUIRED_API
|
||||||
|
)
|
||||||
if (id != null) responseMap.append(id, RESPONSE_BROADCAST_TRANSACTION)
|
if (id != null) responseMap.append(id, RESPONSE_BROADCAST_TRANSACTION)
|
||||||
|
|
||||||
// TODO use an indicator to show that a transaction is in progress
|
// TODO use an indicator to show that a transaction is in progress
|
||||||
|
@ -305,7 +343,10 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
*/
|
*/
|
||||||
private val mRequestDynamicGlobalPropertiesTask = object : Runnable {
|
private val mRequestDynamicGlobalPropertiesTask = object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
val id = mNetworkService?.sendMessage(
|
||||||
|
GetDynamicGlobalProperties(),
|
||||||
|
GetDynamicGlobalProperties.REQUIRED_API
|
||||||
|
)
|
||||||
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES)
|
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES)
|
||||||
|
|
||||||
mHandler.postDelayed(this, Constants.BLOCK_PERIOD)
|
mHandler.postDelayed(this, Constants.BLOCK_PERIOD)
|
||||||
|
@ -320,9 +361,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
val autoCloseOn = PreferenceManager.getDefaultSharedPreferences(context)
|
val autoCloseOn = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)
|
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)
|
||||||
|
|
||||||
switchAutoClose.isChecked = autoCloseOn
|
binding.switchAutoClose.isChecked = autoCloseOn
|
||||||
|
|
||||||
switchAutoClose.setOnCheckedChangeListener { buttonView, isChecked ->
|
binding.switchAutoClose.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
PreferenceManager.getDefaultSharedPreferences(buttonView.context).edit()
|
PreferenceManager.getDefaultSharedPreferences(buttonView.context).edit()
|
||||||
.putBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, isChecked).apply()
|
.putBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, isChecked).apply()
|
||||||
}
|
}
|
||||||
|
@ -337,9 +378,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
val nightModeOn = PreferenceManager.getDefaultSharedPreferences(context)
|
val nightModeOn = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
||||||
|
|
||||||
switchNightMode.isChecked = nightModeOn
|
binding.switchNightMode.isChecked = nightModeOn
|
||||||
|
|
||||||
switchNightMode.setOnCheckedChangeListener { buttonView, isChecked ->
|
binding.switchNightMode.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||||
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(buttonView.context).edit()
|
PreferenceManager.getDefaultSharedPreferences(buttonView.context).edit()
|
||||||
.putBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, isChecked).apply()
|
.putBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, isChecked).apply()
|
||||||
|
@ -371,8 +412,10 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
|
|
||||||
// Args used for both PIN and Pattern options
|
// Args used for both PIN and Pattern options
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
args.putInt(
|
||||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY)
|
BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||||
|
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY
|
||||||
|
)
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
||||||
|
|
||||||
return when (securityLockSelected) {
|
return when (securityLockSelected) {
|
||||||
|
@ -412,7 +455,8 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
// 1 -> PIN
|
// 1 -> PIN
|
||||||
// 2 -> Pattern
|
// 2 -> Pattern
|
||||||
|
|
||||||
tvSecurityLockSelected.text = resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
binding.tvSecurityLockSelected.text =
|
||||||
|
resources.getStringArray(R.array.security_lock_options)[securityLockSelected]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -430,17 +474,23 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
context?.let {
|
context?.let {
|
||||||
MaterialDialog(it).show {
|
MaterialDialog(it).show {
|
||||||
title(R.string.title__security_dialog)
|
title(R.string.title__security_dialog)
|
||||||
listItemsSingleChoice(R.array.security_lock_options, initialSelection = securityLockSelected) {_, index, _ ->
|
listItemsSingleChoice(
|
||||||
|
R.array.security_lock_options,
|
||||||
|
initialSelection = securityLockSelected
|
||||||
|
) { _, index, _ ->
|
||||||
// Args used for both PIN and Pattern options
|
// Args used for both PIN and Pattern options
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
args.putInt(
|
||||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_CREATE)
|
BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||||
|
BaseSecurityLockDialog.STEP_SECURITY_LOCK_CREATE
|
||||||
|
)
|
||||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1)
|
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1)
|
||||||
|
|
||||||
when (index) {
|
when (index) {
|
||||||
0 -> { /* None */
|
0 -> { /* None */
|
||||||
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||||
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0).apply() // 0 -> None
|
.putInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||||
|
.apply() // 0 -> None
|
||||||
|
|
||||||
// Call this function to update the UI
|
// Call this function to update the UI
|
||||||
onPINPatternChanged()
|
onPINPatternChanged()
|
||||||
|
@ -491,7 +541,8 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.map { authority ->
|
.map { authority ->
|
||||||
val plainBrainKey = CryptoUtils.decrypt(it, authority.encryptedBrainKey)
|
val plainBrainKey = CryptoUtils.decrypt(it, authority.encryptedBrainKey)
|
||||||
val plainSequenceNumber = CryptoUtils.decrypt(it, authority.encryptedSequenceNumber)
|
val plainSequenceNumber =
|
||||||
|
CryptoUtils.decrypt(it, authority.encryptedSequenceNumber)
|
||||||
val sequenceNumber = Integer.parseInt(plainSequenceNumber)
|
val sequenceNumber = Integer.parseInt(plainSequenceNumber)
|
||||||
BrainKey(plainBrainKey, sequenceNumber)
|
BrainKey(plainBrainKey, sequenceNumber)
|
||||||
}
|
}
|
||||||
|
@ -518,7 +569,7 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
PreferenceManager.getDefaultSharedPreferences(it.context).edit()
|
PreferenceManager.getDefaultSharedPreferences(it.context).edit()
|
||||||
.putLong(Constants.KEY_LAST_ACCOUNT_BACKUP, now).apply()
|
.putLong(Constants.KEY_LAST_ACCOUNT_BACKUP, now).apply()
|
||||||
tvBackupWarning.visibility = View.GONE
|
binding.tvBackupWarning.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
@ -541,11 +592,18 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
operations.add(operation)
|
operations.add(operation)
|
||||||
|
|
||||||
val currentPrivateKey = ECKey.fromPrivate(
|
val currentPrivateKey = ECKey.fromPrivate(
|
||||||
DumpedPrivateKey.fromBase58(null, privateKey).key.privKeyBytes)
|
DumpedPrivateKey.fromBase58(null, privateKey).key.privKeyBytes
|
||||||
|
)
|
||||||
ltmTransaction = Transaction(currentPrivateKey, null, operations)
|
ltmTransaction = Transaction(currentPrivateKey, null, operations)
|
||||||
|
|
||||||
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
val id = mNetworkService?.sendMessage(
|
||||||
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM)
|
GetDynamicGlobalProperties(),
|
||||||
|
GetDynamicGlobalProperties.REQUIRED_API
|
||||||
|
)
|
||||||
|
if (id != null) responseMap.append(
|
||||||
|
id,
|
||||||
|
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -574,7 +632,8 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
||||||
|
|
||||||
// Marks the license as agreed, so that it is not shown to the user again.
|
// Marks the license as agreed, so that it is not shown to the user again.
|
||||||
pref.edit().putInt(
|
pref.edit().putInt(
|
||||||
Constants.KEY_LAST_AGREED_LICENSE_VERSION, Constants.CURRENT_LICENSE_VERSION).apply()
|
Constants.KEY_LAST_AGREED_LICENSE_VERSION, Constants.CURRENT_LICENSE_VERSION
|
||||||
|
).apply()
|
||||||
|
|
||||||
// Restarts the activity, which will restart the whole application since it uses a
|
// Restarts the activity, which will restart the whole application since it uses a
|
||||||
// single activity architecture.
|
// single activity architecture.
|
||||||
|
|
|
@ -21,12 +21,12 @@ import com.jakewharton.rxbinding3.appcompat.queryTextChangeEvents
|
||||||
import cy.agorise.bitsybitshareswallet.R
|
import cy.agorise.bitsybitshareswallet.R
|
||||||
import cy.agorise.bitsybitshareswallet.adapters.TransfersDetailsAdapter
|
import cy.agorise.bitsybitshareswallet.adapters.TransfersDetailsAdapter
|
||||||
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
||||||
|
import cy.agorise.bitsybitshareswallet.databinding.FragmentTransactionsBinding
|
||||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||||
import cy.agorise.bitsybitshareswallet.utils.*
|
import cy.agorise.bitsybitshareswallet.utils.*
|
||||||
import cy.agorise.bitsybitshareswallet.viewmodels.TransactionsViewModel
|
import cy.agorise.bitsybitshareswallet.viewmodels.TransactionsViewModel
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import kotlinx.android.synthetic.main.fragment_transactions.*
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
@ -42,14 +42,27 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
private const val REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION = 100
|
private const val REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var _binding: FragmentTransactionsBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private lateinit var mViewModel: TransactionsViewModel
|
private lateinit var mViewModel: TransactionsViewModel
|
||||||
|
|
||||||
private var mDisposables = CompositeDisposable()
|
private var mDisposables = CompositeDisposable()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
|
||||||
return inflater.inflate(R.layout.fragment_transactions, container, false)
|
_binding = FragmentTransactionsBinding.inflate(inflater, container, false)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
@ -62,8 +75,8 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
||||||
|
|
||||||
val transfersDetailsAdapter = TransfersDetailsAdapter(context!!)
|
val transfersDetailsAdapter = TransfersDetailsAdapter(context!!)
|
||||||
rvTransactions.adapter = transfersDetailsAdapter
|
binding.rvTransactions.adapter = transfersDetailsAdapter
|
||||||
rvTransactions.layoutManager = LinearLayoutManager(context)
|
binding.rvTransactions.layoutManager = LinearLayoutManager(context)
|
||||||
|
|
||||||
// Configure TransactionsViewModel to fetch the transaction history
|
// Configure TransactionsViewModel to fetch the transaction history
|
||||||
mViewModel = ViewModelProviders.of(this).get(TransactionsViewModel::class.java)
|
mViewModel = ViewModelProviders.of(this).get(TransactionsViewModel::class.java)
|
||||||
|
@ -71,11 +84,11 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
mViewModel.getFilteredTransactions(userId).observe(this,
|
mViewModel.getFilteredTransactions(userId).observe(this,
|
||||||
Observer<List<TransferDetail>> { transactions ->
|
Observer<List<TransferDetail>> { transactions ->
|
||||||
if (transactions.isEmpty()) {
|
if (transactions.isEmpty()) {
|
||||||
rvTransactions.visibility = View.GONE
|
binding.rvTransactions.visibility = View.GONE
|
||||||
tvEmpty.visibility = View.VISIBLE
|
binding.tvEmpty.visibility = View.VISIBLE
|
||||||
} else {
|
} else {
|
||||||
rvTransactions.visibility = View.VISIBLE
|
binding.rvTransactions.visibility = View.VISIBLE
|
||||||
tvEmpty.visibility = View.GONE
|
binding.tvEmpty.visibility = View.GONE
|
||||||
|
|
||||||
val shouldScrollUp = transactions.size - transfersDetailsAdapter.itemCount == 1
|
val shouldScrollUp = transactions.size - transfersDetailsAdapter.itemCount == 1
|
||||||
transfersDetailsAdapter.replaceAll(transactions)
|
transfersDetailsAdapter.replaceAll(transactions)
|
||||||
|
@ -83,13 +96,13 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
// Scroll to the top only if the difference between old and new items is 1
|
// Scroll to the top only if the difference between old and new items is 1
|
||||||
// which most likely means a new transaction was received/sent.
|
// which most likely means a new transaction was received/sent.
|
||||||
if (shouldScrollUp)
|
if (shouldScrollUp)
|
||||||
rvTransactions.scrollToPosition(0)
|
binding.rvTransactions.scrollToPosition(0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set custom touch listener to handle bounce/stretch effect
|
// Set custom touch listener to handle bounce/stretch effect
|
||||||
val bounceTouchListener = BounceTouchListener(rvTransactions)
|
val bounceTouchListener = BounceTouchListener(binding.rvTransactions)
|
||||||
rvTransactions.setOnTouchListener(bounceTouchListener)
|
binding.rvTransactions.setOnTouchListener(bounceTouchListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
@ -118,7 +131,10 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
R.id.menu_filter -> {
|
R.id.menu_filter -> {
|
||||||
val filterOptionsDialog = FilterOptionsDialog()
|
val filterOptionsDialog = FilterOptionsDialog()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putParcelable(FilterOptionsDialog.KEY_FILTER_OPTIONS, mViewModel.getFilterOptions())
|
args.putParcelable(
|
||||||
|
FilterOptionsDialog.KEY_FILTER_OPTIONS,
|
||||||
|
mViewModel.getFilterOptions()
|
||||||
|
)
|
||||||
filterOptionsDialog.arguments = args
|
filterOptionsDialog.arguments = args
|
||||||
filterOptionsDialog.show(childFragmentManager, "filter-options-tag")
|
filterOptionsDialog.show(childFragmentManager, "filter-options-tag")
|
||||||
true
|
true
|
||||||
|
@ -152,11 +168,17 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
|
|
||||||
/** Verifies that the storage permission has been granted before attempting to generate the export options */
|
/** Verifies that the storage permission has been granted before attempting to generate the export options */
|
||||||
private fun verifyStoragePermission() {
|
private fun verifyStoragePermission() {
|
||||||
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
if (ContextCompat.checkSelfPermission(
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
activity!!,
|
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
// Permission is not already granted
|
// Permission is not already granted
|
||||||
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
requestPermissions(
|
||||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
|
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||||
|
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Permission is already granted
|
// Permission is already granted
|
||||||
showExportOptionsDialog()
|
showExportOptionsDialog()
|
||||||
|
@ -166,7 +188,11 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
/** Received the result of the storage permission request and if it was accepted then shows the export options
|
/** Received the result of the storage permission request and if it was accepted then shows the export options
|
||||||
* dialog, but if it was not accepted then shows a toast explaining that the permission is necessary to generate
|
* dialog, but if it was not accepted then shows a toast explaining that the permission is necessary to generate
|
||||||
* the export options */
|
* the export options */
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<out String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
||||||
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||||
|
@ -181,7 +207,10 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
||||||
private fun showExportOptionsDialog() {
|
private fun showExportOptionsDialog() {
|
||||||
MaterialDialog(context!!).show {
|
MaterialDialog(context!!).show {
|
||||||
title(R.string.title_export_transactions)
|
title(R.string.title_export_transactions)
|
||||||
listItemsMultiChoice(R.array.export_options, initialSelection = intArrayOf(0,1)) { _, indices, _ ->
|
listItemsMultiChoice(
|
||||||
|
R.array.export_options,
|
||||||
|
initialSelection = intArrayOf(0, 1)
|
||||||
|
) { _, indices, _ ->
|
||||||
val exportPDF = indices.contains(0)
|
val exportPDF = indices.contains(0)
|
||||||
val exportCSV = indices.contains(1)
|
val exportCSV = indices.contains(1)
|
||||||
exportFilteredTransactions(exportPDF, exportCSV)
|
exportFilteredTransactions(exportPDF, exportCSV)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package cy.agorise.bitsybitshareswallet.models
|
package cy.agorise.bitsybitshareswallet.models
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import kotlinx.android.parcel.Parcelize
|
|
||||||
import cy.agorise.bitsybitshareswallet.fragments.TransactionsFragment
|
import cy.agorise.bitsybitshareswallet.fragments.TransactionsFragment
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model that includes all the options to filter the transactions in the [TransactionsFragment]
|
* Model that includes all the options to filter the transactions in the [TransactionsFragment]
|
||||||
*/
|
*/
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class FilterOptions (
|
data class FilterOptions(
|
||||||
var query: String = "",
|
var query: String = "",
|
||||||
var transactionsDirection: Int = 0,
|
var transactionsDirection: Int = 0,
|
||||||
var dateRangeAll: Boolean = true,
|
var dateRangeAll: Boolean = true,
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
||||||
|
|
||||||
<fragment
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/navHostFragment"
|
android:id="@+id/navHostFragment"
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
Loading…
Reference in a new issue