From c63388155ab5dd568d27f86d6b9e34ea06451f72 Mon Sep 17 00:00:00 2001 From: Severiano Jaramillo Date: Mon, 26 Nov 2018 15:11:34 -0600 Subject: [PATCH] Created a DatabaseLoadActivity which loads basic information of all the BitShares assets and stores them in the database. This process only occurs once during the initial setup. --- app/src/main/AndroidManifest.xml | 7 +- .../activities/ConnectedActivity.kt | 8 +- .../activities/DatabaseLoadActivity.kt | 181 ++++++++++++++++++ .../activities/LicenseActivity.kt | 15 +- .../bitsybitshareswallet/daos/AssetDao.kt | 4 + .../repositories/AssetRepository.kt | 30 +++ .../bitsybitshareswallet/utils/Constants.kt | 15 +- .../res/layout/activity_database_load.xml | 89 +++++++++ app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/strings.xml | 8 + app/src/main/res/values/styles.xml | 1 + 11 files changed, 347 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/cy/agorise/bitsybitshareswallet/activities/DatabaseLoadActivity.kt create mode 100644 app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/AssetRepository.kt create mode 100644 app/src/main/res/layout/activity_database_load.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 29fb061..97989d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,14 +21,15 @@ + + + + - - - \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt index 5df194e..76f3564 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/ConnectedActivity.kt @@ -72,13 +72,13 @@ abstract class ConnectedActivity : AppCompatActivity(), ServiceConnection { Toast.LENGTH_LONG ).show() } -// } else if (message is ConnectionStatusUpdate) { -// handleConnectionStatusUpdate(message) -// if (message.updateCode == ConnectionStatusUpdate.DISCONNECTED) { + } else if (message is ConnectionStatusUpdate) { + handleConnectionStatusUpdate(message) + if (message.updateCode == ConnectionStatusUpdate.DISCONNECTED) { // recurrentAccountUpdateId = -1 // accountOpRequestId = -1 // isProcessingTx = false -// } + } } } } diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/DatabaseLoadActivity.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/DatabaseLoadActivity.kt new file mode 100644 index 0000000..47328c5 --- /dev/null +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/DatabaseLoadActivity.kt @@ -0,0 +1,181 @@ +package cy.agorise.bitsybitshareswallet.activities + +import android.content.ComponentName +import android.content.Intent +import android.os.Bundle +import android.os.CountDownTimer +import android.os.Handler +import android.os.IBinder +import android.preference.PreferenceManager +import android.util.Log +import android.view.View +import cy.agorise.bitsybitshareswallet.R +import cy.agorise.bitsybitshareswallet.repositories.AssetRepository +import cy.agorise.bitsybitshareswallet.utils.Constants +import cy.agorise.graphenej.Asset +import cy.agorise.graphenej.api.ApiAccess +import cy.agorise.graphenej.api.ConnectionStatusUpdate +import cy.agorise.graphenej.api.calls.ListAssets +import cy.agorise.graphenej.models.JsonRpcResponse +import kotlinx.android.synthetic.main.activity_database_load.* + +class DatabaseLoadActivity: ConnectedActivity() { + private val TAG = "DatabaseLoadActivity" + + /** Time in milliseconds to wait before re-trying to access the full node */ + private val NETWORK_RETRY_PERIOD: Long = 1000 + + /** Handler instance used to schedule tasks back to the main thread */ + private var mHandler: Handler? = null + + /** Timer used to avoid multiple instances of the following activity */ + private var countDownTimer: CountDownTimer? = null + + /** Repository used as the single point of truth for Assets */ + private var mAssetRepository: AssetRepository? = null + + // Variable used to keep track of the last lower bound used int asset batch loading + private var lastLowerBound: String? = null + + // Variable used to keep track of the possession state of the database api id + private var hasDatabaseApiId: Boolean = false + + // Variable used to count the number of assets already loaded + private var loadedAssetsCounter: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_database_load) + + mHandler = Handler() + + mAssetRepository = AssetRepository(application) + + btnNext.setOnClickListener { onNext() } + } + override fun handleJsonRpcResponse(response: JsonRpcResponse<*>) { + if (response.result is List<*> && + (response.result as List<*>).size > 0 && + (response.result as List<*>)[0] is Asset) { + handlePlatformAssetBatch(response.result as List) + } + } + + override fun handleConnectionStatusUpdate(connectionStatusUpdate: ConnectionStatusUpdate) { + if (connectionStatusUpdate.updateCode == ConnectionStatusUpdate.API_UPDATE && + connectionStatusUpdate.api and ApiAccess.API_DATABASE == ApiAccess.API_DATABASE) { + hasDatabaseApiId = true + this.lastLowerBound = "" + sendAssetBatchRequest() + } + } + + /** + * Method that issues a request for the next 100 known assets, starting from the last known + * lower bound for the asset symbol. + */ + private fun sendAssetBatchRequest() { + if (mNetworkService != null && mNetworkService!!.isConnected) { + mNetworkService!!.sendMessage(ListAssets(lastLowerBound, ListAssets.LIST_ALL), ListAssets.REQUIRED_API) + } else { + Handler().postDelayed({ sendAssetBatchRequest() }, NETWORK_RETRY_PERIOD) + } + } + + /** + * Method that loads a new batch of platform assets into the database and decides whether to finish + * the procedure or to keep requesting for more assets. + * + * @param assetList The list of assets obtained in the last 'list_assets' API call. + */ + private fun handlePlatformAssetBatch(assetList: List) { + val assets = mutableListOf() + + // TODO find if there is a better way to convert to Bitsy Asset instances + for (_asset in assetList) { + val asset = cy.agorise.bitsybitshareswallet.models.Asset( + _asset.objectId, + _asset.symbol, + _asset.precision, + _asset.description ?: "", + _asset.bitassetId ?: "" + ) + + assets.add(asset) + } + + mAssetRepository!!.insertAll(assets) + + loadedAssetsCounter += assetList.size + + tvLoadMessage.text = getString(R.string.text__loading_assets, loadedAssetsCounter) + + if (assetList.size < ListAssets.MAX_BATCH_SIZE) { + // We might have reached the end of the asset list + Log.d(TAG, "We might have reached the end!") + + // Storing the last asset update time and setting the database as loaded + PreferenceManager.getDefaultSharedPreferences(applicationContext) + .edit() + .putLong(Constants.KEY_LAST_ASSET_LIST_UPDATE, System.currentTimeMillis()) + .apply() + + onAssetsReady() + } else { + // Using the last asset symbol in the list as the new lower bound. + lastLowerBound = assetList[assetList.size - 1].symbol + sendAssetBatchRequest() + } + } + + private fun onAssetsReady() { + // Storing the last asset update time and setting the database as loaded + PreferenceManager.getDefaultSharedPreferences(applicationContext) + .edit() + .putBoolean(Constants.KEY_DATABASE_LOADED, true) + .apply() + + + mHandler!!.post { + progressBar.visibility = View.INVISIBLE + btnNext.isEnabled = true + tvLoadTitle.setText(R.string.title__assets_loaded) + tvLoadMessage.setText(R.string.text__assets_loaded) + + // Timer to automatically take user to the next activity + countDownTimer = object : CountDownTimer(5000, 1000) { + + override fun onTick(millisUntilFinished: Long) {} + + override fun onFinish() { + onNext() + } + }.start() + } + } + + /** + * Called whenever the user clicks on the 'next' button_light. This button_light will only be visible when + * the database loading procedure is done, OR if there was an error in it. + */ + fun onNext() { + // Cancel timer to avoid starting InitialSetupActivity twice + countDownTimer!!.cancel() + + val intent = Intent(applicationContext, ImportBrainkeyActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) +// overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left) + finish() + } + + override fun onServiceConnected(name: ComponentName?, service: IBinder?) { + super.onServiceConnected(name, service) + + hasDatabaseApiId = mNetworkService!!.hasApiId(ApiAccess.API_DATABASE) + if (hasDatabaseApiId) { + this.lastLowerBound = "" + sendAssetBatchRequest() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/LicenseActivity.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/LicenseActivity.kt index 71807fa..e47d03e 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/LicenseActivity.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/activities/LicenseActivity.kt @@ -32,7 +32,8 @@ class LicenseActivity : AppCompatActivity() { /** * This function stores the version of the current accepted license version into the Shared Preferences and - * sends the user to either import/create account if there is no active account or to the MainActivity otherwise + * sends the user to load the assets database if they have not been loaded, import/create account if there is no + * active account or to the MainActivity otherwise. */ private fun agree() { PreferenceManager.getDefaultSharedPreferences(this).edit() @@ -40,13 +41,19 @@ class LicenseActivity : AppCompatActivity() { val intent : Intent? + val isDatabaseLoaded = PreferenceManager.getDefaultSharedPreferences(this) + .getBoolean(Constants.KEY_DATABASE_LOADED, false) + val initialSetupDone = PreferenceManager.getDefaultSharedPreferences(this) .getBoolean(Constants.KEY_INITIAL_SETUP_DONE, false) - intent = if (initialSetupDone) - Intent(this, MainActivity::class.java) - else + intent = if (!isDatabaseLoaded) + Intent(this, DatabaseLoadActivity::class.java) + else if (!initialSetupDone) Intent(this, ImportBrainkeyActivity::class.java) + else + Intent(this, MainActivity::class.java) + startActivity(intent) finish() diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/daos/AssetDao.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/daos/AssetDao.kt index 5083f19..2a343a3 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/daos/AssetDao.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/daos/AssetDao.kt @@ -3,6 +3,7 @@ package cy.agorise.bitsybitshareswallet.daos import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert +import androidx.room.OnConflictStrategy import androidx.room.Query import cy.agorise.bitsybitshareswallet.models.Asset @@ -11,6 +12,9 @@ interface AssetDao { @Insert fun insert(asset: Asset) + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertAll(assets: List) + @Query("SELECT * FROM assets") fun getAllAssets(): LiveData> } diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/AssetRepository.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/AssetRepository.kt new file mode 100644 index 0000000..9830f2a --- /dev/null +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/AssetRepository.kt @@ -0,0 +1,30 @@ +package cy.agorise.bitsybitshareswallet.repositories + +import android.app.Application +import android.os.AsyncTask +import cy.agorise.bitsybitshareswallet.daos.AssetDao +import cy.agorise.bitsybitshareswallet.daos.BitsyDatabase +import cy.agorise.bitsybitshareswallet.models.Asset + +class AssetRepository internal constructor(application: Application) { + + private val mAssetDao: AssetDao + + init { + val db = BitsyDatabase.getDatabase(application) + mAssetDao = db!!.assetDao() + } + + fun insertAll(assets: List) { + insertAllAsyncTask(mAssetDao).execute(assets) + } + + private class insertAllAsyncTask internal constructor(private val mAsyncTaskDao: AssetDao) : + AsyncTask, Void, Void>() { + + override fun doInBackground(vararg assets: List): Void? { + mAsyncTaskDao.insertAll(assets[0]) + return null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Constants.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Constants.kt index 36fd11a..3bc277b 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Constants.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/utils/Constants.kt @@ -9,6 +9,18 @@ object Constants { /** Version of the currently used license */ const val CURRENT_LICENSE_VERSION = 1 + /** Key used to store if the assets database has been loaded or not */ + const val KEY_DATABASE_LOADED = "key_database_loaded" + + /** + * Key used to store a preference value used to keep track of the last time the assets in + * database were updated. + */ + const val KEY_LAST_ASSET_LIST_UPDATE = "key_last_assets_update" + + /** Key used to store if the initial setup is already done or not */ + const val KEY_INITIAL_SETUP_DONE = "key_initial_setup_done" + /** Key used to store the id value of the currently active account in the shared preferences */ const val KEY_CURRENT_ACCOUNT_ID = "key_current_account_id" @@ -24,9 +36,6 @@ object Constants { */ const val LIFETIME_EXPIRATION_DATE = "1969-12-31T23:59:59" - /** Key used to store if the initial setup is already done or not */ - const val KEY_INITIAL_SETUP_DONE = "key_initial_setup_done" - /** Key used to store the night mode setting into the shared preferences */ const val KEY_NIGHT_MODE_ACTIVATED = "key_night_mode_activated" } diff --git a/app/src/main/res/layout/activity_database_load.xml b/app/src/main/res/layout/activity_database_load.xml new file mode 100644 index 0000000..a9bc4f2 --- /dev/null +++ b/app/src/main/res/layout/activity_database_load.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 808684c..934cc19 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -12,4 +12,7 @@ 24dp 40dp + + 180dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3c5c341..18cbd24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,14 @@ Agree Disagree + + Loading Assets… + Performing a series of requests in order to get the complete list of all existing assets + Loaded %1$d assets into the database + Assets Loaded + Next, please setup your account… + Next + 6+ digits PIN PIN too short diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e883885..dcc2016 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -42,6 +42,7 @@ +