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: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: "androidx.navigation.safeargs.kotlin"
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
|
||||
// Needed for Kotlin's @Parcelize annotation
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
defaultConfig {
|
||||
|
@ -31,6 +26,11 @@ android {
|
|||
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
||||
}
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
minifyEnabled false
|
||||
|
|
|
@ -12,8 +12,8 @@ import androidx.navigation.ui.navigateUp
|
|||
import androidx.navigation.ui.onNavDestinationSelected
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.databinding.ActivityMainBinding
|
||||
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,
|
||||
|
@ -21,7 +21,8 @@ import kotlinx.android.synthetic.main.activity_main.*
|
|||
*/
|
||||
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
|
||||
private lateinit var mHandler: Handler
|
||||
|
@ -31,12 +32,14 @@ class MainActivity : ConnectedActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
// Sets the theme to night mode if it has been selected by the user
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)) {
|
||||
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
||||
) {
|
||||
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
|
||||
.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)
|
||||
mRunnable = Runnable {
|
||||
if (PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)) {
|
||||
.getBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, true)
|
||||
) {
|
||||
finish()
|
||||
android.os.Process.killProcess(android.os.Process.myPid())
|
||||
} else
|
||||
|
@ -107,8 +111,8 @@ class MainActivity : ConnectedActivity() {
|
|||
|
||||
override fun onBackPressed() {
|
||||
// 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
|
||||
when(currentDestination?.id) {
|
||||
val currentDestination = binding.navHostFragment.findNavController().currentDestination
|
||||
when (currentDestination?.id) {
|
||||
R.id.license_dest, R.id.import_brainkey_dest -> finish()
|
||||
else -> super.onBackPressed()
|
||||
}
|
||||
|
|
|
@ -9,38 +9,50 @@ import androidx.lifecycle.Observer
|
|||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.BalancesAdapter
|
||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentBalancesBinding
|
||||
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
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
): View {
|
||||
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?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// 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!!)
|
||||
rvBalances.adapter = balancesAdapter
|
||||
rvBalances.layoutManager = LinearLayoutManager(context!!)
|
||||
rvBalances.addItemDecoration(DividerItemDecoration(context!!, DividerItemDecoration.VERTICAL))
|
||||
binding.rvBalances.adapter = balancesAdapter
|
||||
binding.rvBalances.layoutManager = LinearLayoutManager(context!!)
|
||||
binding.rvBalances.addItemDecoration(
|
||||
DividerItemDecoration(context!!, DividerItemDecoration.VERTICAL)
|
||||
)
|
||||
|
||||
mBalanceDetailViewModel.getAll().observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||
balancesAdapter.replaceAll(balancesDetails)
|
||||
})
|
||||
mBalanceDetailViewModel.getAll()
|
||||
.observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||
balancesAdapter.replaceAll(balancesDetails)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -8,9 +8,15 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.collection.LongSparseArray
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.jakewharton.rxbinding3.widget.textChanges
|
||||
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.ServiceGenerator
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.containsDigits
|
||||
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.JsonRpcResponse
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_create_account.*
|
||||
import org.bitcoinj.core.ECKey
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
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() {
|
||||
|
@ -49,10 +49,14 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
|
||||
// Used when trying to validate that the account name is available
|
||||
private const val RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION = 1
|
||||
|
||||
// Used when trying to obtain the info of the newly created account
|
||||
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
|
||||
|
||||
/** 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
|
||||
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)
|
||||
|
||||
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?) {
|
||||
|
@ -77,7 +91,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
|
||||
// Use RxJava Debounce to check the validity and availability of the user's proposed account name
|
||||
mDisposables.add(
|
||||
tietAccountName.textChanges()
|
||||
binding.tietAccountName.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(800, TimeUnit.MILLISECONDS)
|
||||
.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
|
||||
mDisposables.add(
|
||||
tietPin.textChanges()
|
||||
binding.tietPin.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.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
|
||||
mDisposables.add(
|
||||
tietPinConfirmation.textChanges()
|
||||
binding.tietPinConfirmation.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
@ -111,10 +125,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
)
|
||||
)
|
||||
|
||||
btnCancel.setOnClickListener { findNavController().navigateUp() }
|
||||
binding.btnCancel.setOnClickListener { findNavController().navigateUp() }
|
||||
|
||||
btnCreate.isEnabled = false
|
||||
btnCreate.setOnClickListener { createAccount() }
|
||||
binding.btnCreate.isEnabled = false
|
||||
binding.btnCreate.setOnClickListener { createAccount() }
|
||||
|
||||
// Generating BrainKey
|
||||
generateKeys()
|
||||
|
@ -123,19 +137,23 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
private fun validateAccountName(accountName: String) {
|
||||
isAccountValidAndAvailable = false
|
||||
|
||||
if ( !isAccountLengthValid(accountName) ) {
|
||||
tilAccountName.helperText = ""
|
||||
tilAccountName.error = getString(R.string.error__invalid_account_length)
|
||||
} else if ( !isAccountStartValid(accountName) ) {
|
||||
tilAccountName.helperText = ""
|
||||
tilAccountName.error = getString(R.string.error__invalid_account_start)
|
||||
} else if ( !isAccountNameValid(accountName) ) {
|
||||
tilAccountName.helperText = ""
|
||||
tilAccountName.error = getString(R.string.error__invalid_account_name)
|
||||
if (!isAccountLengthValid(accountName)) {
|
||||
binding.tilAccountName.helperText = ""
|
||||
binding.tilAccountName.error = getString(R.string.error__invalid_account_length)
|
||||
} else if (!isAccountStartValid(accountName)) {
|
||||
binding.tilAccountName.helperText = ""
|
||||
binding.tilAccountName.error = getString(R.string.error__invalid_account_start)
|
||||
} else if (!isAccountNameValid(accountName)) {
|
||||
binding.tilAccountName.helperText = ""
|
||||
binding.tilAccountName.error = getString(R.string.error__invalid_account_name)
|
||||
} else {
|
||||
tilAccountName.isErrorEnabled = false
|
||||
tilAccountName.helperText = getString(R.string.text__verifying_account_availability)
|
||||
val id = mNetworkService?.sendMessage(GetAccountByName(accountName), GetAccountByName.REQUIRED_API)
|
||||
binding.tilAccountName.isErrorEnabled = false
|
||||
binding.tilAccountName.helperText =
|
||||
getString(R.string.text__verifying_account_availability)
|
||||
val id = mNetworkService?.sendMessage(
|
||||
GetAccountByName(accountName),
|
||||
GetAccountByName.REQUIRED_API
|
||||
)
|
||||
|
||||
if (id != null)
|
||||
responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION)
|
||||
|
@ -170,13 +188,13 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
}
|
||||
|
||||
private fun validatePIN() {
|
||||
val pin = tietPin.text.toString()
|
||||
val pin = binding.tietPin.text.toString()
|
||||
|
||||
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
|
||||
} else {
|
||||
tilPin.isErrorEnabled = false
|
||||
binding.tilPin.isErrorEnabled = false
|
||||
isPINValid = true
|
||||
}
|
||||
|
||||
|
@ -184,13 +202,13 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
}
|
||||
|
||||
private fun validatePINConfirmation() {
|
||||
val pinConfirmation = tietPinConfirmation.text.toString()
|
||||
val pinConfirmation = binding.tietPinConfirmation.text.toString()
|
||||
|
||||
if (pinConfirmation != tietPin.text.toString()) {
|
||||
tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
||||
if (pinConfirmation != binding.tietPin.text.toString()) {
|
||||
binding.tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
||||
isPINConfirmationValid = false
|
||||
} else {
|
||||
tilPinConfirmation.isErrorEnabled = false
|
||||
binding.tilPinConfirmation.isErrorEnabled = false
|
||||
isPINConfirmationValid = true
|
||||
}
|
||||
|
||||
|
@ -198,20 +216,21 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
}
|
||||
|
||||
private fun enableDisableCreateButton() {
|
||||
btnCreate.isEnabled = (isPINValid && isPINConfirmationValid && isAccountValidAndAvailable)
|
||||
binding.btnCreate.isEnabled =
|
||||
(isPINValid && isPINConfirmationValid && isAccountValidAndAvailable)
|
||||
}
|
||||
|
||||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||
if (responseMap.containsKey(response.id)) {
|
||||
when (responseMap[response.id]) {
|
||||
RESPONSE_GET_ACCOUNT_BY_NAME_VALIDATION -> handleAccountNameValidation(response.result)
|
||||
RESPONSE_GET_ACCOUNT_BY_NAME_CREATED -> handleAccountNameCreated(response.result)
|
||||
RESPONSE_GET_ACCOUNT_BY_NAME_CREATED -> handleAccountNameCreated(response.result)
|
||||
}
|
||||
responseMap.remove(response.id)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -219,12 +238,12 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
*/
|
||||
private fun handleAccountNameValidation(result: Any?) {
|
||||
if (result is AccountProperties) {
|
||||
tilAccountName.helperText = ""
|
||||
tilAccountName.error = getString(R.string.error__account_not_available)
|
||||
binding.tilAccountName.helperText = ""
|
||||
binding.tilAccountName.error = getString(R.string.error__account_not_available)
|
||||
isAccountValidAndAvailable = false
|
||||
} else {
|
||||
tilAccountName.isErrorEnabled = false
|
||||
tilAccountName.helperText = getString(R.string.text__account_is_available)
|
||||
binding.tilAccountName.isErrorEnabled = false
|
||||
binding.tilAccountName.helperText = getString(R.string.text__account_is_available)
|
||||
isAccountValidAndAvailable = true
|
||||
}
|
||||
|
||||
|
@ -237,7 +256,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
*/
|
||||
private fun handleAccountNameCreated(result: Any?) {
|
||||
if (result is AccountProperties) {
|
||||
onAccountSelected(result, tietPin.text.toString())
|
||||
onAccountSelected(result, binding.tietPin.text.toString())
|
||||
} else {
|
||||
context?.toast(getString(R.string.error__created_account_not_found))
|
||||
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.
|
||||
*/
|
||||
private fun setStateLoading() {
|
||||
btnCancel.isEnabled = false
|
||||
btnCreate.isEnabled = false
|
||||
progressBar.visibility = View.VISIBLE
|
||||
binding.btnCancel.isEnabled = false
|
||||
binding.btnCreate.isEnabled = false
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,9 +277,9 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
* the information from the newly created account.
|
||||
*/
|
||||
private fun setStateError() {
|
||||
btnCancel.isEnabled = true
|
||||
btnCreate.isEnabled = false
|
||||
progressBar.visibility = View.GONE
|
||||
binding.btnCancel.isEnabled = true
|
||||
binding.btnCreate.isEnabled = false
|
||||
binding.progressBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,7 +289,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
private fun createAccount() {
|
||||
setStateLoading()
|
||||
|
||||
val accountName = tietAccountName.text.toString()
|
||||
val accountName = binding.tietAccountName.text.toString()
|
||||
val faucetRequest = FaucetRequest(accountName, mAddress, Constants.FAUCET_REFERRER)
|
||||
|
||||
val sg = ServiceGenerator(Constants.FAUCET_URL)
|
||||
|
@ -280,7 +299,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
|
||||
// Execute the call asynchronously. Get a positive or negative callback.
|
||||
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
|
||||
// with a delay to let the nodes update their information
|
||||
val handler = Handler()
|
||||
|
@ -307,8 +329,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
|
||||
private fun getCreatedAccountInfo(faucetResponse: FaucetResponse?) {
|
||||
if (faucetResponse?.account != null) {
|
||||
val id = mNetworkService?.sendMessage(GetAccountByName(faucetResponse.account?.name),
|
||||
GetAccountByName.REQUIRED_API)
|
||||
val id = mNetworkService?.sendMessage(
|
||||
GetAccountByName(faucetResponse.account?.name),
|
||||
GetAccountByName.REQUIRED_API
|
||||
)
|
||||
|
||||
if (id != null)
|
||||
responseMap.append(id, RESPONSE_GET_ACCOUNT_BY_NAME_CREATED)
|
||||
|
@ -320,7 +344,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
getString(R.string.error__faucet_template, "None")
|
||||
}
|
||||
|
||||
context?.let {context ->
|
||||
context?.let { context ->
|
||||
MaterialDialog(context)
|
||||
.title(R.string.title_error)
|
||||
.message(text = content)
|
||||
|
@ -338,7 +362,8 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
var reader: BufferedReader? = null
|
||||
val dictionary: String
|
||||
try {
|
||||
reader = BufferedReader(InputStreamReader(context!!.assets.open(BRAINKEY_FILE), "UTF-8"))
|
||||
reader =
|
||||
BufferedReader(InputStreamReader(context!!.assets.open(BRAINKEY_FILE), "UTF-8"))
|
||||
dictionary = reader.readLine()
|
||||
|
||||
val brainKeySuggestion = BrainKey.suggest(dictionary)
|
||||
|
@ -347,7 +372,7 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
Log.d(TAG, "brain key: $brainKeySuggestion")
|
||||
Log.d(TAG, "address would be: $address")
|
||||
mAddress = address.toString()
|
||||
tvBrainKey.text = mBrainKey?.brainKey
|
||||
binding.tvBrainKey.text = mBrainKey?.brainKey
|
||||
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "IOException while trying to generate key. Msg: " + e.message)
|
||||
|
@ -364,7 +389,10 @@ class CreateAccountFragment : BaseAccountFragment() {
|
|||
try {
|
||||
reader.close()
|
||||
} 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.navigation.fragment.navArgs
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentEReceiptBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||
import cy.agorise.bitsybitshareswallet.utils.toast
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.EReceiptViewModel
|
||||
import kotlinx.android.synthetic.main.fragment_e_receipt.*
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
|
@ -39,15 +38,28 @@ class EReceiptFragment : Fragment() {
|
|||
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 lateinit var mEReceiptViewModel: EReceiptViewModel
|
||||
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)
|
||||
|
||||
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?) {
|
||||
|
@ -65,28 +77,30 @@ class EReceiptFragment : Fragment() {
|
|||
|
||||
mEReceiptViewModel = ViewModelProviders.of(this).get(EReceiptViewModel::class.java)
|
||||
|
||||
mEReceiptViewModel.get(userId, transferId).observe(this, Observer<TransferDetail> { transferDetail ->
|
||||
bindTransferDetail(transferDetail)
|
||||
})
|
||||
mEReceiptViewModel.get(userId, transferId)
|
||||
.observe(this, Observer<TransferDetail> { transferDetail ->
|
||||
bindTransferDetail(transferDetail)
|
||||
})
|
||||
}
|
||||
|
||||
private fun bindTransferDetail(transferDetail: TransferDetail) {
|
||||
context?.let { vPaymentDirection.setBackgroundColor(ContextCompat.getColor(it,
|
||||
if(transferDetail.direction) R.color.colorReceive else R.color.colorSend
|
||||
))}
|
||||
context?.let { context ->
|
||||
val colorRes = if (transferDetail.direction) R.color.colorReceive else R.color.colorSend
|
||||
binding.vPaymentDirection.setBackgroundColor(ContextCompat.getColor(context, colorRes))
|
||||
}
|
||||
|
||||
tvFrom.text = transferDetail.from ?: ""
|
||||
tvTo.text = transferDetail.to ?: ""
|
||||
binding.tvFrom.text = transferDetail.from ?: ""
|
||||
binding.tvTo.text = transferDetail.to ?: ""
|
||||
|
||||
// Show the crypto amount correctly formatted
|
||||
val df = DecimalFormat("####."+("#".repeat(transferDetail.assetPrecision)))
|
||||
val df = DecimalFormat("####." + ("#".repeat(transferDetail.assetPrecision)))
|
||||
df.roundingMode = RoundingMode.CEILING
|
||||
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
||||
|
||||
val amount = transferDetail.assetAmount.toDouble() /
|
||||
Math.pow(10.toDouble(), transferDetail.assetPrecision.toDouble())
|
||||
val assetAmount = "${df.format(amount)} ${transferDetail.getUIAssetSymbol()}"
|
||||
tvAmount.text = assetAmount
|
||||
binding.tvAmount.text = assetAmount
|
||||
|
||||
// Fiat equivalent
|
||||
if (transferDetail.fiatAmount != null && transferDetail.fiatSymbol != null) {
|
||||
|
@ -96,20 +110,21 @@ class EReceiptFragment : Fragment() {
|
|||
Math.pow(10.0, currency.defaultFractionDigits.toDouble())
|
||||
|
||||
val equivalentValue = "${numberFormat.format(fiatEquivalent)} ${currency.currencyCode}"
|
||||
tvEquivalentValue.text = equivalentValue
|
||||
binding.tvEquivalentValue.text = equivalentValue
|
||||
} else {
|
||||
tvEquivalentValue.text = "-"
|
||||
binding.tvEquivalentValue.text = "-"
|
||||
}
|
||||
|
||||
// Memo
|
||||
if (transferDetail.memo != "")
|
||||
tvMemo.text = getString(R.string.template__memo, transferDetail.memo)
|
||||
binding.tvMemo.text = getString(R.string.template__memo, transferDetail.memo)
|
||||
else
|
||||
tvMemo.visibility = View.GONE
|
||||
binding.tvMemo.visibility = View.GONE
|
||||
|
||||
// Date
|
||||
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 #
|
||||
formatTransferTextView(transferDetail.id)
|
||||
|
@ -118,11 +133,14 @@ class EReceiptFragment : Fragment() {
|
|||
/** Formats the transfer TextView to show a link to explore the given transfer
|
||||
* in a BitShares explorer */
|
||||
private fun formatTransferTextView(transferId: String) {
|
||||
val tx = Html.fromHtml(getString(R.string.template__tx,
|
||||
"<a href=\"http://bitshares-explorer.io/#/operations/$transferId\">$transferId</a>"
|
||||
))
|
||||
tvTransferID.text = tx
|
||||
tvTransferID.movementMethod = LinkMovementMethod.getInstance()
|
||||
val tx = Html.fromHtml(
|
||||
getString(
|
||||
R.string.template__tx,
|
||||
"<a href=\"http://bitshares-explorer.io/#/operations/$transferId\">$transferId</a>"
|
||||
)
|
||||
)
|
||||
binding.tvTransferID.text = tx
|
||||
binding.tvTransferID.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
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
|
||||
* shares it but if it is not then it asks the user for that permission */
|
||||
private fun verifyStoragePermission() {
|
||||
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
if (ContextCompat
|
||||
.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// Permission is not already granted
|
||||
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
|
||||
requestPermissions(
|
||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION
|
||||
)
|
||||
} else {
|
||||
// Permission is already granted
|
||||
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)
|
||||
|
||||
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. */
|
||||
private fun shareEReceiptScreenshot() {
|
||||
// Get Screenshot
|
||||
tvTransferID.text = getString(R.string.template__tx, args.transferId)
|
||||
val screenshot = Helper.loadBitmapFromView(container)
|
||||
binding.tvTransferID.text = getString(R.string.template__tx, args.transferId)
|
||||
val screenshot = Helper.loadBitmapFromView(binding.container)
|
||||
formatTransferTextView(args.transferId)
|
||||
val imageUri = context?.let { Helper.saveTemporalBitmap(it, screenshot) }
|
||||
|
||||
|
|
|
@ -6,25 +6,24 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import android.widget.*
|
||||
import android.widget.TextView
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||
import cy.agorise.bitsybitshareswallet.databinding.DialogFilterOptionsBinding
|
||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||
import cy.agorise.bitsybitshareswallet.views.DatePickerFragment
|
||||
import kotlinx.android.synthetic.main.dialog_filter_options.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.ClassCastException
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
|
@ -43,12 +42,17 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
const val END_DATE_PICKER = 1
|
||||
}
|
||||
|
||||
private var _binding: DialogFilterOptionsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var mFilterOptions: FilterOptions
|
||||
|
||||
private var mCallback: OnFilterOptionsSelectedListener? = null
|
||||
|
||||
private var dateFormat: SimpleDateFormat = SimpleDateFormat("d/MMM/yyyy",
|
||||
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0])
|
||||
private var dateFormat: SimpleDateFormat = SimpleDateFormat(
|
||||
"d/MMM/yyyy",
|
||||
ConfigurationCompat.getLocales(Resources.getSystem().configuration)[0]
|
||||
)
|
||||
|
||||
private var mBalanceDetails = ArrayList<BalanceDetail>()
|
||||
|
||||
|
@ -59,7 +63,7 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
private lateinit var mCurrency: Currency
|
||||
|
||||
override fun onDateSet(which: Int, timestamp: Long) {
|
||||
when(which) {
|
||||
when (which) {
|
||||
START_DATE_PICKER -> {
|
||||
mFilterOptions.startDate = timestamp
|
||||
|
||||
|
@ -85,10 +89,10 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
|
||||
private fun updateDateTextViews() {
|
||||
var date = Date(mFilterOptions.startDate)
|
||||
tvStartDate.text = dateFormat.format(date)
|
||||
binding.tvStartDate.text = dateFormat.format(date)
|
||||
|
||||
date = Date(mFilterOptions.endDate)
|
||||
tvEndDate.text = dateFormat.format(date)
|
||||
binding.tvEndDate.text = dateFormat.format(date)
|
||||
}
|
||||
|
||||
// Container Fragment must implement this interface
|
||||
|
@ -96,8 +100,18 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
fun onFilterOptionsSelected(filterOptions: FilterOptions)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.dialog_filter_options, container, false)
|
||||
override fun onCreateView(
|
||||
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?) {
|
||||
|
@ -112,73 +126,81 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
|
||||
// Initialize Transactions direction
|
||||
when (mFilterOptions.transactionsDirection) {
|
||||
0 -> rbTransactionAll.isChecked = true
|
||||
1 -> rbTransactionSent.isChecked = true
|
||||
2 -> rbTransactionReceived.isChecked = true
|
||||
0 -> binding.rbTransactionAll.isChecked = true
|
||||
1 -> binding.rbTransactionSent.isChecked = true
|
||||
2 -> binding.rbTransactionReceived.isChecked = true
|
||||
}
|
||||
|
||||
// Initialize Date range
|
||||
cbDateRange.setOnCheckedChangeListener { _, isChecked ->
|
||||
llDateRange.visibility = if(isChecked) View.GONE else View.VISIBLE }
|
||||
cbDateRange.isChecked = mFilterOptions.dateRangeAll
|
||||
binding.cbDateRange.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.llDateRange.visibility = if (isChecked) View.GONE else View.VISIBLE
|
||||
}
|
||||
binding.cbDateRange.isChecked = mFilterOptions.dateRangeAll
|
||||
|
||||
tvStartDate.setOnClickListener(mDateClickListener)
|
||||
binding.tvStartDate.setOnClickListener(mDateClickListener)
|
||||
|
||||
tvEndDate.setOnClickListener(mDateClickListener)
|
||||
binding.tvEndDate.setOnClickListener(mDateClickListener)
|
||||
|
||||
updateDateTextViews()
|
||||
|
||||
// Initialize Asset
|
||||
cbAsset.setOnCheckedChangeListener { _, isChecked ->
|
||||
sAsset.visibility = if(isChecked) View.GONE else View.VISIBLE
|
||||
binding.cbAsset.setOnCheckedChangeListener { _, isChecked ->
|
||||
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
|
||||
mBalanceDetailViewModel = ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
||||
mBalanceDetailViewModel =
|
||||
ViewModelProviders.of(this).get(BalanceDetailViewModel::class.java)
|
||||
|
||||
mBalanceDetailViewModel.getAll().observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||
mBalanceDetails.clear()
|
||||
mBalanceDetails.addAll(balancesDetails)
|
||||
mBalanceDetails.sortWith(
|
||||
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
||||
)
|
||||
mBalancesDetailsAdapter = BalancesDetailsAdapter(context!!, android.R.layout.simple_spinner_item, mBalanceDetails)
|
||||
sAsset.adapter = mBalancesDetailsAdapter
|
||||
mBalanceDetailViewModel.getAll()
|
||||
.observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||
mBalanceDetails.clear()
|
||||
mBalanceDetails.addAll(balancesDetails)
|
||||
mBalanceDetails.sortWith(
|
||||
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
||||
)
|
||||
mBalancesDetailsAdapter = BalancesDetailsAdapter(
|
||||
context!!,
|
||||
android.R.layout.simple_spinner_item,
|
||||
mBalanceDetails
|
||||
)
|
||||
binding.sAsset.adapter = mBalancesDetailsAdapter
|
||||
|
||||
// Try to select the selectedAssetSymbol
|
||||
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
||||
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == mFilterOptions.asset) {
|
||||
sAsset.setSelection(i)
|
||||
break
|
||||
// Try to select the selectedAssetSymbol
|
||||
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
||||
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == mFilterOptions.asset) {
|
||||
binding.sAsset.setSelection(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Initialize Equivalent Value
|
||||
cbEquivalentValue.setOnCheckedChangeListener { _, isChecked ->
|
||||
llEquivalentValue.visibility = if(isChecked) View.GONE else View.VISIBLE }
|
||||
cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
||||
binding.cbEquivalentValue.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.llEquivalentValue.visibility = if (isChecked) View.GONE else View.VISIBLE
|
||||
}
|
||||
binding.cbEquivalentValue.isChecked = mFilterOptions.equivalentValueAll
|
||||
|
||||
val currencyCode = Helper.getCoingeckoSupportedCurrency(Locale.getDefault())
|
||||
mCurrency = Currency.getInstance(currencyCode)
|
||||
|
||||
val fromEquivalentValue = mFilterOptions.fromEquivalentValue /
|
||||
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 /
|
||||
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
|
||||
switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
||||
binding.switchAgoriseFees.isChecked = mFilterOptions.agoriseFees
|
||||
|
||||
// Setup cancel and filter buttons
|
||||
btnCancel.setOnClickListener { dismiss() }
|
||||
btnFilter.setOnClickListener { validateFields() }
|
||||
binding.btnCancel.setOnClickListener { dismiss() }
|
||||
binding.btnFilter.setOnClickListener { validateFields() }
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -187,7 +209,10 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
// Force dialog fragment to use the full width of the screen
|
||||
// TODO use the same width as standard fragments
|
||||
val dialogWindow = dialog?.window
|
||||
dialogWindow?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
dialogWindow?.setLayout(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -226,18 +251,20 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
}
|
||||
|
||||
private fun validateFields() {
|
||||
mFilterOptions.transactionsDirection = when {
|
||||
rbTransactionAll.isChecked -> 0
|
||||
rbTransactionSent.isChecked -> 1
|
||||
rbTransactionReceived.isChecked -> 2
|
||||
else -> { 0 }
|
||||
mFilterOptions.transactionsDirection = when {
|
||||
binding.rbTransactionAll.isChecked -> 0
|
||||
binding.rbTransactionSent.isChecked -> 1
|
||||
binding.rbTransactionReceived.isChecked -> 2
|
||||
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
|
||||
// fetched the account balances) symbol will be null, make sure that does not create a crash.
|
||||
if (symbol != null)
|
||||
|
@ -245,19 +272,20 @@ class FilterOptionsDialog : DialogFragment(), DatePickerFragment.OnDateSetListen
|
|||
else
|
||||
mFilterOptions.assetAll = true
|
||||
|
||||
mFilterOptions.equivalentValueAll = cbEquivalentValue.isChecked
|
||||
mFilterOptions.equivalentValueAll = binding.cbEquivalentValue.isChecked
|
||||
|
||||
mFilterOptions.fromEquivalentValue = etFromEquivalentValue.text.toString().toLong() *
|
||||
Math.pow(10.0, mCurrency.defaultFractionDigits.toDouble()).toLong()
|
||||
mFilterOptions.fromEquivalentValue =
|
||||
binding.etFromEquivalentValue.text.toString().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()
|
||||
|
||||
// Make sure ToEquivalentValue is at least 50 units bigger than FromEquivalentValue
|
||||
mFilterOptions.toEquivalentValue =
|
||||
Math.max(mFilterOptions.toEquivalentValue, mFilterOptions.fromEquivalentValue + 50)
|
||||
|
||||
mFilterOptions.agoriseFees = switchAgoriseFees.isChecked
|
||||
mFilterOptions.agoriseFees = binding.switchAgoriseFees.isChecked
|
||||
|
||||
mCallback!!.onFilterOptionsSelected(mFilterOptions)
|
||||
dismiss()
|
||||
|
|
|
@ -1,36 +1,42 @@
|
|||
package cy.agorise.bitsybitshareswallet.fragments
|
||||
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.database.entities.UserAccount
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentHomeBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
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() {
|
||||
|
||||
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
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -52,13 +58,21 @@ class HomeFragment : Fragment() {
|
|||
window?.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
// Sets the status and navigation bars background color to a dark blue or just dark
|
||||
context?.let { context ->
|
||||
val statusBarColor = ContextCompat.getColor(context,
|
||||
if (!nightMode) R.color.colorPrimaryVariant else R.color.colorStatusBarDark)
|
||||
val statusBarColor = ContextCompat.getColor(
|
||||
context,
|
||||
if (!nightMode) R.color.colorPrimaryVariant else R.color.colorStatusBarDark
|
||||
)
|
||||
window?.statusBarColor = 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?) {
|
||||
|
@ -82,48 +96,49 @@ class HomeFragment : Fragment() {
|
|||
// Configure UserAccountViewModel to show the current account
|
||||
mUserAccountViewModel = ViewModelProviders.of(this).get(UserAccountViewModel::class.java)
|
||||
|
||||
mUserAccountViewModel.getUserAccount(userId).observe(this, Observer<UserAccount>{ userAccount ->
|
||||
if (userAccount != null) {
|
||||
tvAccountName.text = userAccount.name
|
||||
if (userAccount.isLtm) {
|
||||
// Add the lightning bolt to the start of the account name if it is LTM
|
||||
tvAccountName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
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
|
||||
tvAccountName.compoundDrawablePadding = 12
|
||||
mUserAccountViewModel.getUserAccount(userId)
|
||||
.observe(this, Observer<UserAccount> { userAccount ->
|
||||
if (userAccount != null) {
|
||||
binding.tvAccountName.text = userAccount.name
|
||||
if (userAccount.isLtm) {
|
||||
// Add the lightning bolt to the start of the account name if it is LTM
|
||||
binding.tvAccountName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
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
|
||||
binding.tvAccountName.compoundDrawablePadding = 12
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Navigate to the Receive Transaction Fragment
|
||||
fabReceiveTransaction.setOnClickListener (
|
||||
binding.fabReceiveTransaction.setOnClickListener(
|
||||
Navigation.createNavigateOnClickListener(R.id.receive_action)
|
||||
)
|
||||
|
||||
// Navigate to the Send Transaction Fragment without activating the camera
|
||||
fabSendTransaction.setOnClickListener(
|
||||
binding.fabSendTransaction.setOnClickListener(
|
||||
Navigation.createNavigateOnClickListener(R.id.send_action)
|
||||
)
|
||||
|
||||
// Navigate to the Send Transaction Fragment using Navigation's SafeArgs to activate the camera
|
||||
fabSendTransactionCamera.setOnClickListener {
|
||||
binding.fabSendTransactionCamera.setOnClickListener {
|
||||
val action = HomeFragmentDirections.sendAction(true)
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
|
||||
// Configure ViewPager with PagerAdapter and TabLayout to display the Balances/NetWorth section
|
||||
val pagerAdapter = PagerAdapter(childFragmentManager)
|
||||
viewPager.adapter = pagerAdapter
|
||||
tabLayout.setupWithViewPager(viewPager)
|
||||
binding.viewPager.adapter = pagerAdapter
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
// 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
|
||||
*/
|
||||
private inner class PagerAdapter internal constructor(fm: FragmentManager) : FragmentPagerAdapter(fm) {
|
||||
private inner class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) {
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
// getItem is called to instantiate the fragment for the given page.
|
||||
|
@ -133,8 +148,12 @@ class HomeFragment : Fragment() {
|
|||
NetWorthFragment()
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return listOf(getString(R.string.title_balances), getString(R.string.title_net_worth), "")[position]
|
||||
override fun getPageTitle(position: Int): CharSequence {
|
||||
return listOf(
|
||||
getString(R.string.title_balances),
|
||||
getString(R.string.title_net_worth),
|
||||
""
|
||||
)[position]
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
|
@ -19,9 +20,12 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
|||
import cy.agorise.bitsybitshareswallet.BuildConfig
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentImportBrainkeyBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
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.calls.GetAccounts
|
||||
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.JsonRpcResponse
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_import_brainkey.*
|
||||
import org.bitcoinj.core.ECKey
|
||||
import java.text.NumberFormat
|
||||
import java.util.*
|
||||
|
@ -42,6 +45,9 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
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 */
|
||||
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*/
|
||||
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
|
||||
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||
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?) {
|
||||
|
@ -88,7 +104,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
|
||||
// Use RxJava Debounce to update the PIN error only after the user stops writing for > 500 ms
|
||||
mDisposables.add(
|
||||
tietPin.textChanges()
|
||||
binding.tietPin.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.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
|
||||
mDisposables.add(
|
||||
tietPinConfirmation.textChanges()
|
||||
binding.tietPinConfirmation.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.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
|
||||
mDisposables.add(
|
||||
tietBrainKey.textChanges()
|
||||
binding.tietBrainKey.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.map { it.toString().trim() }
|
||||
|
@ -123,14 +139,14 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
)
|
||||
)
|
||||
|
||||
btnImport.isEnabled = false
|
||||
btnImport.setOnClickListener { verifyBrainKey(false) }
|
||||
binding.btnImport.isEnabled = false
|
||||
binding.btnImport.setOnClickListener { verifyBrainKey(false) }
|
||||
|
||||
btnCreate.setOnClickListener (
|
||||
binding.btnCreate.setOnClickListener(
|
||||
Navigation.createNavigateOnClickListener(R.id.create_account_action)
|
||||
)
|
||||
|
||||
tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
||||
binding.tvNetworkStatus.setOnClickListener { v -> showNodesDialog(v) }
|
||||
}
|
||||
|
||||
private fun showNodesDialog(v: View) {
|
||||
|
@ -160,7 +176,13 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
)
|
||||
|
||||
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, "-------"))
|
||||
customListAdapter(nodesAdapter as FullNodesAdapter)
|
||||
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
|
||||
mHandler.post(mRequestDynamicGlobalPropertiesTask)
|
||||
|
@ -178,13 +201,13 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
}
|
||||
|
||||
private fun validatePIN() {
|
||||
val pin = tietPin.text.toString()
|
||||
val pin = binding.tietPin.text.toString()
|
||||
|
||||
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
|
||||
} else {
|
||||
tilPin.isErrorEnabled = false
|
||||
binding.tilPin.isErrorEnabled = false
|
||||
isPINValid = true
|
||||
}
|
||||
|
||||
|
@ -192,13 +215,13 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
}
|
||||
|
||||
private fun validatePINConfirmation() {
|
||||
val pinConfirmation = tietPinConfirmation.text.toString()
|
||||
val pinConfirmation = binding.tietPinConfirmation.text.toString()
|
||||
|
||||
if (pinConfirmation != tietPin.text.toString()) {
|
||||
tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
||||
if (pinConfirmation != binding.tietPin.text.toString()) {
|
||||
binding.tilPinConfirmation.error = getString(R.string.error__pin_mismatch)
|
||||
isPINConfirmationValid = false
|
||||
} else {
|
||||
tilPinConfirmation.isErrorEnabled = false
|
||||
binding.tilPinConfirmation.isErrorEnabled = false
|
||||
isPINConfirmationValid = true
|
||||
}
|
||||
|
||||
|
@ -207,10 +230,10 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
|
||||
private fun validateBrainKey(brainKey: String) {
|
||||
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
|
||||
} else {
|
||||
tilBrainKey.isErrorEnabled = false
|
||||
binding.tilBrainKey.isErrorEnabled = false
|
||||
isBrainKeyValid = true
|
||||
}
|
||||
|
||||
|
@ -218,7 +241,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
}
|
||||
|
||||
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) {
|
||||
//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?
|
||||
if (switchCase) {
|
||||
if (Character.isUpperCase(brainKey.toCharArray()[brainKey.length - 1])) {
|
||||
|
@ -270,7 +293,8 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
mBrainKey = BrainKey(brainKey, 0)
|
||||
val address = Address(ECKey.fromPublicOnly(mBrainKey!!.privateKey.pubKey))
|
||||
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() {
|
||||
|
@ -290,8 +314,14 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
} else if (response.result is DynamicGlobalProperties) {
|
||||
val dynamicGlobalProperties = response.result as DynamicGlobalProperties
|
||||
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
||||
val blockNumber = NumberFormat.getInstance().format(dynamicGlobalProperties.head_block_number)
|
||||
mNodesDialog?.message(text = getString(R.string.title__bitshares_nodes_dialog, blockNumber))
|
||||
val 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() {
|
||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
||||
resources.getDrawable(R.drawable.ic_connected, null), null)
|
||||
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null,
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_connected, null), null
|
||||
)
|
||||
}
|
||||
|
||||
private fun showDisconnectedState() {
|
||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
||||
resources.getDrawable(R.drawable.ic_disconnected, null), null)
|
||||
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null,
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_disconnected, null), null
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -342,7 +376,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
// trying to find out the account name
|
||||
mUserAccount = accountList[0]
|
||||
getAccountsRequestId =
|
||||
mNetworkService?.sendMessage(GetAccounts(mUserAccount), GetAccounts.REQUIRED_API)
|
||||
mNetworkService?.sendMessage(GetAccounts(mUserAccount), GetAccounts.REQUIRED_API)
|
||||
} else {
|
||||
// If we found more than one account linked to this key, we must also
|
||||
// find out the account names, but the procedure is a bit different in
|
||||
|
@ -371,12 +405,18 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
MaterialDialog(context!!)
|
||||
.title(R.string.dialog__account_candidates_title)
|
||||
.message(R.string.dialog__account_candidates_content)
|
||||
.listItemsSingleChoice (items = candidates, initialSelection = -1) { _, index, _ ->
|
||||
.listItemsSingleChoice(
|
||||
items = candidates,
|
||||
initialSelection = -1
|
||||
) { _, index, _ ->
|
||||
if (index >= 0) {
|
||||
// If one account was selected, we keep a reference to it and
|
||||
// store the account properties
|
||||
mUserAccount = mUserAccountCandidates!![index]
|
||||
onAccountSelected(accountPropertiesList[index], tietPin.text.toString())
|
||||
onAccountSelected(
|
||||
accountPropertiesList[index],
|
||||
binding.tietPin.text.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
.positiveButton(android.R.string.ok)
|
||||
|
@ -386,7 +426,7 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
.cancelable(false)
|
||||
.show()
|
||||
} else if (accountPropertiesList.size == 1) {
|
||||
onAccountSelected(accountPropertiesList[0], tietPin.text.toString())
|
||||
onAccountSelected(accountPropertiesList[0], binding.tietPin.text.toString())
|
||||
} else {
|
||||
context?.toast(getString(R.string.error__try_again))
|
||||
}
|
||||
|
@ -400,7 +440,10 @@ class ImportBrainkeyFragment : BaseAccountFragment() {
|
|||
override fun run() {
|
||||
if (mNetworkService != null) {
|
||||
if (mNetworkService?.isConnected == true) {
|
||||
mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
||||
mNetworkService?.sendMessage(
|
||||
GetDynamicGlobalProperties(),
|
||||
GetDynamicGlobalProperties.REQUIRED_API
|
||||
)
|
||||
} else {
|
||||
Log.d(TAG, "NetworkService exists but is not connected")
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentLicenseBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import kotlinx.android.synthetic.main.fragment_license.*
|
||||
|
||||
class LicenseFragment : Fragment() {
|
||||
|
||||
|
@ -19,12 +19,25 @@ class LicenseFragment : Fragment() {
|
|||
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
|
||||
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||
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?) {
|
||||
|
@ -41,11 +54,11 @@ class LicenseFragment : Fragment() {
|
|||
if (agreedLicenseVersion == Constants.CURRENT_LICENSE_VERSION) {
|
||||
agree()
|
||||
} 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() {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import com.jakewharton.rxbinding3.appcompat.queryTextChangeEvents
|
|||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.database.entities.Merchant
|
||||
import cy.agorise.bitsybitshareswallet.database.entities.Teller
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentMerchantsBinding
|
||||
import cy.agorise.bitsybitshareswallet.models.MapObject
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.MerchantClusterRenderer
|
||||
|
@ -50,10 +51,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
|||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.fragment_merchants.*
|
||||
import java.math.BigInteger
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
|
||||
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 var _binding: FragmentMerchantsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private var mMap: GoogleMap? = null
|
||||
|
||||
private lateinit var mMerchantViewModel: MerchantViewModel
|
||||
|
@ -108,17 +110,33 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
private var statusBarSize = 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
|
||||
val activityToolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||
activityToolbar?.visibility = View.GONE
|
||||
|
||||
// Sets the Navigation and Status bars translucent so that the map can be viewed through them
|
||||
val window = activity?.window
|
||||
window?.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
window?.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
|
||||
window?.setFlags(
|
||||
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?) {
|
||||
|
@ -132,17 +150,17 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
view.setOnApplyWindowInsetsListener { v, insets ->
|
||||
statusBarSize = insets.systemWindowInsetTop
|
||||
navigationBarSize = insets.systemWindowInsetBottom
|
||||
val layoutParams = toolbar.layoutParams as ViewGroup.MarginLayoutParams
|
||||
val layoutParams = binding.toolbar.layoutParams as ViewGroup.MarginLayoutParams
|
||||
layoutParams.topMargin = statusBarSize
|
||||
insets
|
||||
}
|
||||
|
||||
// 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)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
toolbar?.setOnClickListener { dismissPopupWindow() }
|
||||
binding.toolbar.setOnClickListener { dismissPopupWindow() }
|
||||
|
||||
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
|
||||
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) {
|
||||
|
@ -192,7 +214,8 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
// Adds listener for the SearchView
|
||||
val searchItem = menu.findItem(R.id.menu_search)
|
||||
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),
|
||||
intArrayOf(R.id.tvName, R.id.tvAddress, R.id.ivMarkerPin)
|
||||
)
|
||||
|
@ -268,24 +291,40 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
|
||||
mapObjects
|
||||
}
|
||||
).subscribe({mapObjects ->
|
||||
).subscribe({ mapObjects ->
|
||||
run {
|
||||
Log.d(TAG, "list with ${mapObjects.size} elements")
|
||||
val cursor = MatrixCursor(
|
||||
arrayOf(
|
||||
SUGGEST_COLUMN_ID, SUGGEST_COLUMN_LAT, SUGGEST_COLUMN_LON, SUGGEST_COLUMN_NAME,
|
||||
SUGGEST_COLUMN_ADDRESS, SUGGEST_COLUMN_IS_MERCHANT, SUGGEST_COLUMN_IMAGE_RESOURCE
|
||||
SUGGEST_COLUMN_ID,
|
||||
SUGGEST_COLUMN_LAT,
|
||||
SUGGEST_COLUMN_LON,
|
||||
SUGGEST_COLUMN_NAME,
|
||||
SUGGEST_COLUMN_ADDRESS,
|
||||
SUGGEST_COLUMN_IS_MERCHANT,
|
||||
SUGGEST_COLUMN_IMAGE_RESOURCE
|
||||
)
|
||||
)
|
||||
for (mapObject in mapObjects) {
|
||||
cursor.addRow(arrayOf(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))
|
||||
cursor.addRow(
|
||||
arrayOf(
|
||||
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)
|
||||
}
|
||||
},
|
||||
{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) {
|
||||
// Try to show or dismiss the custom popup window with the merchants and tellers switches
|
||||
if (mPopupWindow?.isShowing == false) {
|
||||
mPopupWindow?.showAsDropDown(toolbar, screenWidth, 8)
|
||||
mPopupWindow?.showAsDropDown(binding.toolbar, screenWidth, 8)
|
||||
if (mMap?.isMyLocationEnabled == true)
|
||||
mMap?.uiSettings?.isMyLocationButtonEnabled = false
|
||||
} else
|
||||
|
@ -331,7 +370,11 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
|
||||
/** Handles the result from the location permission request */
|
||||
@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)
|
||||
|
||||
if (requestCode == REQUEST_LOCATION_PERMISSION) {
|
||||
|
@ -357,7 +400,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
mMap = googleMap
|
||||
|
||||
// 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()
|
||||
|
||||
|
@ -407,9 +450,13 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
|
||||
private fun verifyLocationPermission() {
|
||||
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
!= PackageManager.PERMISSION_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 {
|
||||
// Permission is already granted
|
||||
mMap?.isMyLocationEnabled = true
|
||||
|
@ -435,7 +482,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
// Force marker to use a custom info window
|
||||
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.addAll(merchants)
|
||||
showHideMerchantsMarkers()
|
||||
|
@ -461,7 +508,7 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
// Force marker to use a custom info window
|
||||
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.addAll(tellers)
|
||||
showHideTellersMarkers()
|
||||
|
@ -512,13 +559,14 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
inner class MerchantInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
|
||||
|
||||
override fun getInfoWindow(marker: Marker?): View {
|
||||
val infoWindowLayout: View = LayoutInflater.from(context).inflate(
|
||||
R.layout.marker_merch_info_window, null)
|
||||
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
||||
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
||||
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
||||
val tvTelegram = infoWindowLayout.findViewById<TextView>(R.id.tvTelegram)
|
||||
val tvWebsite = infoWindowLayout.findViewById<TextView>(R.id.tvWebsite)
|
||||
val infoWindowLayout: View = LayoutInflater.from(context)
|
||||
.inflate(R.layout.marker_merch_info_window, null)
|
||||
|
||||
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
||||
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
||||
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
||||
val tvTelegram = infoWindowLayout.findViewById<TextView>(R.id.tvTelegram)
|
||||
val tvWebsite = infoWindowLayout.findViewById<TextView>(R.id.tvWebsite)
|
||||
|
||||
if (selectedMerchant != null) {
|
||||
tvName.text = selectedMerchant?.name
|
||||
|
@ -557,17 +605,18 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio
|
|||
inner class TellerInfoWindowAdapter : GoogleMap.InfoWindowAdapter {
|
||||
|
||||
override fun getInfoWindow(marker: Marker?): View {
|
||||
val infoWindowLayout: View = LayoutInflater.from(context).inflate(
|
||||
R.layout.marker_teller_info_window, null)
|
||||
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
||||
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
||||
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
||||
val tvTelegram = infoWindowLayout.findViewById<TextView>(R.id.tvTelegram)
|
||||
val tvKeybase = infoWindowLayout.findViewById<TextView>(R.id.tvKeybase)
|
||||
val tvWhatsapp = infoWindowLayout.findViewById<TextView>(R.id.tvWhatsapp)
|
||||
val tvViber = infoWindowLayout.findViewById<TextView>(R.id.tvViber)
|
||||
val tvEmail = infoWindowLayout.findViewById<TextView>(R.id.tvEmail)
|
||||
val tvWebsite = infoWindowLayout.findViewById<TextView>(R.id.tvWebsite)
|
||||
val infoWindowLayout: View = LayoutInflater.from(context)
|
||||
.inflate(R.layout.marker_teller_info_window, null)
|
||||
|
||||
val tvName = infoWindowLayout.findViewById<TextView>(R.id.tvName)
|
||||
val tvAddress = infoWindowLayout.findViewById<TextView>(R.id.tvAddress)
|
||||
val tvPhone = infoWindowLayout.findViewById<TextView>(R.id.tvPhone)
|
||||
val tvTelegram = infoWindowLayout.findViewById<TextView>(R.id.tvTelegram)
|
||||
val tvKeybase = infoWindowLayout.findViewById<TextView>(R.id.tvKeybase)
|
||||
val tvWhatsapp = infoWindowLayout.findViewById<TextView>(R.id.tvWhatsapp)
|
||||
val tvViber = infoWindowLayout.findViewById<TextView>(R.id.tvViber)
|
||||
val tvEmail = infoWindowLayout.findViewById<TextView>(R.id.tvEmail)
|
||||
val tvWebsite = infoWindowLayout.findViewById<TextView>(R.id.tvWebsite)
|
||||
|
||||
if (selectedTeller != null) {
|
||||
tvName.text = selectedTeller?.gt_name
|
||||
|
|
|
@ -5,16 +5,25 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
): View {
|
||||
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.jakewharton.rxbinding3.widget.textChanges
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.databinding.DialogPinSecurityLockBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||
import cy.agorise.bitsybitshareswallet.utils.hideKeyboard
|
||||
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.
|
||||
|
@ -25,9 +25,22 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
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 = ""
|
||||
|
@ -39,13 +52,13 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
crashlytics.setCustomKey(Constants.CRASHLYTICS_KEY_LAST_SCREEN, TAG)
|
||||
|
||||
// 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)
|
||||
|
||||
setupScreen()
|
||||
|
||||
// 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
|
||||
if (actionId == EditorInfo.IME_ACTION_GO) {
|
||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||
|
@ -56,8 +69,8 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
if (hashedPIN == currentHashedPINPattern) {
|
||||
// PIN is correct, proceed
|
||||
resetIncorrectSecurityLockAttemptsAndTime()
|
||||
tietPIN.hideKeyboard()
|
||||
rootView.requestFocus()
|
||||
binding.tietPIN.hideKeyboard()
|
||||
binding.rootView.requestFocus()
|
||||
dismiss()
|
||||
mCallback?.onPINPatternEntered(actionIdentifier)
|
||||
} else {
|
||||
|
@ -65,7 +78,7 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
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
|
||||
// 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()
|
||||
}
|
||||
|
@ -81,7 +94,7 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
} else if (currentStep == STEP_SECURITY_LOCK_CONFIRM) {
|
||||
val pinConfirm = v.text.toString().trim()
|
||||
if (pinConfirm != newPIN) {
|
||||
tvTitle.text = getString(R.string.title__pins_dont_match)
|
||||
binding.tvTitle.text = getString(R.string.title__pins_dont_match)
|
||||
} else {
|
||||
val salt = CryptoUtils.generateSalt()
|
||||
val hashedPIN = CryptoUtils.createSHA256Hash(salt + pinConfirm)
|
||||
|
@ -103,20 +116,21 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
}
|
||||
|
||||
mDisposables.add(
|
||||
tietPIN.textChanges()
|
||||
binding.tietPIN.textChanges()
|
||||
.skipInitialValue()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
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
|
||||
tilPIN.isErrorEnabled = false
|
||||
binding.tilPIN.isErrorEnabled = false
|
||||
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
||||
// Show the min length requirement for the PIN only when it has not been fulfilled
|
||||
if (it.trim().length >= Constants.MIN_PIN_LENGTH) {
|
||||
tilPIN.helperText = ""
|
||||
binding.tilPIN.helperText = ""
|
||||
} 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() {
|
||||
when (currentStep) {
|
||||
STEP_SECURITY_LOCK_VERIFY -> {
|
||||
tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
||||
tvSubTitle.text = getString(R.string.msg__enter_your_pin)
|
||||
tietPIN.isEnabled = true
|
||||
binding.tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
||||
binding.tvSubTitle.text = getString(R.string.msg__enter_your_pin)
|
||||
binding.tietPIN.isEnabled = true
|
||||
if (incorrectSecurityLockAttempts >= Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
||||
// User has entered the PIN incorrectly too many times
|
||||
val now = System.currentTimeMillis()
|
||||
if (now <= incorrectSecurityLockTime + Constants.INCORRECT_SECURITY_LOCK_COOLDOWN) {
|
||||
tietPIN.setText("")
|
||||
tietPIN.isEnabled = false
|
||||
binding.tietPIN.setText("")
|
||||
binding.tietPIN.isEnabled = false
|
||||
startContDownTimer()
|
||||
} else {
|
||||
resetIncorrectSecurityLockAttemptsAndTime()
|
||||
|
@ -142,28 +156,28 @@ class PINSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
}
|
||||
}
|
||||
STEP_SECURITY_LOCK_CREATE -> {
|
||||
tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
||||
tvSubTitle.text = getString(R.string.msg__set_a_pin)
|
||||
tilPIN.helperText = getString(R.string.msg__min_pin_length)
|
||||
tilPIN.isErrorEnabled = false
|
||||
binding.tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
||||
binding.tvSubTitle.text = getString(R.string.msg__set_a_pin)
|
||||
binding.tilPIN.helperText = getString(R.string.msg__min_pin_length)
|
||||
binding.tilPIN.isErrorEnabled = false
|
||||
}
|
||||
STEP_SECURITY_LOCK_CONFIRM -> {
|
||||
tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
||||
tvSubTitle.text = ""
|
||||
tvSubTitle.visibility = View.GONE
|
||||
tietPIN.setText("")
|
||||
tilPIN.helperText = ""
|
||||
tilPIN.isErrorEnabled = false
|
||||
binding.tvTitle.text = getString(R.string.title__re_enter_your_pin)
|
||||
binding.tvSubTitle.text = ""
|
||||
binding.tvSubTitle.visibility = View.GONE
|
||||
binding.tietPIN.setText("")
|
||||
binding.tilPIN.helperText = ""
|
||||
binding.tilPIN.isErrorEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTimerSecondPassed(errorMessage: String) {
|
||||
tilPIN.error = errorMessage
|
||||
binding.tilPIN.error = errorMessage
|
||||
}
|
||||
|
||||
override fun onTimerFinished() {
|
||||
setupScreen()
|
||||
tilPIN.isErrorEnabled = false
|
||||
binding.tilPIN.isErrorEnabled = false
|
||||
}
|
||||
}
|
|
@ -5,11 +5,11 @@ import android.preference.PreferenceManager
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
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.listener.PatternLockViewListener
|
||||
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.CryptoUtils
|
||||
|
||||
|
@ -23,9 +23,22 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
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 = ""
|
||||
|
@ -38,9 +51,9 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
|
||||
setupScreen()
|
||||
|
||||
patternLockView.addPatternLockListener(mPatternLockViewListener)
|
||||
binding.patternLockView.addPatternLockListener(mPatternLockViewListener)
|
||||
|
||||
btnClear.setOnClickListener { setupScreen() }
|
||||
binding.btnClear.setOnClickListener { setupScreen() }
|
||||
}
|
||||
|
||||
private val mPatternLockViewListener = object : PatternLockViewListener {
|
||||
|
@ -51,11 +64,11 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
setMessage("")
|
||||
}
|
||||
STEP_SECURITY_LOCK_CREATE -> {
|
||||
btnClear.visibility = View.INVISIBLE
|
||||
binding.btnClear.visibility = View.INVISIBLE
|
||||
setMessage(getString(R.string.msg__release_finger))
|
||||
}
|
||||
STEP_SECURITY_LOCK_CONFIRM -> {
|
||||
btnClear.visibility = View.INVISIBLE
|
||||
binding.btnClear.visibility = View.INVISIBLE
|
||||
setMessage(getString(R.string.msg__release_finger))
|
||||
}
|
||||
}
|
||||
|
@ -67,8 +80,10 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
|
||||
override fun onComplete(pattern: List<PatternLockView.Dot>) {
|
||||
if (currentStep == STEP_SECURITY_LOCK_VERIFY) {
|
||||
val hashedPattern = CryptoUtils.createSHA256Hash(currentPINPatternSalt +
|
||||
getStringPattern(pattern))
|
||||
val hashedPattern = CryptoUtils.createSHA256Hash(
|
||||
currentPINPatternSalt +
|
||||
getStringPattern(pattern)
|
||||
)
|
||||
if (hashedPattern == currentHashedPINPattern) {
|
||||
// Pattern is correct, proceed
|
||||
resetIncorrectSecurityLockAttemptsAndTime()
|
||||
|
@ -84,17 +99,17 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
setupScreen()
|
||||
}
|
||||
} else if (currentStep == STEP_SECURITY_LOCK_CREATE) {
|
||||
btnClear.visibility = View.VISIBLE
|
||||
binding.btnClear.visibility = View.VISIBLE
|
||||
if (pattern.size < 4) {
|
||||
setError(getString(R.string.error__connect_at_least_4_dots))
|
||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||
} else {
|
||||
setMessage(getString(R.string.text__pattern_recorded))
|
||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
||||
patternLockView.isInputEnabled = false
|
||||
btnNext.isEnabled = true
|
||||
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
||||
binding.patternLockView.isInputEnabled = false
|
||||
binding.btnNext.isEnabled = true
|
||||
newPattern = getStringPattern(pattern)
|
||||
btnNext.setOnClickListener {
|
||||
binding.btnNext.setOnClickListener {
|
||||
currentStep = STEP_SECURITY_LOCK_CONFIRM
|
||||
setupScreen()
|
||||
}
|
||||
|
@ -103,14 +118,14 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
val patternConfirm = getStringPattern(pattern)
|
||||
if (patternConfirm != newPattern) {
|
||||
setError(getString(R.string.error__wront_pattern))
|
||||
btnNext.isEnabled = false
|
||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||
binding.btnNext.isEnabled = false
|
||||
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.WRONG)
|
||||
} else {
|
||||
setMessage(getString(R.string.msg__your_new_unlock_pattern))
|
||||
patternLockView.isEnabled = false
|
||||
patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
||||
btnNext.isEnabled = true
|
||||
btnNext.setOnClickListener {
|
||||
binding.patternLockView.isEnabled = false
|
||||
binding.patternLockView.setViewMode(PatternLockView.PatternViewMode.CORRECT)
|
||||
binding.btnNext.isEnabled = true
|
||||
binding.btnNext.setOnClickListener {
|
||||
context?.let {
|
||||
val salt = CryptoUtils.generateSalt()
|
||||
val hashedPattern = CryptoUtils.createSHA256Hash(salt + patternConfirm)
|
||||
|
@ -119,7 +134,8 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
PreferenceManager.getDefaultSharedPreferences(it).edit()
|
||||
.putString(Constants.KEY_HASHED_PIN_PATTERN, hashedPattern)
|
||||
.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()
|
||||
mCallback?.onPINPatternChanged()
|
||||
|
@ -148,17 +164,17 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
private fun setupScreen() {
|
||||
when (currentStep) {
|
||||
STEP_SECURITY_LOCK_VERIFY -> {
|
||||
tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
||||
tvSubTitle.text = getString(R.string.msg__enter_your_pattern)
|
||||
btnClear.visibility = View.GONE
|
||||
btnNext.visibility = View.GONE
|
||||
patternLockView.isInputEnabled = true
|
||||
patternLockView.isInStealthMode = true
|
||||
binding.tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
||||
binding.tvSubTitle.text = getString(R.string.msg__enter_your_pattern)
|
||||
binding.btnClear.visibility = View.GONE
|
||||
binding.btnNext.visibility = View.GONE
|
||||
binding.patternLockView.isInputEnabled = true
|
||||
binding.patternLockView.isInStealthMode = true
|
||||
if (incorrectSecurityLockAttempts >= Constants.MAX_INCORRECT_SECURITY_LOCK_ATTEMPTS) {
|
||||
// User has entered the Pattern incorrectly too many times
|
||||
val now = System.currentTimeMillis()
|
||||
if (now <= incorrectSecurityLockTime + Constants.INCORRECT_SECURITY_LOCK_COOLDOWN) {
|
||||
patternLockView.isInputEnabled = false
|
||||
binding.patternLockView.isInputEnabled = false
|
||||
startContDownTimer()
|
||||
} else {
|
||||
resetIncorrectSecurityLockAttemptsAndTime()
|
||||
|
@ -166,24 +182,24 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
}
|
||||
}
|
||||
STEP_SECURITY_LOCK_CREATE -> {
|
||||
tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
||||
tvSubTitle.text = getString(R.string.msg__set_a_pattern)
|
||||
binding.tvTitle.text = getString(R.string.title__set_bitsy_security_lock)
|
||||
binding.tvSubTitle.text = getString(R.string.msg__set_a_pattern)
|
||||
setMessage(getString(R.string.text__draw_an_unlock_pattern))
|
||||
patternLockView.clearPattern()
|
||||
patternLockView.isInputEnabled = true
|
||||
btnClear.visibility = View.INVISIBLE
|
||||
btnNext.isEnabled = false
|
||||
binding.patternLockView.clearPattern()
|
||||
binding.patternLockView.isInputEnabled = true
|
||||
binding.btnClear.visibility = View.INVISIBLE
|
||||
binding.btnNext.isEnabled = false
|
||||
}
|
||||
STEP_SECURITY_LOCK_CONFIRM -> {
|
||||
tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
||||
tvSubTitle.text = ""
|
||||
binding.tvTitle.text = getString(R.string.title__re_enter_your_pattern)
|
||||
binding.tvSubTitle.text = ""
|
||||
setMessage(getString(R.string.msg__draw_pattern_confirm))
|
||||
tvSubTitle.visibility = View.GONE
|
||||
patternLockView.clearPattern()
|
||||
patternLockView.isInputEnabled = true
|
||||
btnClear.visibility = View.INVISIBLE
|
||||
btnNext.isEnabled = false
|
||||
btnNext.text = getString(R.string.button__confirm)
|
||||
binding.tvSubTitle.visibility = View.GONE
|
||||
binding.patternLockView.clearPattern()
|
||||
binding.patternLockView.isInputEnabled = true
|
||||
binding.btnClear.visibility = View.INVISIBLE
|
||||
binding.btnNext.isEnabled = false
|
||||
binding.btnNext.text = getString(R.string.button__confirm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,21 +207,21 @@ class PatternSecurityLockDialog : BaseSecurityLockDialog() {
|
|||
@Suppress("DEPRECATION")
|
||||
private fun setMessage(message: String) {
|
||||
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 {
|
||||
tvMessage.setTextAppearance(R.style.TextAppearance_Bitsy_Body2)
|
||||
binding.tvMessage.setTextAppearance(R.style.TextAppearance_Bitsy_Body2)
|
||||
}
|
||||
tvMessage.text = message
|
||||
binding.tvMessage.text = message
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun setError(error: String) {
|
||||
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 {
|
||||
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) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
|||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.AssetsAdapter
|
||||
import cy.agorise.bitsybitshareswallet.adapters.AutoSuggestAssetAdapter
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentReceiveTransactionBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.Helper
|
||||
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.models.JsonRpcResponse
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_receive_transaction.*
|
||||
import java.lang.Exception
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.DecimalFormatSymbols
|
||||
|
@ -53,6 +52,9 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
private const val OTHER_ASSET = "other_asset"
|
||||
}
|
||||
|
||||
private var _binding: FragmentReceiveTransactionBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var mViewModel: ReceiveTransactionViewModel
|
||||
|
||||
/** Current user account */
|
||||
|
@ -75,7 +77,11 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
// Map used to keep track of request and response id pairs
|
||||
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)
|
||||
|
||||
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
|
||||
val window = activity?.window
|
||||
context?.let { context ->
|
||||
val statusBarColor = ContextCompat.getColor(context,
|
||||
if (!nightMode) R.color.colorReceiveDark else R.color.colorStatusBarDark)
|
||||
val statusBarColor = ContextCompat.getColor(
|
||||
context,
|
||||
if (!nightMode) R.color.colorReceiveDark else R.color.colorStatusBarDark
|
||||
)
|
||||
window?.statusBarColor = 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?) {
|
||||
|
@ -110,9 +124,9 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "")
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
mViewModel.getAllNonZero().observe(this,
|
||||
Observer<List<cy.agorise.bitsybitshareswallet.database.entities.Asset>> { assets ->
|
||||
|
@ -121,8 +135,10 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
|
||||
// Add BTS to always show a QR
|
||||
if (mAssets.isEmpty())
|
||||
mAssets.add(cy.agorise.bitsybitshareswallet.database.entities.Asset(
|
||||
"1.3.0", "BTS", 5, "", "")
|
||||
mAssets.add(
|
||||
cy.agorise.bitsybitshareswallet.database.entities.Asset(
|
||||
"1.3.0", "BTS", 5, "", ""
|
||||
)
|
||||
)
|
||||
|
||||
mAssets.sortWith(
|
||||
|
@ -135,33 +151,39 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
)
|
||||
mAssets.add(asset)
|
||||
|
||||
mAssetsAdapter = AssetsAdapter(context!!, android.R.layout.simple_spinner_item, mAssets)
|
||||
spAsset.adapter = mAssetsAdapter
|
||||
mAssetsAdapter =
|
||||
AssetsAdapter(context!!, android.R.layout.simple_spinner_item, mAssets)
|
||||
binding.spAsset.adapter = mAssetsAdapter
|
||||
|
||||
// Try to select the selectedAssetSymbol
|
||||
for (i in 0 until mAssetsAdapter!!.count) {
|
||||
if (mAssetsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
||||
spAsset.setSelection(i)
|
||||
binding.spAsset.setSelection(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
mViewModel.qrCodeBitmap.observe(this, Observer { bitmap ->
|
||||
ivQR.setImageBitmap(bitmap)
|
||||
binding.ivQR.setImageBitmap(bitmap)
|
||||
})
|
||||
|
||||
spAsset.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) { }
|
||||
binding.spAsset.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
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 ->
|
||||
if (asset.id == OTHER_ASSET) {
|
||||
tilAsset.visibility = View.VISIBLE
|
||||
actvAsset.showKeyboard()
|
||||
binding.tilAsset.visibility = View.VISIBLE
|
||||
binding.actvAsset.showKeyboard()
|
||||
mAsset = null
|
||||
} else {
|
||||
tilAsset.visibility = View.GONE
|
||||
binding.tilAsset.visibility = View.GONE
|
||||
selectedAssetSymbol = asset.symbol
|
||||
|
||||
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
|
||||
mDisposables.add(
|
||||
tietAmount.textChanges()
|
||||
binding.tietAmount.textChanges()
|
||||
.debounce(1000, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe( { updateQR() }, { mAsset = null} )
|
||||
.subscribe({ updateQR() }, { mAsset = null })
|
||||
)
|
||||
|
||||
// Add adapter to the Assets AutoCompleteTextView
|
||||
mAutoSuggestAssetAdapter = AutoSuggestAssetAdapter(context!!, android.R.layout.simple_dropdown_item_1line)
|
||||
actvAsset.setAdapter(mAutoSuggestAssetAdapter)
|
||||
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
|
||||
// the first call when the View is created
|
||||
mDisposables.add(
|
||||
actvAsset.textChanges()
|
||||
binding.actvAsset.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.map { it.toString().trim().toUpperCase() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe( {
|
||||
.subscribe({
|
||||
if (!selectedInAutoCompleteTextView) {
|
||||
mAsset = null
|
||||
updateQR()
|
||||
|
@ -201,15 +224,18 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
|
||||
// Get a list of assets that match the already typed string by the user
|
||||
if (it.length > 1 && mNetworkService != null) {
|
||||
val id = mNetworkService?.sendMessage(ListAssets(it, AUTO_SUGGEST_ASSET_LIMIT),
|
||||
ListAssets.REQUIRED_API)
|
||||
val id = mNetworkService?.sendMessage(
|
||||
ListAssets(it, AUTO_SUGGEST_ASSET_LIMIT),
|
||||
ListAssets.REQUIRED_API
|
||||
)
|
||||
if (id != null) responseMap.append(id, RESPONSE_LIST_ASSETS)
|
||||
}
|
||||
}, { mAsset = null } )
|
||||
}, { mAsset = null })
|
||||
)
|
||||
|
||||
actvAsset.setOnItemClickListener { parent, _, position, _ ->
|
||||
val asset = parent.adapter.getItem(position) as cy.agorise.bitsybitshareswallet.database.entities.Asset
|
||||
binding.actvAsset.setOnItemClickListener { parent, _, position, _ ->
|
||||
val asset =
|
||||
parent.adapter.getItem(position) as cy.agorise.bitsybitshareswallet.database.entities.Asset
|
||||
mAsset = Asset(asset.id, asset.toString(), asset.precision)
|
||||
selectedInAutoCompleteTextView = true
|
||||
updateQR()
|
||||
|
@ -220,7 +246,7 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
if (responseMap.containsKey(response.id)) {
|
||||
val responseType = responseMap[response.id]
|
||||
when (responseType) {
|
||||
RESPONSE_LIST_ASSETS -> handleListAssets(response.result)
|
||||
RESPONSE_LIST_ASSETS -> handleListAssets(response.result)
|
||||
}
|
||||
responseMap.remove(response.id)
|
||||
}
|
||||
|
@ -261,7 +287,7 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
|
||||
private fun updateQR() {
|
||||
if (mAsset == null) {
|
||||
ivQR.setImageDrawable(null)
|
||||
binding.ivQR.setImageDrawable(null)
|
||||
// TODO clean the please pay and to text at the bottom too
|
||||
return
|
||||
}
|
||||
|
@ -270,20 +296,22 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
|
||||
// Try to obtain the amount from the Amount Text Field or make it zero otherwise
|
||||
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()
|
||||
}catch (e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
0
|
||||
}
|
||||
|
||||
val total = AssetAmount(UnsignedLong.valueOf(amount), asset)
|
||||
val totalInDouble = Util.fromBase(total)
|
||||
val items = arrayOf(LineItem("transfer", 1, totalInDouble))
|
||||
val invoice = Invoice(mUserAccount?.name, "", "",
|
||||
asset.symbol.replaceFirst("bit", ""), items, "", "")
|
||||
val invoice = Invoice(
|
||||
mUserAccount?.name, "", "",
|
||||
asset.symbol.replaceFirst("bit", ""), items, "", ""
|
||||
)
|
||||
Log.d(TAG, "invoice: " + invoice.toJsonString())
|
||||
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)
|
||||
} catch (e: NullPointerException) {
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
getString(R.string.template__please_send, getString(R.string.text__any_amount), " ")
|
||||
} else {
|
||||
val df = DecimalFormat("####."+("#".repeat(assetPrecision)))
|
||||
val df = DecimalFormat("####." + ("#".repeat(assetPrecision)))
|
||||
df.roundingMode = RoundingMode.CEILING
|
||||
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
||||
|
||||
|
@ -309,8 +342,8 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
|
||||
val txtAccount = getString(R.string.template__to, account)
|
||||
|
||||
tvPleasePay.text = txtAmount
|
||||
tvTo.text = txtAccount
|
||||
binding.tvPleasePay.text = txtAmount
|
||||
binding.tvTo.text = txtAccount
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
|
@ -332,18 +365,28 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
}
|
||||
|
||||
private fun verifyStoragePermission() {
|
||||
if (ContextCompat.checkSelfPermission(activity!!, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
activity!!,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// Permission is not already granted
|
||||
requestPermissions(arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
|
||||
requestPermissions(
|
||||
arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION
|
||||
)
|
||||
} else {
|
||||
// Permission is already granted
|
||||
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)
|
||||
|
||||
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
||||
|
@ -367,13 +410,13 @@ class ReceiveTransactionFragment : ConnectedFragment() {
|
|||
return
|
||||
|
||||
// Get Screenshot
|
||||
val screenshot = Helper.loadBitmapFromView(container)
|
||||
val screenshot = Helper.loadBitmapFromView(binding.container)
|
||||
val imageUri = Helper.saveTemporalBitmap(context!!, screenshot)
|
||||
|
||||
// Prepare information for share intent
|
||||
val subject = getString(R.string.msg__invoice_subject, mUserAccount?.name)
|
||||
val content = tvPleasePay.text.toString() + "\n" +
|
||||
tvTo.text.toString()
|
||||
val content = binding.tvPleasePay.text.toString() + "\n" +
|
||||
binding.tvTo.text.toString()
|
||||
|
||||
// Create share intent and call it
|
||||
val shareIntent = Intent()
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.jakewharton.rxbinding3.widget.textChanges
|
|||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.BalancesDetailsAdapter
|
||||
import cy.agorise.bitsybitshareswallet.database.joins.BalanceDetail
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentSendTransactionBinding
|
||||
import cy.agorise.bitsybitshareswallet.utils.*
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.BalanceDetailViewModel
|
||||
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.TransferOperationBuilder
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.android.synthetic.main.fragment_send_transaction.*
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||
import org.bitcoinj.core.DumpedPrivateKey
|
||||
import org.bitcoinj.core.ECKey
|
||||
|
@ -74,6 +74,9 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
private const val ACTION_SEND_TRANSFER = 1
|
||||
}
|
||||
|
||||
private var _binding: FragmentSendTransactionBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
// Navigation AAC Safe Args
|
||||
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 */
|
||||
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)
|
||||
|
||||
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
|
||||
val window = activity?.window
|
||||
context?.let { context ->
|
||||
val statusBarColor = ContextCompat.getColor(context,
|
||||
if (!nightMode) R.color.colorSendDark else R.color.colorStatusBarDark)
|
||||
val statusBarColor = ContextCompat.getColor(
|
||||
context,
|
||||
if (!nightMode) R.color.colorSendDark else R.color.colorStatusBarDark
|
||||
)
|
||||
window?.statusBarColor = 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?) {
|
||||
|
@ -147,7 +162,7 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
mUserAccount = UserAccount(userId)
|
||||
|
||||
// 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,
|
||||
Observer<String> { encryptedWIF ->
|
||||
|
@ -168,37 +183,43 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
}, 500)
|
||||
}
|
||||
|
||||
fabOpenCamera.setOnClickListener { if (isCameraPreviewVisible) stopCameraPreview() else verifyCameraPermission() }
|
||||
binding.fabOpenCamera.setOnClickListener { if (isCameraPreviewVisible) stopCameraPreview() else verifyCameraPermission() }
|
||||
|
||||
// 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 ->
|
||||
mBalancesDetails.clear()
|
||||
mBalancesDetails.addAll(balancesDetails)
|
||||
mBalancesDetails.sortWith(
|
||||
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
||||
)
|
||||
mBalancesDetailsAdapter = BalancesDetailsAdapter(context!!, android.R.layout.simple_spinner_item, mBalancesDetails)
|
||||
spAsset.adapter = mBalancesDetailsAdapter
|
||||
mBalanceDetailViewModel.getAll()
|
||||
.observe(this, Observer<List<BalanceDetail>> { balancesDetails ->
|
||||
mBalancesDetails.clear()
|
||||
mBalancesDetails.addAll(balancesDetails)
|
||||
mBalancesDetails.sortWith(
|
||||
Comparator { a, b -> a.toString().compareTo(b.toString(), true) }
|
||||
)
|
||||
mBalancesDetailsAdapter = BalancesDetailsAdapter(
|
||||
context!!,
|
||||
android.R.layout.simple_spinner_item,
|
||||
mBalancesDetails
|
||||
)
|
||||
binding.spAsset.adapter = mBalancesDetailsAdapter
|
||||
|
||||
// Try to select the selectedAssetSymbol
|
||||
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
||||
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
||||
spAsset.setSelection(i)
|
||||
break
|
||||
// Try to select the selectedAssetSymbol
|
||||
for (i in 0 until mBalancesDetailsAdapter!!.count) {
|
||||
if (mBalancesDetailsAdapter!!.getItem(i)!!.symbol == selectedAssetSymbol) {
|
||||
binding.spAsset.setSelection(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
spAsset.onItemSelectedListener = assetItemSelectedListener
|
||||
binding.spAsset.onItemSelectedListener = assetItemSelectedListener
|
||||
|
||||
fabSendTransaction.setOnClickListener { verifySecurityLockSendTransfer() }
|
||||
fabSendTransaction.disable(R.color.lightGray)
|
||||
binding.fabSendTransaction.setOnClickListener { verifySecurityLockSendTransfer() }
|
||||
binding.fabSendTransaction.disable(R.color.lightGray)
|
||||
|
||||
// Use RxJava Debounce to avoid making calls to the NetworkService on every text change event
|
||||
mDisposables.add(
|
||||
tietTo.textChanges()
|
||||
binding.tietTo.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.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
|
||||
mDisposables.add(
|
||||
tietAmount.textChanges()
|
||||
binding.tietAmount.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.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
|
||||
mDisposables.add(
|
||||
tietMemo.textChanges()
|
||||
binding.tietMemo.textChanges()
|
||||
.skipInitialValue()
|
||||
.debounce(500, TimeUnit.MILLISECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.map { it.toString().trim() }
|
||||
.subscribe {
|
||||
isMemoCorrect = it.length <= tilMemo.counterMaxLength
|
||||
isMemoCorrect = it.length <= binding.tilMemo.counterMaxLength
|
||||
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
|
||||
* current user's balance of the selected asset. */
|
||||
private val assetItemSelectedListener = object : AdapterView.OnItemSelectedListener{
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) { }
|
||||
private val assetItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
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())
|
||||
|
||||
tvAvailableAssetAmount.text =
|
||||
String.format("%." + Math.min(balance.precision, 8) + "f %s", amount, balance.toString())
|
||||
binding.tvAvailableAssetAmount.text =
|
||||
String.format(
|
||||
"%." + Math.min(balance.precision, 8) + "f %s",
|
||||
amount,
|
||||
balance.toString()
|
||||
)
|
||||
|
||||
validateAmount()
|
||||
}
|
||||
|
@ -262,10 +287,10 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||
if (responseMap.containsKey(response.id)) {
|
||||
when (responseMap[response.id]) {
|
||||
RESPONSE_GET_ACCOUNT_BY_NAME -> handleAccountProperties(response.result)
|
||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES -> handleDynamicGlobalProperties(response.result)
|
||||
RESPONSE_GET_REQUIRED_FEES -> handleRequiredFees(response.result)
|
||||
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
||||
RESPONSE_GET_ACCOUNT_BY_NAME -> handleAccountProperties(response.result)
|
||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES -> handleDynamicGlobalProperties(response.result)
|
||||
RESPONSE_GET_REQUIRED_FEES -> handleRequiredFees(response.result)
|
||||
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
||||
}
|
||||
responseMap.remove(response.id)
|
||||
}
|
||||
|
@ -285,12 +310,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
if (result is AccountProperties) {
|
||||
mSelectedUserAccount = UserAccount(result.id, result.name)
|
||||
destinationPublicKey = result.active.keyAuths.keys.iterator().next()
|
||||
tilTo.isErrorEnabled = false
|
||||
binding.tilTo.isErrorEnabled = false
|
||||
isToAccountCorrect = true
|
||||
} else {
|
||||
mSelectedUserAccount = null
|
||||
destinationPublicKey = null
|
||||
tilTo.error = getString(R.string.error__invalid_account)
|
||||
binding.tilTo.error = getString(R.string.error__invalid_account)
|
||||
isToAccountCorrect = false
|
||||
}
|
||||
|
||||
|
@ -320,7 +345,10 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
|
||||
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)
|
||||
} else {
|
||||
context?.toast(getString(R.string.msg__transaction_not_sent))
|
||||
|
@ -334,7 +362,10 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
Log.d(TAG, "GetRequiredFees: " + transaction.toString())
|
||||
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)
|
||||
} else {
|
||||
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 */
|
||||
private fun verifyCameraPermission() {
|
||||
if (ContextCompat.checkSelfPermission(activity!!, android.Manifest.permission.CAMERA)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
!= PackageManager.PERMISSION_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 {
|
||||
// Permission is already granted
|
||||
startCameraPreview()
|
||||
|
@ -367,7 +402,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
}
|
||||
|
||||
/** 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)
|
||||
|
||||
if (requestCode == REQUEST_CAMERA_PERMISSION) {
|
||||
|
@ -381,27 +420,27 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
}
|
||||
|
||||
private fun startCameraPreview() {
|
||||
cameraPreview.visibility = View.VISIBLE
|
||||
fabOpenCamera.setImageResource(R.drawable.ic_close)
|
||||
binding.cameraPreview.visibility = View.VISIBLE
|
||||
binding.fabOpenCamera.setImageResource(R.drawable.ic_close)
|
||||
isCameraPreviewVisible = true
|
||||
|
||||
// Configure QR scanner
|
||||
cameraPreview.setFormats(listOf(BarcodeFormat.QR_CODE))
|
||||
cameraPreview.setAspectTolerance(0.5f)
|
||||
cameraPreview.setAutoFocus(true)
|
||||
cameraPreview.setLaserColor(R.color.colorSecondary)
|
||||
cameraPreview.setMaskColor(R.color.colorSecondary)
|
||||
cameraPreview.setResultHandler(this)
|
||||
cameraPreview.startCamera()
|
||||
binding.cameraPreview.setFormats(listOf(BarcodeFormat.QR_CODE))
|
||||
binding.cameraPreview.setAspectTolerance(0.5f)
|
||||
binding.cameraPreview.setAutoFocus(true)
|
||||
binding.cameraPreview.setLaserColor(R.color.colorSecondary)
|
||||
binding.cameraPreview.setMaskColor(R.color.colorSecondary)
|
||||
binding.cameraPreview.setResultHandler(this)
|
||||
binding.cameraPreview.startCamera()
|
||||
|
||||
cameraPreview.scrollY = holderCamera.width / 6
|
||||
binding.cameraPreview.scrollY = binding.holderCamera.width / 6
|
||||
}
|
||||
|
||||
private fun stopCameraPreview() {
|
||||
cameraPreview.visibility = View.INVISIBLE
|
||||
fabOpenCamera.setImageResource(R.drawable.ic_camera)
|
||||
binding.cameraPreview.visibility = View.INVISIBLE
|
||||
binding.fabOpenCamera.setImageResource(R.drawable.ic_camera)
|
||||
isCameraPreviewVisible = false
|
||||
cameraPreview.stopCamera()
|
||||
binding.cameraPreview.stopCamera()
|
||||
}
|
||||
|
||||
/** Handles the result of the QR code read from the camera **/
|
||||
|
@ -410,19 +449,19 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
}
|
||||
|
||||
/** Tries to populate the Account, Amount and Memo fields
|
||||
* and the Asset spinner with the obtained information */
|
||||
* and the Asset spinner with the obtained information */
|
||||
private fun populatePropertiesFromQRCodeString(qrString: String) {
|
||||
try {
|
||||
val invoice = Invoice.fromQrCode(qrString)
|
||||
|
||||
Log.d(TAG, "QR Code read: " + invoice.toJsonString())
|
||||
|
||||
tietTo.setText(invoice.to)
|
||||
binding.tietTo.setText(invoice.to)
|
||||
|
||||
if (invoice.memo != null) {
|
||||
tietMemo.setText(invoice.memo)
|
||||
binding.tietMemo.setText(invoice.memo)
|
||||
if (invoice.memo.startsWith("PP"))
|
||||
tietMemo.isEnabled = false
|
||||
binding.tietMemo.isEnabled = false
|
||||
}
|
||||
|
||||
var balanceDetail: BalanceDetail? = null
|
||||
|
@ -432,8 +471,9 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
if (mBalancesDetailsAdapter?.getItem(i)?.symbol == invoice.currency.toUpperCase() ||
|
||||
(invoice.currency.startsWith("bit", true) &&
|
||||
invoice.currency.replaceFirst("bit", "").toUpperCase() ==
|
||||
mBalancesDetailsAdapter?.getItem(i)?.symbol)) {
|
||||
spAsset.setSelection(i)
|
||||
mBalancesDetailsAdapter?.getItem(i)?.symbol)
|
||||
) {
|
||||
binding.spAsset.setSelection(i)
|
||||
balanceDetail = mBalancesDetailsAdapter?.getItem(i)
|
||||
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
|
||||
// return early to avoid filling the asset field
|
||||
if (balanceDetail == null) {
|
||||
Snackbar.make(rootView, getString(R.string.error__you_dont_own_asset, invoice.currency.toUpperCase()),
|
||||
Snackbar.LENGTH_INDEFINITE).setAction(android.R.string.ok) { }.show()
|
||||
Snackbar.make(
|
||||
binding.rootView,
|
||||
getString(R.string.error__you_dont_own_asset, invoice.currency.toUpperCase()),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).setAction(android.R.string.ok) { }.show()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -457,12 +500,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
val df = DecimalFormat("####." + "#".repeat(balanceDetail.precision))
|
||||
df.roundingMode = RoundingMode.CEILING
|
||||
df.decimalFormatSymbols = DecimalFormatSymbols(Locale.getDefault())
|
||||
tietAmount.setText(df.format(amount))
|
||||
binding.tietAmount.setText(df.format(amount))
|
||||
} else {
|
||||
tietAmount.setText("")
|
||||
binding.tietAmount.setText("")
|
||||
}
|
||||
|
||||
}catch (e: Exception) {
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "Invoice error: " + e.message)
|
||||
}
|
||||
}
|
||||
|
@ -473,15 +516,19 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
*/
|
||||
private fun validateAccount(accountName: String) {
|
||||
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)
|
||||
}
|
||||
|
||||
private fun validateAmount() {
|
||||
val txtAmount = tietAmount.text.toString().replace(",", ".")
|
||||
val txtAmount = binding.tietAmount.text.toString().replace(",", ".")
|
||||
|
||||
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 amount: Double = try {
|
||||
|
@ -492,15 +539,15 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
|
||||
when {
|
||||
currentAmount < amount -> {
|
||||
tilAmount.error = getString(R.string.error__not_enough_funds)
|
||||
binding.tilAmount.error = getString(R.string.error__not_enough_funds)
|
||||
isAmountCorrect = false
|
||||
}
|
||||
amount == 0.0 -> {
|
||||
tilAmount.isErrorEnabled = false
|
||||
binding.tilAmount.isErrorEnabled = false
|
||||
isAmountCorrect = false
|
||||
}
|
||||
else -> {
|
||||
tilAmount.isErrorEnabled = false
|
||||
binding.tilAmount.isErrorEnabled = false
|
||||
isAmountCorrect = true
|
||||
}
|
||||
}
|
||||
|
@ -510,11 +557,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
|
||||
private fun enableDisableSendFAB() {
|
||||
if (isToAccountCorrect && isAmountCorrect && isMemoCorrect) {
|
||||
fabSendTransaction.enable(R.color.colorSend)
|
||||
vSend.setBackgroundResource(R.drawable.send_fab_background)
|
||||
binding.fabSendTransaction.enable(R.color.colorSend)
|
||||
binding.vSend.setBackgroundResource(R.drawable.send_fab_background)
|
||||
} else {
|
||||
fabSendTransaction.disable(R.color.lightGray)
|
||||
vSend.setBackgroundResource(R.drawable.send_fab_background_disabled)
|
||||
binding.fabSendTransaction.disable(R.color.lightGray)
|
||||
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
|
||||
val args = Bundle()
|
||||
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY)
|
||||
args.putInt(
|
||||
BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY
|
||||
)
|
||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, ACTION_SEND_TRANSFER)
|
||||
|
||||
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
|
||||
* NetworkService to obtain the [DynamicGlobalProperties] object needed to successfully send a Transfer */
|
||||
private fun startSendTransferOperation() {
|
||||
// Create TransferOperation
|
||||
if (mNetworkService?.isConnected == true) {
|
||||
val balance = mBalancesDetailsAdapter!!.getItem(spAsset.selectedItemPosition)!!
|
||||
val amount = (tietAmount.text.toString().replace(",", ".").toDouble()
|
||||
val balance = mBalancesDetailsAdapter!!.getItem(binding.spAsset.selectedItemPosition)!!
|
||||
val amount = (binding.tietAmount.text.toString().replace(",", ".").toDouble()
|
||||
* Math.pow(10.0, balance.precision.toDouble())).toLong()
|
||||
|
||||
val transferAmount = AssetAmount(UnsignedLong.valueOf(amount), Asset(balance.id))
|
||||
|
@ -574,13 +624,16 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
.setDestination(mSelectedUserAccount)
|
||||
.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
|
||||
val memoMsg = tietMemo.text.toString()
|
||||
val memoMsg = binding.tietMemo.text.toString()
|
||||
if (memoMsg.isNotEmpty()) {
|
||||
val nonce = Math.abs(SecureRandomGenerator.getSecureRandom().nextLong()).toBigInteger()
|
||||
val encryptedMemo = Memo.encryptMessage(privateKey, destinationPublicKey!!, nonce, memoMsg)
|
||||
val nonce =
|
||||
Math.abs(SecureRandomGenerator.getSecureRandom().nextLong()).toBigInteger()
|
||||
val encryptedMemo =
|
||||
Memo.encryptMessage(privateKey, destinationPublicKey!!, nonce, memoMsg)
|
||||
val from = Address(ECKey.fromPublicOnly(privateKey.pubKey))
|
||||
val to = Address(destinationPublicKey!!.key)
|
||||
val memo = Memo(from, to, nonce, encryptedMemo)
|
||||
|
@ -601,9 +654,11 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
transaction = Transaction(privateKey, null, operations)
|
||||
|
||||
// Start the send transaction procedure which includes a series of calls
|
||||
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(),
|
||||
GetDynamicGlobalProperties.REQUIRED_API)
|
||||
if (id != null ) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES)
|
||||
val id = mNetworkService?.sendMessage(
|
||||
GetDynamicGlobalProperties(),
|
||||
GetDynamicGlobalProperties.REQUIRED_API
|
||||
)
|
||||
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES)
|
||||
} else
|
||||
Log.d(TAG, "Network Service is not connected")
|
||||
}
|
||||
|
@ -634,8 +689,12 @@ class SendTransactionFragment : ConnectedFragment(), ZXingScannerView.ResultHand
|
|||
return null
|
||||
|
||||
// Verify that the current Asset is either BTS or a SmartCoin
|
||||
if (Constants.assetsWhichSendFeeToAgorise.contains(transferOperation.assetAmount?.asset?.objectId ?: "")) {
|
||||
val fee = transferOperation.assetAmount?.multiplyBy(Constants.FEE_PERCENTAGE) ?: return null
|
||||
if (Constants.assetsWhichSendFeeToAgorise.contains(
|
||||
transferOperation.assetAmount?.asset?.objectId ?: ""
|
||||
)
|
||||
) {
|
||||
val fee =
|
||||
transferOperation.assetAmount?.multiplyBy(Constants.FEE_PERCENTAGE) ?: return null
|
||||
|
||||
return TransferOperationBuilder()
|
||||
.setSource(mUserAccount)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package cy.agorise.bitsybitshareswallet.fragments
|
||||
|
||||
import android.content.*
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.preference.PreferenceManager
|
||||
|
@ -10,6 +10,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.collection.LongSparseArray
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
|
@ -22,6 +23,7 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
|||
import cy.agorise.bitsybitshareswallet.BuildConfig
|
||||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.FullNodesAdapter
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentSettingsBinding
|
||||
import cy.agorise.bitsybitshareswallet.repositories.AuthorityRepository
|
||||
import cy.agorise.bitsybitshareswallet.utils.Constants
|
||||
import cy.agorise.bitsybitshareswallet.utils.CryptoUtils
|
||||
|
@ -36,7 +38,6 @@ import cy.agorise.graphenej.models.JsonRpcResponse
|
|||
import cy.agorise.graphenej.operations.AccountUpgradeOperationBuilder
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.fragment_settings.*
|
||||
import org.bitcoinj.core.DumpedPrivateKey
|
||||
import org.bitcoinj.core.ECKey
|
||||
import java.text.NumberFormat
|
||||
|
@ -59,6 +60,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
private const val RESPONSE_BROADCAST_TRANSACTION = 3
|
||||
}
|
||||
|
||||
private var _binding: FragmentSettingsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var mViewModel: SettingsFragmentViewModel
|
||||
|
||||
private var mUserAccount: UserAccount? = null
|
||||
|
@ -82,17 +86,27 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
|
||||
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)
|
||||
|
||||
val nightMode = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
||||
.getBoolean(Constants.KEY_NIGHT_MODE_ACTIVATED, false)
|
||||
|
||||
// Make sure the toolbar show the correct colors in both day and night modes
|
||||
val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar)
|
||||
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?) {
|
||||
|
@ -105,15 +119,16 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
||||
|
||||
// Configure ViewModel
|
||||
mViewModel= ViewModelProviders.of(this).get(SettingsFragmentViewModel::class.java)
|
||||
mViewModel = ViewModelProviders.of(this).get(SettingsFragmentViewModel::class.java)
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
mViewModel.getWIF(userId, AuthorityType.ACTIVE.ordinal).observe(this,
|
||||
androidx.lifecycle.Observer<String> { encryptedWIF ->
|
||||
|
@ -121,7 +136,10 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
try {
|
||||
privateKey = CryptoUtils.decrypt(it, encryptedWIF)
|
||||
} 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) {
|
||||
crashlytics.recordException(e)
|
||||
}
|
||||
|
@ -132,22 +150,23 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
|
||||
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
|
||||
val securityLockSelected = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||
.getInt(Constants.KEY_SECURITY_LOCK_SELECTED, 0)
|
||||
// Security Lock Options
|
||||
// 0 -> None
|
||||
// 1 -> PIN
|
||||
// 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() }
|
||||
tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected() }
|
||||
binding.tvSecurityLock.setOnClickListener { onSecurityLockTextSelected() }
|
||||
binding.tvSecurityLockSelected.setOnClickListener { onSecurityLockTextSelected() }
|
||||
|
||||
btnViewBrainKey.setOnClickListener { onShowBrainKeyButtonSelected() }
|
||||
binding.btnViewBrainKey.setOnClickListener { onShowBrainKeyButtonSelected() }
|
||||
|
||||
val lastAccountBackup = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.getLong(Constants.KEY_LAST_ACCOUNT_BACKUP, 0L)
|
||||
|
@ -155,11 +174,11 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
val now = System.currentTimeMillis()
|
||||
|
||||
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) {
|
||||
|
@ -191,7 +210,13 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
mNodesDialogLinearLayoutManager = LinearLayoutManager(v.context)
|
||||
|
||||
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, "-------"))
|
||||
customListAdapter(nodesAdapter as FullNodesAdapter, mNodesDialogLinearLayoutManager)
|
||||
negativeButton(android.R.string.ok)
|
||||
|
@ -218,9 +243,13 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) {
|
||||
if (responseMap.containsKey(response.id)) {
|
||||
when (responseMap[response.id]) {
|
||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES -> handleDynamicGlobalPropertiesNodes(response.result)
|
||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM -> handleDynamicGlobalPropertiesLTM(response.result)
|
||||
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_NODES -> handleDynamicGlobalPropertiesNodes(
|
||||
response.result
|
||||
)
|
||||
RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM -> handleDynamicGlobalPropertiesLTM(
|
||||
response.result
|
||||
)
|
||||
RESPONSE_BROADCAST_TRANSACTION -> handleBroadcastTransaction(response)
|
||||
}
|
||||
responseMap.remove(response.id)
|
||||
}
|
||||
|
@ -238,13 +267,17 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
}
|
||||
|
||||
private fun showConnectedState() {
|
||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
||||
resources.getDrawable(R.drawable.ic_connected, null), null)
|
||||
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
null, null,
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_connected, null), null
|
||||
)
|
||||
}
|
||||
|
||||
private fun showDisconnectedState() {
|
||||
tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null,
|
||||
resources.getDrawable(R.drawable.ic_disconnected, null), null)
|
||||
binding.tvNetworkStatus.setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
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
|
||||
|
@ -253,7 +286,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
if (result is DynamicGlobalProperties) {
|
||||
if (mNodesDialog != null && mNodesDialog?.isShowing == true) {
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
mHandler.postDelayed(this, Constants.BLOCK_PERIOD)
|
||||
|
@ -320,9 +361,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
val autoCloseOn = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.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()
|
||||
.putBoolean(Constants.KEY_AUTO_CLOSE_ACTIVATED, isChecked).apply()
|
||||
}
|
||||
|
@ -337,9 +378,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
val nightModeOn = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.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()
|
||||
.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
|
||||
val args = Bundle()
|
||||
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY)
|
||||
args.putInt(
|
||||
BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_VERIFY
|
||||
)
|
||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, actionIdentifier)
|
||||
|
||||
return when (securityLockSelected) {
|
||||
|
@ -397,9 +440,9 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
override fun onPINPatternEntered(actionIdentifier: Int) {
|
||||
when (actionIdentifier) {
|
||||
ACTION_CHANGE_SECURITY_LOCK -> showChooseSecurityLockDialog()
|
||||
ACTION_SHOW_BRAINKEY -> getBrainkey()
|
||||
ACTION_UPGRADE_TO_LTM -> showUpgradeToLTMDialog()
|
||||
ACTION_REMOVE_ACCOUNT -> showRemoveAccountDialog()
|
||||
ACTION_SHOW_BRAINKEY -> getBrainkey()
|
||||
ACTION_UPGRADE_TO_LTM -> showUpgradeToLTMDialog()
|
||||
ACTION_REMOVE_ACCOUNT -> showRemoveAccountDialog()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,7 +455,8 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
// 1 -> PIN
|
||||
// 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 {
|
||||
MaterialDialog(it).show {
|
||||
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
|
||||
val args = Bundle()
|
||||
args.putInt(BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_CREATE)
|
||||
args.putInt(
|
||||
BaseSecurityLockDialog.KEY_STEP_SECURITY_LOCK,
|
||||
BaseSecurityLockDialog.STEP_SECURITY_LOCK_CREATE
|
||||
)
|
||||
args.putInt(BaseSecurityLockDialog.KEY_ACTION_IDENTIFIER, -1)
|
||||
|
||||
when (index) {
|
||||
0 -> { /* None */
|
||||
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
|
||||
onPINPatternChanged()
|
||||
|
@ -491,7 +541,8 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
.subscribeOn(Schedulers.io())
|
||||
.map { authority ->
|
||||
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)
|
||||
BrainKey(plainBrainKey, sequenceNumber)
|
||||
}
|
||||
|
@ -518,7 +569,7 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
val now = System.currentTimeMillis()
|
||||
PreferenceManager.getDefaultSharedPreferences(it.context).edit()
|
||||
.putLong(Constants.KEY_LAST_ACCOUNT_BACKUP, now).apply()
|
||||
tvBackupWarning.visibility = View.GONE
|
||||
binding.tvBackupWarning.visibility = View.GONE
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
|
@ -541,11 +592,18 @@ class SettingsFragment : ConnectedFragment(), BaseSecurityLockDialog.OnPINPatter
|
|||
operations.add(operation)
|
||||
|
||||
val currentPrivateKey = ECKey.fromPrivate(
|
||||
DumpedPrivateKey.fromBase58(null, privateKey).key.privKeyBytes)
|
||||
DumpedPrivateKey.fromBase58(null, privateKey).key.privKeyBytes
|
||||
)
|
||||
ltmTransaction = Transaction(currentPrivateKey, null, operations)
|
||||
|
||||
val id = mNetworkService?.sendMessage(GetDynamicGlobalProperties(), GetDynamicGlobalProperties.REQUIRED_API)
|
||||
if (id != null) responseMap.append(id, RESPONSE_GET_DYNAMIC_GLOBAL_PROPERTIES_LTM)
|
||||
val id = mNetworkService?.sendMessage(
|
||||
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.
|
||||
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
|
||||
// single activity architecture.
|
||||
|
|
|
@ -21,12 +21,12 @@ import com.jakewharton.rxbinding3.appcompat.queryTextChangeEvents
|
|||
import cy.agorise.bitsybitshareswallet.R
|
||||
import cy.agorise.bitsybitshareswallet.adapters.TransfersDetailsAdapter
|
||||
import cy.agorise.bitsybitshareswallet.database.joins.TransferDetail
|
||||
import cy.agorise.bitsybitshareswallet.databinding.FragmentTransactionsBinding
|
||||
import cy.agorise.bitsybitshareswallet.models.FilterOptions
|
||||
import cy.agorise.bitsybitshareswallet.utils.*
|
||||
import cy.agorise.bitsybitshareswallet.viewmodels.TransactionsViewModel
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import kotlinx.android.synthetic.main.fragment_transactions.*
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
|
@ -42,14 +42,27 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
|||
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 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)
|
||||
|
||||
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?) {
|
||||
|
@ -62,8 +75,8 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
|||
.getString(Constants.KEY_CURRENT_ACCOUNT_ID, "") ?: ""
|
||||
|
||||
val transfersDetailsAdapter = TransfersDetailsAdapter(context!!)
|
||||
rvTransactions.adapter = transfersDetailsAdapter
|
||||
rvTransactions.layoutManager = LinearLayoutManager(context)
|
||||
binding.rvTransactions.adapter = transfersDetailsAdapter
|
||||
binding.rvTransactions.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
// Configure TransactionsViewModel to fetch the transaction history
|
||||
mViewModel = ViewModelProviders.of(this).get(TransactionsViewModel::class.java)
|
||||
|
@ -71,11 +84,11 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
|||
mViewModel.getFilteredTransactions(userId).observe(this,
|
||||
Observer<List<TransferDetail>> { transactions ->
|
||||
if (transactions.isEmpty()) {
|
||||
rvTransactions.visibility = View.GONE
|
||||
tvEmpty.visibility = View.VISIBLE
|
||||
binding.rvTransactions.visibility = View.GONE
|
||||
binding.tvEmpty.visibility = View.VISIBLE
|
||||
} else {
|
||||
rvTransactions.visibility = View.VISIBLE
|
||||
tvEmpty.visibility = View.GONE
|
||||
binding.rvTransactions.visibility = View.VISIBLE
|
||||
binding.tvEmpty.visibility = View.GONE
|
||||
|
||||
val shouldScrollUp = transactions.size - transfersDetailsAdapter.itemCount == 1
|
||||
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
|
||||
// which most likely means a new transaction was received/sent.
|
||||
if (shouldScrollUp)
|
||||
rvTransactions.scrollToPosition(0)
|
||||
binding.rvTransactions.scrollToPosition(0)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Set custom touch listener to handle bounce/stretch effect
|
||||
val bounceTouchListener = BounceTouchListener(rvTransactions)
|
||||
rvTransactions.setOnTouchListener(bounceTouchListener)
|
||||
val bounceTouchListener = BounceTouchListener(binding.rvTransactions)
|
||||
binding.rvTransactions.setOnTouchListener(bounceTouchListener)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
|
@ -118,7 +131,10 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
|||
R.id.menu_filter -> {
|
||||
val filterOptionsDialog = FilterOptionsDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelable(FilterOptionsDialog.KEY_FILTER_OPTIONS, mViewModel.getFilterOptions())
|
||||
args.putParcelable(
|
||||
FilterOptionsDialog.KEY_FILTER_OPTIONS,
|
||||
mViewModel.getFilterOptions()
|
||||
)
|
||||
filterOptionsDialog.arguments = args
|
||||
filterOptionsDialog.show(childFragmentManager, "filter-options-tag")
|
||||
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 */
|
||||
private fun verifyStoragePermission() {
|
||||
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
activity!!,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
!= PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
// Permission is not already granted
|
||||
requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION)
|
||||
requestPermissions(
|
||||
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
|
||||
REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION
|
||||
)
|
||||
} else {
|
||||
// Permission is already granted
|
||||
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
|
||||
* dialog, but if it was not accepted then shows a toast explaining that the permission is necessary to generate
|
||||
* 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)
|
||||
if (requestCode == REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION) {
|
||||
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
|
||||
|
@ -181,7 +207,10 @@ class TransactionsFragment : Fragment(), FilterOptionsDialog.OnFilterOptionsSele
|
|||
private fun showExportOptionsDialog() {
|
||||
MaterialDialog(context!!).show {
|
||||
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 exportCSV = indices.contains(1)
|
||||
exportFilteredTransactions(exportPDF, exportCSV)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package cy.agorise.bitsybitshareswallet.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import cy.agorise.bitsybitshareswallet.fragments.TransactionsFragment
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
/**
|
||||
* Model that includes all the options to filter the transactions in the [TransactionsFragment]
|
||||
*/
|
||||
@Parcelize
|
||||
data class FilterOptions (
|
||||
data class FilterOptions(
|
||||
var query: String = "",
|
||||
var transactionsDirection: Int = 0,
|
||||
var dateRangeAll: Boolean = true,
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
android:elevation="4dp"
|
||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
||||
|
||||
<fragment
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/navHostFragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
|
|
Loading…
Reference in a new issue