diff --git a/app/build.gradle b/app/build.gradle index 4b1a404..9cea642 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,6 +47,11 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + // Gradle automatically adds 'android.test.runner' as a dependency. + useLibrary 'android.test.runner' + useLibrary 'android.test.base' + useLibrary 'android.test.mock' + } dependencies { @@ -100,9 +105,12 @@ dependencies { debugImplementation 'com.amitshekhar.android:debug-db:1.0.4' // TODO enable and make proper testing -// testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.12' + // Core library + androidTestImplementation 'androidx.test:core:1.1.0' + // testImplementation "androidx.arch.core:core-testing:$lifecycle_version" // testImplementation "androidx.room:room-testing:$room_version" // androidTestImplementation 'androidx.test:runner:1.1.0' -// androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/app/src/androidTest/java/cy/agorise/bitsybitshareswallet/MerchantQueryTest.kt b/app/src/androidTest/java/cy/agorise/bitsybitshareswallet/MerchantQueryTest.kt new file mode 100644 index 0000000..17b2e04 --- /dev/null +++ b/app/src/androidTest/java/cy/agorise/bitsybitshareswallet/MerchantQueryTest.kt @@ -0,0 +1,65 @@ +package cy.agorise.bitsybitshareswallet; + +import android.content.Context +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.runner.AndroidJUnit4 +import cy.agorise.bitsybitshareswallet.database.BitsyDatabase +import cy.agorise.bitsybitshareswallet.database.entities.Merchant +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + +@RunWith(AndroidJUnit4::class) +public class MerchantQueryTest { + private lateinit var db: BitsyDatabase + + @Before + fun createDb(){ + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder(context, BitsyDatabase::class.java).build() + + // Creating a few sample merchants + val merchant1 = Merchant("5c23c8c234c83f0013e67786", + "Café del Mar", + "Address1", + 17.82834, + -9.38483, + "+1 999.999.9999", + "@user", + "https://100Natural.com") + val merchant2 = Merchant("5c23c91a34c83f0013e67787", + "Condesa Acapulco", + "Address2", + 13.82834, + -67.38483, + "+1 999.999.9999", + "@user", + "https://100Natural.com") + db.merchantDao().insert(merchant1) + db.merchantDao().insert(merchant2) + } + + @After + @Throws(IOException::class) + fun closeDb(){ + db.close() + } + + @Test + fun testSimpleQuery(){ + val allMerchants = db.merchantDao().getAllSync() + Assert.assertEquals(2, allMerchants.size) + } + + @Test + fun testSearchQuery(){ + val query = "%con%" + val merchants = db.merchantDao().findMerchantsByWordSync(query) + Assert.assertEquals(1, merchants.size) + Assert.assertEquals(merchants[0].name, "Condesa Acapulco") + } +} diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/MerchantDao.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/MerchantDao.kt index 2476684..296829b 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/MerchantDao.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/MerchantDao.kt @@ -6,6 +6,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import cy.agorise.bitsybitshareswallet.database.entities.Merchant +import io.reactivex.Single @Dao interface MerchantDao { @@ -18,6 +19,15 @@ interface MerchantDao { @Query("SELECT * FROM merchants") fun getAll(): LiveData> + @Query("SELECT * FROM merchants") + fun getAllSync(): List + + @Query("SELECT * FROM merchants WHERE (name LIKE :query) OR (address LIKE :query) OR (phone LIKE :query) OR (telegram LIKE :query) OR (website LIKE :query)") + fun findMerchantsByWord(query: String): Single> + + @Query("SELECT * FROM merchants WHERE (name LIKE :query) OR (address LIKE :query) OR (phone LIKE :query) OR (telegram LIKE :query) OR (website LIKE :query)") + fun findMerchantsByWordSync(query: String): List + @Query("DELETE FROM merchants") fun deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/TellerDao.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/TellerDao.kt index 64b5ec7..a6531ce 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/TellerDao.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/database/daos/TellerDao.kt @@ -6,6 +6,7 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import cy.agorise.bitsybitshareswallet.database.entities.Teller +import io.reactivex.Single @Dao interface TellerDao { @@ -18,6 +19,9 @@ interface TellerDao { @Query("SELECT * FROM tellers") fun getAll(): LiveData> + @Query("SELECT * FROM tellers WHERE (name LIKE :query) OR (address LIKE :query) OR (phone LIKE :query) OR (telegram LIKE :query) OR (website LIKE :query)") + fun findTellersByWord(query: String): Single> + @Query("DELETE FROM tellers") fun deleteAll() } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/MerchantsFragment.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/MerchantsFragment.kt index 61a808e..e1981a6 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/MerchantsFragment.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/fragments/MerchantsFragment.kt @@ -6,13 +6,8 @@ import android.content.pm.PackageManager import android.database.Cursor import android.database.MatrixCursor import android.os.Bundle -import android.util.Log -import com.google.android.gms.maps.GoogleMap -import com.google.android.gms.maps.OnMapReadyCallback -import com.google.android.gms.maps.SupportMapFragment - -import cy.agorise.bitsybitshareswallet.R import android.preference.PreferenceManager +import android.util.Log import android.view.* import android.widget.PopupWindow import android.widget.TextView @@ -27,12 +22,18 @@ import androidx.lifecycle.ViewModelProviders import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.customview.customView import com.google.android.gms.maps.CameraUpdateFactory -import com.google.android.gms.maps.model.* +import com.google.android.gms.maps.GoogleMap +import com.google.android.gms.maps.OnMapReadyCallback +import com.google.android.gms.maps.SupportMapFragment +import com.google.android.gms.maps.model.LatLngBounds +import com.google.android.gms.maps.model.MapStyleOptions +import com.google.android.gms.maps.model.Marker import com.google.maps.android.MarkerManager import com.google.maps.android.clustering.Cluster import com.google.maps.android.clustering.ClusterItem import com.google.maps.android.clustering.ClusterManager 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.utils.Constants @@ -42,7 +43,9 @@ import cy.agorise.bitsybitshareswallet.utils.toast import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import java.lang.Exception +import io.reactivex.schedulers.Schedulers +import java.math.BigInteger +import java.util.* import java.util.concurrent.TimeUnit @@ -173,20 +176,34 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestio } private fun updateSearchViewSuggestions(query: String) { - // TODO make call to the db to obtain the list of merchants/tellers + val merchObs = mMerchantViewModel.queryMerchants(query) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) - // Create a cursor out of an Array - val cursor = MatrixCursor(arrayOf(SUGGEST_COLUMN_ID, SUGGEST_COLUMN_NAME, - SUGGEST_COLUMN_ADDRESS, SUGGEST_COLUMN_IS_MERCHANT, SUGGEST_COLUMN_IMAGE_RESOURCE)) + val tellerObs = mMerchantViewModel.queryTellers(query) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) - for (i in 1..3) { - if (i%2 == 1) - cursor.addRow(arrayOf(i.toString(), "Merchant $i", "Address $i", 1, R.drawable.ic_merchant_pin)) - else - cursor.addRow(arrayOf(i.toString(), "Teller $i", "Address $i", 0, R.drawable.ic_teller_pin)) - } + //TODO: Combine the results of both the merchants and teller queries +// val combined: Observable> = Observable.combineLatest, List, List>(merchObs, tellerObs, BiFunction { t1: List, t2:List -> Arrays.asList(t1, t2)}) + + merchObs.subscribe({list -> + run { + Log.d(TAG, "list with ${list.size} elements") + val cursor = MatrixCursor( + arrayOf( + SUGGEST_COLUMN_ID, SUGGEST_COLUMN_NAME, + SUGGEST_COLUMN_ADDRESS, SUGGEST_COLUMN_IS_MERCHANT, SUGGEST_COLUMN_IMAGE_RESOURCE + ) + ) + for (item in list) { + cursor.addRow(arrayOf(BigInteger(item._id, 16).toLong(), item.name, item.address, 1, R.drawable.ic_merchant_pin)) + } + mSearchView?.suggestionsAdapter?.changeCursor(cursor) + } + }, + {error -> Log.e(TAG, "Error while retrieving autocomplete suggestions. Msg: ${error}")}) - mSearchView?.suggestionsAdapter?.changeCursor(cursor) } override fun onSuggestionSelect(position: Int): Boolean { diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/MerchantRepository.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/MerchantRepository.kt index 615fde6..99594a6 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/MerchantRepository.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/MerchantRepository.kt @@ -11,6 +11,7 @@ import cy.agorise.bitsybitshareswallet.database.entities.Merchant import cy.agorise.bitsybitshareswallet.network.FeathersResponse import cy.agorise.bitsybitshareswallet.network.MerchantsWebservice import cy.agorise.bitsybitshareswallet.utils.Constants +import io.reactivex.Single import retrofit2.Call import retrofit2.Response import retrofit2.Retrofit @@ -79,4 +80,8 @@ class MerchantRepository internal constructor(val context: Context) : retrofit2. return null } } + + fun findMerchantsByWord(query: String): Single> { + return mMerchantDao.findMerchantsByWord("%$query%") + } } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/TellerRepository.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/TellerRepository.kt index e96f818..5c46e2d 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/TellerRepository.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/repositories/TellerRepository.kt @@ -11,6 +11,7 @@ import cy.agorise.bitsybitshareswallet.database.entities.Teller import cy.agorise.bitsybitshareswallet.network.FeathersResponse import cy.agorise.bitsybitshareswallet.network.MerchantsWebservice import cy.agorise.bitsybitshareswallet.utils.Constants +import io.reactivex.Single import retrofit2.Call import retrofit2.Response import retrofit2.Retrofit @@ -79,4 +80,8 @@ class TellerRepository internal constructor(val context: Context) : retrofit2.Ca return null } } + + fun findTellerByWord(word: String): Single> { + return mTellerDao.findTellersByWord("%$word%") + } } \ No newline at end of file diff --git a/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/MerchantViewModel.kt b/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/MerchantViewModel.kt index d37eb72..baa3cfd 100644 --- a/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/MerchantViewModel.kt +++ b/app/src/main/java/cy/agorise/bitsybitshareswallet/viewmodels/MerchantViewModel.kt @@ -7,6 +7,7 @@ import cy.agorise.bitsybitshareswallet.database.entities.Merchant import cy.agorise.bitsybitshareswallet.database.entities.Teller import cy.agorise.bitsybitshareswallet.repositories.MerchantRepository import cy.agorise.bitsybitshareswallet.repositories.TellerRepository +import io.reactivex.Single class MerchantViewModel(application: Application) : AndroidViewModel(application) { private var mMerchantRepository = MerchantRepository(application) @@ -19,4 +20,12 @@ class MerchantViewModel(application: Application) : AndroidViewModel(application internal fun getAllTellers(): LiveData> { return mTellerRepository.getAll() } + + fun queryMerchants(query: String): Single> { + return mMerchantRepository.findMerchantsByWord(query) + } + + fun queryTellers(query: String): Single> { + return mTellerRepository.findTellerByWord(query) + } } \ No newline at end of file