This is an automated email from the ASF dual-hosted git repository. rajanmaurya154 pushed a commit to branch development in repository https://gitbox.apache.org/repos/asf/fineract-cn-mobile.git
commit 86c910ab44f65ca8fed3e19689d4924334bd533b Author: jawidMuhammadi <spotlightapp...@gmail.com> AuthorDate: Fri Aug 7 14:49:14 2020 +0530 initialize couchbaseLite, implement group & customers and add UI tests --- app/build.gradle | 10 +- .../creategroup/CreateGroupActivityAndroidTest.kt | 115 +++++++++++++++++ .../grouplist/GroupListFragmentAndroidTest.kt | 54 ++++++++ .../org/apache/fineract/FineractApplication.java | 2 + .../apache/fineract/couchbase/CouchbaseDatabase.kt | 72 +++++++++++ .../org/apache/fineract/couchbase/DocumentType.kt | 10 ++ .../org/apache/fineract/couchbase/Replicate.java | 69 +++++++++++ .../fineract/couchbase/SynchronizationManager.kt | 112 +++++++++++++++++ .../java/org/apache/fineract/data/models/Group.kt | 6 +- .../fineract/data/models/customer/Customer.kt | 46 +++---- .../org/apache/fineract/data/remote/BaseUrl.java | 1 + .../apache/fineract/ui/adapters/GroupsAdapter.kt | 4 +- .../customeractivity/CreateCustomerPresenter.java | 89 +++++--------- .../FormCustomerAddressFragment.java | 18 +-- .../customerdetails/CustomerDetailsFragment.java | 31 +++-- .../customerdetails/CustomerDetailsPresenter.java | 50 ++------ .../customers/customerlist/CustomersFragment.java | 35 +++--- .../customers/customerlist/CustomersPresenter.java | 136 +++++++++------------ .../CustomerTasksBottomSheetContract.java | 5 +- .../CustomerTasksBottomSheetFragment.java | 20 ++- .../CustomerTasksBottomSheetPresenter.java | 70 ++++++----- .../groups/creategroup/CreateGroupActivity.kt | 6 +- .../online/groups/grouplist/GroupListFragment.kt | 24 ++-- .../ui/online/groups/grouplist/GroupViewModel.kt | 64 +++++++--- .../groups/grouplist/GroupViewModelFactory.kt | 11 +- .../grouptasks/GroupTasksBottomSheetFragment.kt | 4 +- .../java/org/apache/fineract/utils/Constants.kt | 4 + .../java/org/apache/fineract/utils/DateUtils.java | 3 +- .../java/org/apache/fineract/utils/GsonUtils.kt | 31 +++++ app/src/main/resources/groups.json | 3 +- .../fineract/online/CustomerPresenterTest.kt | 97 --------------- app/sync-gateway-config.json | 19 +++ build.gradle | 8 +- 33 files changed, 817 insertions(+), 412 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0c7f303..5dbcc83 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -71,6 +71,9 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } } dependencies { @@ -157,6 +160,9 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines" + //couchbase + implementation "com.couchbase.lite:couchbase-lite-android:$couchbase" + // Instrumentation test dependencies androidTestImplementation jUnit @@ -173,6 +179,9 @@ dependencies { androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "androidx.test:runner:$runnerVersion" androidTestImplementation "androidx.test:rules:$rulesVersion" + androidTestImplementation "androidx.test.ext:junit:$extJunit" + androidTestImplementation "androidx.test.espresso:espresso-core:$espressoCore" + androidTestImplementation "androidx.test.espresso:espresso-contrib: $espressonContribute" // Unit tests dependencies testImplementation jUnit @@ -182,7 +191,6 @@ dependencies { testImplementation "org.hamcrest:hamcrest-library:$hamcrestVersion" testImplementation "org.hamcrest:hamcrest-integration:$hamcrestVersion" testImplementation "org.robolectric:robolectric:$roboElectricVersion" - } // Log out test results to console diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt new file mode 100644 index 0000000..83441ad --- /dev/null +++ b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivityAndroidTest.kt @@ -0,0 +1,115 @@ +package org.apache.fineract.ui.online.groups.creategroup + +import android.content.Intent +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.rule.ActivityTestRule +import org.apache.fineract.R +import org.apache.fineract.couchbase.SynchronizationManager +import org.apache.fineract.data.models.Group +import org.apache.fineract.ui.online.groups.GroupAction +import org.apache.fineract.utils.Constants +import org.apache.fineract.utils.toDataClass +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Created by Ahmad Jawid Muhammadi on 24/8/20. + */ + +@RunWith(AndroidJUnit4::class) +class CreateGroupActivityAndroidTest { + + @get:Rule + var activityTestRule = + ActivityTestRule<CreateGroupActivity>(CreateGroupActivity::class.java, false, false) + + lateinit var synchronizationManager: SynchronizationManager + + @Before + fun before() { + //Open CreateGroupActivity with an intent by putting create group action as an extra + val intent = Intent().apply { + putExtra(Constants.GROUP_ACTION, GroupAction.CREATE) + } + activityTestRule.launchActivity(intent) + synchronizationManager = SynchronizationManager( + InstrumentationRegistry.getInstrumentation().context + ) + } + + @Test + fun createGroupItem() { + onView(withId(R.id.etIdentifier)) + .perform(typeText("testIdentifier")) + onView(withId(R.id.etGroupDefinitionIdentifier)) + .perform(typeText("group definition")) + onView(withId(R.id.etName)) + .perform(typeText("group name")) + onView(withId(R.id.etOffice)) + .perform(typeText("office name")) + onView(withId(R.id.etAssignedEmployee)) + .perform(typeText("assignedEmployee")) + + //go to next fragment + onView(withText("NEXT")).perform(click()) + + //Add a member + onView(withId(R.id.ibAddMember)) + .perform(click()) + onView(withId(R.id.etNewMember)) + .perform(typeText("Ahmad")) + onView(withId((R.id.btnAddMember))) + .perform(click()) + onView(withText("NEXT")).perform(click()) + + //Add leader name + onView(withId(R.id.ibAddLeader)) + .perform(click()) + onView(withId(R.id.etNewLeader)) + .perform(typeText("Jawid")) + onView(withId((R.id.btnAddLeader))) + .perform(click()) + onView(withText("NEXT")).perform(click()) + + //fill address details + onView(withId(R.id.etStreet)) + .perform(typeText("Street")) + onView(withId(R.id.etCity)) + .perform(typeText("Pune")) + onView(withId(R.id.etRegion)) + .perform(typeText("Region")) + onView(withId(R.id.etPostalCode)) + .perform(typeText("411048")) + onView(withId(R.id.etCountry)) + .perform(typeText("India")) + onView(withText("NEXT")).perform(click()) + onView(withText("COMPLETE")).perform(click()) + + //Then assert if group item has been created + val mapItem = synchronizationManager.getDocumentForTest( + "testIdentifier", + InstrumentationRegistry.getInstrumentation().context + ) + val groupItem = mapItem.toDataClass<Group>() + assertEquals(groupItem.identifier, "testIdentifier") + assertEquals(groupItem.name, "group name") + } + + @After + fun after() { + //delete the created document in test + synchronizationManager.deleteDocument( + "testIdentifier" + ) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt new file mode 100644 index 0000000..88cc97b --- /dev/null +++ b/app/src/androidTest/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragmentAndroidTest.kt @@ -0,0 +1,54 @@ +package org.apache.fineract.ui.online.groups.grouplist + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.contrib.DrawerActions +import androidx.test.espresso.contrib.NavigationViewActions +import androidx.test.espresso.contrib.RecyclerViewActions +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.rule.ActivityTestRule +import org.apache.fineract.R +import org.apache.fineract.ui.adapters.GroupsAdapter +import org.apache.fineract.ui.online.DashboardActivity +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Created by Ahmad Jawid Muhammadi on 24/8/20. + */ + +@RunWith(AndroidJUnit4::class) +class GroupListFragmentAndroidTest { + + @get:Rule + val activityRule = + ActivityTestRule<DashboardActivity>(DashboardActivity::class.java) + + @Test + fun openDrawer_OpenGroupList_ClickOnRecyclerViewItem() { + + //Open drawer + onView(withId(R.id.drawer_layout)) + .perform(DrawerActions.open()) + + //From NavigationView navigate to Group list screen + onView(withId(R.id.nav_view)) + .perform(NavigationViewActions.navigateTo(R.id.item_groups)) + + //Click on group item + onView(withId(R.id.rvGroups)).perform( + RecyclerViewActions.actionOnItem<GroupsAdapter.ViewHolder>( + hasDescendant(withText("group")), + click() + ) + ) + + //Assert if thw item has been displayed correctly + onView(withId(R.id.tvIdentifier)) + .check(matches(withText("group"))) + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/FineractApplication.java b/app/src/main/java/org/apache/fineract/FineractApplication.java index de32205..8144d4f 100644 --- a/app/src/main/java/org/apache/fineract/FineractApplication.java +++ b/app/src/main/java/org/apache/fineract/FineractApplication.java @@ -3,6 +3,7 @@ package org.apache.fineract; import android.app.Application; import android.content.Context; +import com.couchbase.lite.CouchbaseLite; import com.crashlytics.android.Crashlytics; import com.evernote.android.job.JobManager; import com.mifos.mobile.passcode.utils.ForegroundChecker; @@ -37,6 +38,7 @@ public class FineractApplication extends Application { Fabric.with(this, new Crashlytics()); FlowManager.init(this); ForegroundChecker.init(this); + CouchbaseLite.init(this); } public static Context getContext() { diff --git a/app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt b/app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt new file mode 100644 index 0000000..5cfac7b --- /dev/null +++ b/app/src/main/java/org/apache/fineract/couchbase/CouchbaseDatabase.kt @@ -0,0 +1,72 @@ +package org.apache.fineract.couchbase + +import android.content.Context +import com.couchbase.lite.CouchbaseLiteException +import com.couchbase.lite.Database +import com.couchbase.lite.DatabaseConfiguration +import com.couchbase.lite.ListenerToken +import org.apache.fineract.data.local.PreferencesHelper +import org.apache.fineract.injection.ApplicationContext +import org.apache.fineract.utils.Constants.DATABASE_NAME +import javax.inject.Inject + +/** + * Created by Ahmad Jawid Muhammadi on 7/8/20. + */ + +class CouchbaseDatabase @Inject constructor( + @ApplicationContext val context: Context?) { + + @Volatile + private var database: Database? = null + + @Inject + lateinit var preferencesHelper: PreferencesHelper + + private var listener: ListenerToken? = null + + fun getDatabase(): Database { + // we need this check only when we use database for UI/Integration testing + if (!::preferencesHelper.isInitialized) { + preferencesHelper = PreferencesHelper(context) + preferencesHelper.putUserName("ahmad") + } + return database + ?: createDatabase(context, preferencesHelper.userName, DATABASE_NAME).also { + database = it + Replicate.startReplicating(it) + } + } + + fun deleteDatabase() { + database?.close() + database?.delete() + database = null + } + + private fun createDatabase( + context: Context?, + userName: String, + databaseName: String + ): Database { + val configuration = DatabaseConfiguration() +// configuration.directory = String.format( +// "%s/%s", +// context?.filesDir, +// userName + // ) + + return Database(databaseName, configuration) + } + + fun closeDatabaseForUser() { + try { + database?.let { + database?.close() + database = null + } + } catch (e: CouchbaseLiteException) { + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt b/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt new file mode 100644 index 0000000..9e32fd4 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/couchbase/DocumentType.kt @@ -0,0 +1,10 @@ +package org.apache.fineract.couchbase + +/** + * Created by Ahmad Jawid Muhammadi on 14/8/20. + */ + +enum class DocumentType(val value: String) { + GROUP("Group"), + CUSTOMER("customer") +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/couchbase/Replicate.java b/app/src/main/java/org/apache/fineract/couchbase/Replicate.java new file mode 100644 index 0000000..ab61639 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/couchbase/Replicate.java @@ -0,0 +1,69 @@ +package org.apache.fineract.couchbase; + +import android.util.Log; + +import com.couchbase.lite.BasicAuthenticator; +import com.couchbase.lite.Database; +import com.couchbase.lite.Endpoint; +import com.couchbase.lite.Replicator; +import com.couchbase.lite.ReplicatorConfiguration; +import com.couchbase.lite.URLEndpoint; +import com.google.gson.Gson; + +import org.apache.fineract.data.local.PreferencesHelper; + +import java.net.URI; +import java.net.URISyntaxException; + +import static com.couchbase.lite.ReplicatorConfiguration.ReplicatorType; +import static org.apache.fineract.data.remote.BaseUrl.LOCALHOST_URL; +import static org.apache.fineract.utils.Constants.BASIC_AUTH_KEY; + +/** + * Created by Ahmad Jawid Muhammadi on 12/8/20. + */ + +public class Replicate { + + public static final String TAG = Replicate.class.getSimpleName(); + + public static void startReplicating(Database database) throws URISyntaxException { + + Endpoint targetEndpoint = new URLEndpoint(URI.create(LOCALHOST_URL)); + ReplicatorConfiguration config = new ReplicatorConfiguration(database, targetEndpoint); + config.setReplicatorType(ReplicatorType.PUSH_AND_PULL); + + // config.setAuthenticator(new BasicAuthenticator(GATEWAY_USER_NAME, GATEWAY_PASSWORD)); + + Replicator replicator = new Replicator(config); + + replicator.addChangeListener(change -> { + if (change.getStatus().getError() != null) { + Log.e(TAG, "Error status: " + change.getStatus()); + Log.e(TAG, "Error message: " + change.getStatus().getProgress()); + } + }); + + replicator.start(); + } + + /** + * Saves the Basic Authentication in SharedPreference + */ + public void saveBasicAuthentication( + String username, String password, + PreferencesHelper preferencesHelper) { + preferencesHelper.putString( + BASIC_AUTH_KEY, + new Gson().toJson(new BasicAuthenticator(username, password)) + ); + } + + public BasicAuthenticator getBasicAuthentication( + PreferencesHelper preferencesHelper) { + return new Gson().fromJson( + preferencesHelper.getString(BASIC_AUTH_KEY, null), + BasicAuthenticator.class + ); + } +} diff --git a/app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt b/app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt new file mode 100644 index 0000000..35f0382 --- /dev/null +++ b/app/src/main/java/org/apache/fineract/couchbase/SynchronizationManager.kt @@ -0,0 +1,112 @@ +package org.apache.fineract.couchbase + +import android.content.Context +import android.util.Log.e +import com.couchbase.lite.* +import org.apache.fineract.injection.ApplicationContext +import org.apache.fineract.utils.Constants.DATABASE_NAME +import javax.inject.Inject + + +/** + * Created by Ahmad Jawid Muhammadi on 6/8/20. + */ + +class SynchronizationManager @Inject constructor( + @ApplicationContext val context: Context +) { + + private val TAG = SynchronizationManager::class.java.simpleName + + @Inject + lateinit var database: CouchbaseDatabase + + fun closeDatabase() { + database.closeDatabaseForUser() + } + + + @Throws(CouchbaseLiteException::class) + fun <T> saveDocument(identifier: String, properties: Map<String, T>) { + val document = MutableDocument(identifier, properties) + try { + e(TAG, "Document: $document") + database.getDatabase().save(document) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun deleteDocument(identifier: String) { + try { + database.getDatabase().purge(identifier) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun <T> updateDocument(identifier: String, properties: Map<String, T>) { + val document = MutableDocument(identifier, properties) + try { + database.getDatabase().save(document) + } catch (e: Exception) { + e.printStackTrace() + } + } + + fun getDocuments(expression: Expression, limit: Int = 50, offset: Int = 0): List<HashMap<String, Any>>? { + val list = arrayListOf<HashMap<String, Any>>() + val query = QueryBuilder + .select(SelectResult.all()) + .from(DataSource.database(database.getDatabase())) + .where(expression) + .orderBy(Ordering.expression(Meta.id)) + .limit(Expression.intValue(limit), + Expression.intValue(offset) + ) + + val resultSet = query.execute() + var result: Result? = resultSet.next() + while (result != null) { + val valueMap = result.getDictionary(DATABASE_NAME) + val item = valueMap?.toMap() + list.add(item as HashMap<String, Any>) + result = resultSet.next() + } + + return list + } + + + fun getDocumentForTest(identifier: String, context: Context): HashMap<String, Any> { + database = CouchbaseDatabase(context) + return getDocumentById(identifier) + } + + fun getDocumentById(identifier: String): HashMap<String, Any> { + val query = QueryBuilder + .select(SelectResult.all()) + .from(DataSource.database(database.getDatabase())) + .where(Meta.id.equalTo(Expression.string(identifier)) + ) + .limit(Expression.intValue(1)) + + val resultSet = query.execute().next() + return resultSet.getDictionary(DATABASE_NAME)?.toMap() as HashMap<String, Any> + } + + + fun clearDatabase() { + val query = QueryBuilder + .select(SelectResult.all()) + .from(DataSource.database(database.getDatabase())) + + val resultSet = query.execute() + var result: Result? = resultSet.next() + while (result != null) { + val valueMap = result.getDictionary(DATABASE_NAME) + TODO("implement code here to delete item") + result = resultSet.next() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/data/models/Group.kt b/app/src/main/java/org/apache/fineract/data/models/Group.kt index 9f8a35f..781b6c0 100644 --- a/app/src/main/java/org/apache/fineract/data/models/Group.kt +++ b/app/src/main/java/org/apache/fineract/data/models/Group.kt @@ -2,6 +2,7 @@ package org.apache.fineract.data.models import android.os.Parcelable import kotlinx.android.parcel.Parcelize +import org.apache.fineract.couchbase.DocumentType import org.apache.fineract.data.models.customer.Address /* @@ -18,12 +19,13 @@ data class Group( var office: String? = null, var assignedEmployee: String? = null, var weekday: Int? = null, - var status: Status? = null, + var status: Status? = Status.PENDING, var address: Address? = null, var createdOn: String? = null, var createdBy: String? = null, var lastModifiedBy: String? = null, - var lastModifiedOn: String? = null) : Parcelable { + var lastModifiedOn: String? = null, + var documentType: String = DocumentType.GROUP.value) : Parcelable { enum class Status { PENDING, diff --git a/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt b/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt index 8fe5dd3..7471ec6 100644 --- a/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt +++ b/app/src/main/java/org/apache/fineract/data/models/customer/Customer.kt @@ -7,38 +7,40 @@ import com.raizlabs.android.dbflow.annotation.* import com.raizlabs.android.dbflow.sql.language.SQLite.select import com.raizlabs.android.dbflow.structure.BaseModel import kotlinx.android.parcel.Parcelize +import org.apache.fineract.couchbase.DocumentType import org.apache.fineract.data.local.database.AppDatabase @Parcelize @Table(database = AppDatabase::class, useBooleanGetterSetters = false) data class Customer( - @PrimaryKey - @Column var identifier: String? = null, - @Column var type: String? = null, - @Column var givenName: String? = null, - @Column var middleName: String? = null, - @Column var surname: String? = null, - @ForeignKey(saveForeignKeyModel = true) - @Column var dateOfBirth: DateOfBirth? = null, - @Column var member: Boolean? = null, - @Column var accountBeneficiary: String? = null, - @Column var referenceCustomer: String? = null, - @Column var assignedOffice: String? = null, - @Column var assignedEmployee: String? = null, - @ForeignKey(saveForeignKeyModel = true) - @Column var address: Address? = null, - var contactDetails: List<ContactDetail>? = null, - @Column var currentState: State? = null, - @Column var createdBy: String? = null, - @Column var createdOn: String? = null, - @Column var lastModifiedBy: String? = null, - @Column var lastModifiedOn: String? = null + @PrimaryKey + @Column var identifier: String? = null, + @Column var type: String? = null, + @Column var givenName: String? = null, + @Column var middleName: String? = null, + @Column var surname: String? = null, + @ForeignKey(saveForeignKeyModel = true) + @Column var dateOfBirth: DateOfBirth? = null, + @Column var member: Boolean? = null, + @Column var accountBeneficiary: String? = null, + @Column var referenceCustomer: String? = null, + @Column var assignedOffice: String? = null, + @Column var assignedEmployee: String? = null, + @ForeignKey(saveForeignKeyModel = true) + @Column var address: Address? = null, + var contactDetails: List<ContactDetail>? = null, + @Column var currentState: State? = State.PENDING, + @Column var createdBy: String? = null, + @Column var createdOn: String? = null, + @Column var lastModifiedBy: String? = null, + @Column var lastModifiedOn: String? = null, + @Column var documentType: String = DocumentType.CUSTOMER.value ) : BaseModel(), Parcelable { var isUpdate: Boolean? = null @OneToMany(methods = arrayOf(OneToMany.Method.ALL), variableName = "contactDetails") - fun getContactDetail() : List<ContactDetail>? { + fun getContactDetail(): List<ContactDetail>? { contactDetails = select() .from(ContactDetail::class.java) .where(ContactDetail_Table.customer_identifier.eq(identifier)) diff --git a/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java b/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java index 95fee4b..445bed3 100755 --- a/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java +++ b/app/src/main/java/org/apache/fineract/data/remote/BaseUrl.java @@ -14,6 +14,7 @@ public class BaseUrl { public static final String API_ENDPOINT = "pilot.kuelap.io"; public static final String PORT = "80"; // "/" in the last of the base url always + public static final String LOCALHOST_URL = "ws://10.0.2.2:4984/fineract-cn/"; public String getName() { return "fineract"; diff --git a/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt b/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt index 75e6ece..0324bf2 100644 --- a/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt +++ b/app/src/main/java/org/apache/fineract/ui/adapters/GroupsAdapter.kt @@ -37,7 +37,9 @@ class GroupsAdapter @Inject constructor(@ApplicationContext var context: Context override fun onBindViewHolder(holder: ViewHolder, position: Int) { var group: Group = groups[position] - StatusUtils.setGroupsStatusIcon(group.status, holder.ivTypeIndicator, context) + group.status?.let { + StatusUtils.setGroupsStatusIcon(group.status, holder.ivTypeIndicator, context) + } holder.tvGroupIdentifier.text = group.identifier diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java index 251a6e1..a09bad3 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/customeractivity/CreateCustomerPresenter.java @@ -1,87 +1,64 @@ package org.apache.fineract.ui.online.customers.createcustomer.customeractivity; import android.content.Context; +import android.util.Log; -import org.apache.fineract.R; -import org.apache.fineract.data.datamanager.contracts.ManagerCustomer; -import org.apache.fineract.data.datamanager.database.DbManagerCustomer; +import com.couchbase.lite.CouchbaseLiteException; + +import org.apache.fineract.couchbase.SynchronizationManager; +import org.apache.fineract.data.local.PreferencesHelper; import org.apache.fineract.data.models.customer.Customer; import org.apache.fineract.injection.ApplicationContext; import org.apache.fineract.injection.ConfigPersistent; import org.apache.fineract.ui.base.BasePresenter; +import org.apache.fineract.utils.DateUtils; +import org.apache.fineract.utils.GsonUtilsKt; -import javax.inject.Inject; +import java.util.Objects; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.observers.DisposableCompletableObserver; -import io.reactivex.schedulers.Schedulers; +import javax.inject.Inject; /** * @author Rajan Maurya - * On 27/07/17. + * On 27/07/17. */ @ConfigPersistent public class CreateCustomerPresenter extends BasePresenter<CreateCustomerContract.View> implements CreateCustomerContract.Presenter { - private ManagerCustomer dbManagerCustomer; - private final CompositeDisposable compositeDisposable; + private SynchronizationManager synchronizationManager; + private PreferencesHelper preferencesHelper; @Inject public CreateCustomerPresenter(@ApplicationContext Context context, - DbManagerCustomer dataManagerCustomer) { + SynchronizationManager synchronizationManager, + PreferencesHelper preferencesHelper) { super(context); - this.dbManagerCustomer = dataManagerCustomer; - compositeDisposable = new CompositeDisposable(); + this.synchronizationManager = synchronizationManager; + this.preferencesHelper = preferencesHelper; } @Override - public void createCustomer(final Customer customer) { - checkViewAttached(); - getMvpView().showProgressbar(); - compositeDisposable.add(dbManagerCustomer.createCustomer(customer) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableCompletableObserver() { - @Override - public void onComplete() { - getMvpView().hideProgressbar(); - getMvpView().customerCreatedSuccessfully(); - } - - @Override - public void onError(Throwable throwable) { - getMvpView().hideProgressbar(); - showExceptionError(throwable, - context.getString(R.string.error_creating_customer)); - } - }) - - ); + public void createCustomer(Customer customer) { + try { + customer.setCreatedBy(preferencesHelper.getUserName()); + customer.setCreatedOn(DateUtils.getCurrentDate()); + customer.setLastModifiedBy(preferencesHelper.getUserName()); + customer.setLastModifiedOn(DateUtils.getCurrentDate()); + synchronizationManager.saveDocument(Objects.requireNonNull(customer.getIdentifier()), + GsonUtilsKt.serializeToMap(customer)); + } catch (CouchbaseLiteException e) { + Log.e("CreateCustomerPresenter", e.toString()); + } + getMvpView().customerCreatedSuccessfully(); } @Override public void updateCustomer(String customerIdentifier, Customer customer) { - checkViewAttached(); - getMvpView().showProgressbar(); - compositeDisposable.add(dbManagerCustomer.updateCustomer(customerIdentifier, customer) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableCompletableObserver() { - @Override - public void onComplete() { - getMvpView().hideProgressbar(); - getMvpView().customerUpdatedSuccessfully(); - } - - @Override - public void onError(Throwable throwable) { - getMvpView().hideProgressbar(); - showExceptionError(throwable, - context.getString(R.string.error_updating_customer)); - } - }) - ); + customer.setLastModifiedBy(preferencesHelper.getUserName()); + customer.setLastModifiedOn(DateUtils.getCurrentDate()); + synchronizationManager.updateDocument(Objects.requireNonNull(customer.getIdentifier()), + GsonUtilsKt.serializeToMap(customer)); + getMvpView().customerUpdatedSuccessfully(); } } diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java index f3ba64c..b4c446f 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/createcustomer/formcustomeraddress/FormCustomerAddressFragment.java @@ -134,16 +134,18 @@ public class FormCustomerAddressFragment extends FineractBaseFragment implements public void showPreviousAddress() { Address address = customer.getAddress(); - etStreet.setText(address.getStreet()); - etCity.setText(address.getCity()); - if (address.getPostalCode() != null) { - etPostalCode.setText(address.getPostalCode()); + if (address != null) { + etStreet.setText(address.getStreet()); + etCity.setText(address.getCity()); + if (address.getPostalCode() != null) { + etPostalCode.setText(address.getPostalCode()); + } + etCountry.setText(address.getCountry()); + if (address.getRegion() != null) { + etRegion.setText(address.getRegion()); + } } - etCountry.setText(address.getCountry()); showTextInputLayoutError(tilCountry, null); - if (address.getRegion() != null) { - etRegion.setText(address.getRegion()); - } } @Override diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java index b34f58e..64cdfe0 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsFragment.java @@ -2,12 +2,6 @@ package org.apache.fineract.ui.online.customers.customerdetails; import android.content.Intent; import android.os.Bundle; -import androidx.annotation.Nullable; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.CollapsingToolbarLayout; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -15,6 +9,14 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.coordinatorlayout.widget.CoordinatorLayout; + +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.CollapsingToolbarLayout; + import org.apache.fineract.R; import org.apache.fineract.data.models.customer.Address; import org.apache.fineract.data.models.customer.ContactDetail; @@ -44,7 +46,7 @@ import butterknife.OnClick; /** * @author Rajan Maurya - * On 26/06/17. + * On 26/06/17. */ public class CustomerDetailsFragment extends FineractBaseFragment implements AppBarLayout.OnOffsetChangedListener, CustomerDetailsContract.View, @@ -133,7 +135,7 @@ public class CustomerDetailsFragment extends FineractBaseFragment @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.fragment_customer_details, container, false); ((FineractBaseActivity) getActivity()).getActivityComponent().inject(this); ButterKnife.bind(this, rootView); @@ -181,6 +183,7 @@ public class CustomerDetailsFragment extends FineractBaseFragment tasksBottomSheet.setCustomerIdentifier(customerIdentifier); tasksBottomSheet.setCustomerTasksChangeListener(this); tasksBottomSheet.show(getChildFragmentManager(), getString(R.string.tasks)); + tasksBottomSheet.setCustomer(customer); } @OnClick(R.id.ll_identifier_cards) @@ -246,14 +249,16 @@ public class CustomerDetailsFragment extends FineractBaseFragment Address address = customer.getAddress(); StringBuilder addressBuilder = new StringBuilder(); - addressBuilder - .append(address.getStreet()).append(", ") - .append(address.getCity()).append(", "); - if (address.getPostalCode() != null) { + if (address != null) { + addressBuilder.append(address.getStreet()).append(", ") + .append(address.getCity()).append(", "); + addressBuilder.append(address.getPostalCode()); addressBuilder.append(", "); + + addressBuilder.append(address.getCountry()); } - addressBuilder.append(address.getCountry()); + tvAddress.setText(addressBuilder); if (customer.getContactDetails().size() == 0) { diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java index 97072ae..6047187 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerdetails/CustomerDetailsPresenter.java @@ -2,38 +2,32 @@ package org.apache.fineract.ui.online.customers.customerdetails; import android.content.Context; -import org.apache.fineract.R; -import org.apache.fineract.data.datamanager.contracts.ManagerCustomer; -import org.apache.fineract.data.datamanager.database.DbManagerCustomer; +import org.apache.fineract.couchbase.SynchronizationManager; import org.apache.fineract.data.models.customer.Customer; import org.apache.fineract.injection.ApplicationContext; import org.apache.fineract.injection.ConfigPersistent; import org.apache.fineract.ui.base.BasePresenter; +import org.apache.fineract.utils.GsonUtilsKt; -import javax.inject.Inject; +import java.util.HashMap; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.observers.DisposableObserver; -import io.reactivex.schedulers.Schedulers; +import javax.inject.Inject; /** * @author Rajan Maurya - * On 26/06/17. + * On 26/06/17. */ @ConfigPersistent public class CustomerDetailsPresenter extends BasePresenter<CustomerDetailsContract.View> implements CustomerDetailsContract.Presenter { - private ManagerCustomer dataManagerCustomer; - private final CompositeDisposable compositeDisposable; + private SynchronizationManager synchronizationManager; @Inject public CustomerDetailsPresenter(@ApplicationContext Context context, - DbManagerCustomer dataManager) { + SynchronizationManager synchronizationManager) { super(context); - dataManagerCustomer = dataManager; - compositeDisposable = new CompositeDisposable(); + this.synchronizationManager = synchronizationManager; } @Override @@ -44,35 +38,15 @@ public class CustomerDetailsPresenter extends BasePresenter<CustomerDetailsContr @Override public void detachView() { super.detachView(); - compositeDisposable.clear(); } @Override public void loanCustomerDetails(String identifier) { checkViewAttached(); getMvpView().showProgressbar(); - compositeDisposable.add(dataManagerCustomer.fetchCustomer(identifier) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableObserver<Customer>() { - @Override - public void onNext(Customer customer) { - getMvpView().hideProgressbar(); - getMvpView().showCustomerDetails(customer); - } - - @Override - public void onError(Throwable throwable) { - getMvpView().hideProgressbar(); - showExceptionError(throwable, - context.getString(R.string.error_fetching_customer_details)); - } - - @Override - public void onComplete() { - - } - }) - ); + HashMap<String, Object> item = synchronizationManager.getDocumentById(identifier); + Customer customer = GsonUtilsKt.convertToData(item, Customer.class); + getMvpView().showCustomerDetails(customer); + getMvpView().hideProgressbar(); } } diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java index b4d80d1..32a9938 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersFragment.java @@ -1,18 +1,9 @@ package org.apache.fineract.ui.online.customers.customerlist; -import static android.app.Activity.RESULT_OK; - import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import androidx.transition.TransitionManager; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.appcompat.widget.SearchView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; @@ -23,6 +14,14 @@ import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.SearchView; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import androidx.transition.TransitionManager; + import com.github.therajanmaurya.sweeterror.SweetUIErrorHandler; import org.apache.fineract.R; @@ -48,6 +47,8 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import static android.app.Activity.RESULT_OK; + /** * @author Rajan Maurya * On 20/06/17. @@ -123,18 +124,19 @@ public class CustomersFragment extends FineractBaseFragment implements Customers showUserInterface(); sweetUIErrorHandler = new SweetUIErrorHandler(getActivity(), rootView); - if (preferencesHelper.isFetching()) { - sweetUIErrorHandler.showSweetCustomErrorUI(getString(R.string.syncing_please_wait), - R.drawable.ic_error_black_24dp, swipeRefreshLayout, layoutError); - } else { - customerPresenter.fetchCustomers(0, false); - } + return rootView; } @Override public void onResume() { super.onResume(); + if (preferencesHelper.isFetching()) { + sweetUIErrorHandler.showSweetCustomErrorUI(getString(R.string.syncing_please_wait), + R.drawable.ic_error_black_24dp, swipeRefreshLayout, layoutError); + } else { + customerPresenter.fetchCustomers(0, false); + } if (isNewCustomer) { isNewCustomer = false; customerPresenter.fetchCustomers(0, false); @@ -192,8 +194,7 @@ public class CustomersFragment extends FineractBaseFragment implements Customers @Override public void showEmptyCustomers(String message) { showRecyclerView(false); - sweetUIErrorHandler.showSweetCustomErrorUI(getString(R.string.customer), - getString(Integer.parseInt(message)), + sweetUIErrorHandler.showSweetCustomErrorUI(message, R.drawable.ic_customer_black_24dp, rvCustomers, layoutError); } diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java index 398b615..afdf1b1 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customerlist/CustomersPresenter.java @@ -2,24 +2,23 @@ package org.apache.fineract.ui.online.customers.customerlist; import android.content.Context; +import com.couchbase.lite.Expression; + import org.apache.fineract.R; -import org.apache.fineract.data.datamanager.contracts.ManagerCustomer; -import org.apache.fineract.data.datamanager.database.DbManagerCustomer; +import org.apache.fineract.couchbase.DocumentType; +import org.apache.fineract.couchbase.SynchronizationManager; import org.apache.fineract.data.models.customer.Customer; -import org.apache.fineract.data.models.customer.CustomerPage; import org.apache.fineract.injection.ApplicationContext; import org.apache.fineract.injection.ConfigPersistent; import org.apache.fineract.ui.base.BasePresenter; +import org.apache.fineract.utils.GsonUtilsKt; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import javax.inject.Inject; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.observers.DisposableObserver; -import io.reactivex.schedulers.Schedulers; - /** * @author Rajan Maurya * On 20/06/17. @@ -28,18 +27,16 @@ import io.reactivex.schedulers.Schedulers; public class CustomersPresenter extends BasePresenter<CustomersContract.View> implements CustomersContract.Presenter { - private final ManagerCustomer dataManagerCustomer; - private CompositeDisposable compositeDisposable; - private int customerListSize = 50; private boolean loadmore = false; + private SynchronizationManager synchronizationManager; + @Inject public CustomersPresenter(@ApplicationContext Context context, - DbManagerCustomer dataManager) { + SynchronizationManager synchronizationManager) { super(context); - dataManagerCustomer = dataManager; - compositeDisposable = new CompositeDisposable(); + this.synchronizationManager = synchronizationManager; } @Override @@ -50,7 +47,6 @@ public class CustomersPresenter extends BasePresenter<CustomersContract.View> @Override public void detachView() { super.detachView(); - compositeDisposable.clear(); } @Override @@ -63,44 +59,34 @@ public class CustomersPresenter extends BasePresenter<CustomersContract.View> public void fetchCustomers(Integer pageIndex, Integer size) { checkViewAttached(); getMvpView().showProgressbar(); - compositeDisposable.add(dataManagerCustomer.fetchCustomers(pageIndex, size) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableObserver<CustomerPage>() { - @Override - public void onNext(CustomerPage customerPage) { - getMvpView().hideProgressbar(); - - if (!loadmore && customerPage.getTotalPages() == 0) { - getMvpView().showEmptyCustomers( - context.getString(R.string.empty_customer_list)); - } else if (loadmore && customerPage.getCustomers().size() == 0) { - getMvpView().showMessage( - context.getString(R.string.no_more_customer_available)); - } else { - showCustomers(customerPage.getCustomers()); - } - } - - @Override - public void onError(Throwable throwable) { - getMvpView().hideProgressbar(); - if (loadmore) { - getMvpView().showMessage( - context.getString(R.string.error_loading_customers)); - } else { - showExceptionError(throwable, - context.getString(R.string.error_loading_customers)); - } - } - - @Override - public void onComplete() { - } - }) - ); + ArrayList<Customer> customerList = new ArrayList<>(); + + Expression expression = Expression.property("documentType") + .equalTo(Expression.string(DocumentType.CUSTOMER.getValue())); + + List<HashMap<String, Object>> map = synchronizationManager.getDocuments( + expression, + size, + pageIndex); + + for (HashMap<String, Object> item : map) { + Customer customer = GsonUtilsKt.convertToData(item, Customer.class); + customerList.add(customer); + } + + if (!loadmore && customerList.size() == 0) { + getMvpView().showEmptyCustomers( + context.getString(R.string.empty_customer_list)); + } else if (loadmore && customerList.size() == 0) { + getMvpView().showMessage( + context.getString(R.string.no_more_customer_available)); + } else { + showCustomers(customerList); + } + getMvpView().hideProgressbar(); } + @Override public void showCustomers(List<Customer> customers) { if (loadmore) { @@ -112,29 +98,29 @@ public class CustomersPresenter extends BasePresenter<CustomersContract.View> @Override public void searchCustomerOnline(String query) { - checkViewAttached(); - getMvpView().showProgressbar(); - compositeDisposable.add( - dataManagerCustomer.fetchCustomer(query) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableObserver<Customer>() { - @Override - public void onNext(Customer value) { - getMvpView().hideProgressbar(); - getMvpView().searchCustomerList(value); - } - - @Override - public void onError(Throwable e) { - showExceptionError(e, - context.getString(R.string.error_finding_customer)); - } - - @Override - public void onComplete() { - - } - })); +// checkViewAttached(); +// getMvpView().showProgressbar(); +// compositeDisposable.add( +// dataManagerCustomer.fetchCustomer(query) +// .subscribeOn(Schedulers.io()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribeWith(new DisposableObserver<Customer>() { +// @Override +// public void onNext(Customer value) { +// getMvpView().hideProgressbar(); +// getMvpView().searchCustomerList(value); +// } +// +// @Override +// public void onError(Throwable e) { +// showExceptionError(e, +// context.getString(R.string.error_finding_customer)); +// } +// +// @Override +// public void onComplete() { +// +// } +// })); } } diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java index 9d52907..7e79fe6 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetContract.java @@ -1,11 +1,12 @@ package org.apache.fineract.ui.online.customers.customertasks; import org.apache.fineract.data.models.customer.Command; +import org.apache.fineract.data.models.customer.Customer; import org.apache.fineract.ui.base.MvpView; /** * @author Rajan Maurya - * On 27/07/17. + * On 27/07/17. */ public interface CustomerTasksBottomSheetContract { @@ -21,6 +22,6 @@ public interface CustomerTasksBottomSheetContract { interface Presenter { - void changeCustomerStatus(String identifier, Command command); + void changeCustomerStatus(String identifier, Customer customer, Command command); } } diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java index 65e99d7..c09ca25 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetFragment.java @@ -3,10 +3,6 @@ package org.apache.fineract.ui.online.customers.customertasks; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; -import androidx.annotation.NonNull; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.bottomsheet.BottomSheetDialog; -import androidx.core.content.ContextCompat; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -15,6 +11,12 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.bottomsheet.BottomSheetDialog; + import org.apache.fineract.R; import org.apache.fineract.data.models.customer.Command; import org.apache.fineract.data.models.customer.Customer; @@ -30,7 +32,7 @@ import butterknife.OnClick; /** * @author Rajan Maurya - * On 27/07/17. + * On 27/07/17. */ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDialogFragment implements CustomerTasksBottomSheetContract.View { @@ -71,6 +73,7 @@ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDia @Inject CustomerTasksBottomSheetPresenter tasksBottomSheetPresenter; + View rootView; private BottomSheetBehavior behavior; @@ -78,6 +81,7 @@ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDia private Command command; private String customerIdentifier; private OnTasksChangeListener onTasksChangeListener; + private Customer customer; @NonNull @Override @@ -188,11 +192,15 @@ public class CustomerTasksBottomSheetFragment extends FineractBaseBottomSheetDia customerIdentifier = identifier; } + public void setCustomer(Customer customer) { + this.customer = customer; + } + @OnClick(R.id.btn_submit_task) void submitTask() { command.setComment(etComment.getText().toString().trim()); etComment.setEnabled(false); - tasksBottomSheetPresenter.changeCustomerStatus(customerIdentifier, command); + tasksBottomSheetPresenter.changeCustomerStatus(customerIdentifier, customer, command); } @OnClick(R.id.btn_cancel) diff --git a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java index 5d3f6a7..e8cdece 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java +++ b/app/src/main/java/org/apache/fineract/ui/online/customers/customertasks/CustomerTasksBottomSheetPresenter.java @@ -1,39 +1,38 @@ package org.apache.fineract.ui.online.customers.customertasks; import android.content.Context; +import android.util.Log; import org.apache.fineract.R; -import org.apache.fineract.data.datamanager.api.DataManagerCustomer; +import org.apache.fineract.couchbase.SynchronizationManager; import org.apache.fineract.data.models.customer.Command; +import org.apache.fineract.data.models.customer.Customer; import org.apache.fineract.injection.ApplicationContext; import org.apache.fineract.injection.ConfigPersistent; import org.apache.fineract.ui.base.BasePresenter; +import org.apache.fineract.utils.GsonUtilsKt; -import javax.inject.Inject; +import java.util.Objects; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.observers.DisposableCompletableObserver; -import io.reactivex.schedulers.Schedulers; +import javax.inject.Inject; /** * @author Rajan Maurya - * On 28/07/17. + * On 28/07/17. */ @ConfigPersistent public class CustomerTasksBottomSheetPresenter extends BasePresenter<CustomerTasksBottomSheetContract.View> implements CustomerTasksBottomSheetContract.Presenter { - private DataManagerCustomer dataManagerCustomer; - private final CompositeDisposable compositeDisposable; + private SynchronizationManager synchronizationManager; @Inject public CustomerTasksBottomSheetPresenter(@ApplicationContext Context context, - DataManagerCustomer dataManagerCustomer) { + SynchronizationManager synchronizationManager) { super(context); - this.dataManagerCustomer = dataManagerCustomer; - compositeDisposable = new CompositeDisposable(); + + this.synchronizationManager = synchronizationManager; } @Override @@ -44,30 +43,37 @@ public class CustomerTasksBottomSheetPresenter @Override public void detachView() { super.detachView(); - compositeDisposable.clear(); + synchronizationManager.closeDatabase(); } @Override - public void changeCustomerStatus(String identifier, Command command) { + public void changeCustomerStatus(String identifier, Customer customer, Command command) { checkViewAttached(); getMvpView().showProgressbar(); - compositeDisposable.add(dataManagerCustomer.customerCommand(identifier, command) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(new DisposableCompletableObserver() { - @Override - public void onComplete() { - getMvpView().hideProgressbar(); - getMvpView().statusChangedSuccessfully(); - } - - @Override - public void onError(Throwable throwable) { - getMvpView().hideProgressbar(); - showExceptionError(throwable, - context.getString(R.string.error_updating_status)); - } - }) - ); + try { + switch (Objects.requireNonNull(command.getAction())) { + case LOCK: + customer.setCurrentState(Customer.State.LOCKED); + break; + case REOPEN: + customer.setCurrentState(Customer.State.PENDING); + break; + case ACTIVATE: + case UNLOCK: + customer.setCurrentState(Customer.State.ACTIVE); + break; + case CLOSE: + customer.setCurrentState(Customer.State.CLOSED); + break; + } + synchronizationManager.updateDocument(identifier, GsonUtilsKt.serializeToMap(customer)); + getMvpView().hideProgressbar(); + getMvpView().statusChangedSuccessfully(); + } catch (Exception e) { + getMvpView().hideProgressbar(); + showExceptionError(e, + context.getString(R.string.error_updating_status)); + Log.e("CustomerTasks", e.toString()); + } } } diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt index 00e5a8a..871f53f 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt +++ b/app/src/main/java/org/apache/fineract/ui/online/groups/creategroup/CreateGroupActivity.kt @@ -15,8 +15,8 @@ import org.apache.fineract.data.models.customer.Address import org.apache.fineract.ui.base.FineractBaseActivity import org.apache.fineract.ui.base.Toaster import org.apache.fineract.ui.online.groups.GroupAction -import org.apache.fineract.ui.online.groups.grouplist.GroupViewModelFactory import org.apache.fineract.ui.online.groups.grouplist.GroupViewModel +import org.apache.fineract.ui.online.groups.grouplist.GroupViewModelFactory import org.apache.fineract.utils.Constants import org.apache.fineract.utils.DateUtils import javax.inject.Inject @@ -85,7 +85,7 @@ class CreateGroupActivity : FineractBaseActivity(), StepperLayout.StepperListene Status.DONE -> { hideMifosProgressDialog() if (groupAction == GroupAction.CREATE) { - Toast.makeText(this, getString(R.string.group_identifier_updated_successfully, group.identifier), Toast.LENGTH_SHORT).show() + Toast.makeText(this, getString(R.string.group_identifier_created_successfully, group.identifier), Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, getString(R.string.group_identifier_updated_successfully, group.identifier), Toast.LENGTH_SHORT).show() } @@ -98,7 +98,7 @@ class CreateGroupActivity : FineractBaseActivity(), StepperLayout.StepperListene override fun onCompleted(completeButton: View?) { when (groupAction) { GroupAction.EDIT -> group.identifier?.let { - viewModel.updateGroup(it, group) + viewModel.updateGroup(group) } GroupAction.CREATE -> viewModel.createGroup(group) } diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt index 448554b..fad1dfd 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt +++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupListFragment.kt @@ -30,6 +30,7 @@ import javax.inject.Inject * Created by saksham on 15/June/2019 */ + class GroupListFragment : FineractBaseFragment(), OnItemClickListener { lateinit var rootView: View @@ -42,6 +43,7 @@ class GroupListFragment : FineractBaseFragment(), OnItemClickListener { @Inject lateinit var groupViewModelFactory: GroupViewModelFactory + lateinit var groupList: ArrayList<Group> val searchedGroup: (ArrayList<Group>) -> Unit = { groups -> @@ -63,15 +65,11 @@ class GroupListFragment : FineractBaseFragment(), OnItemClickListener { savedInstanceState: Bundle?): View? { rootView = inflater.inflate(R.layout.fragment_group_list, container, false) (activity as FineractBaseActivity).activityComponent.inject(this) - viewModel = ViewModelProviders.of(this, - groupViewModelFactory).get(GroupViewModel::class.java) + viewModel = ViewModelProviders.of(this, groupViewModelFactory) + .get(GroupViewModel::class.java) return rootView } - override fun onActivityCreated(savedInstanceState: Bundle?) { - super.onActivityCreated(savedInstanceState) - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ButterKnife.bind(this, rootView) @@ -79,12 +77,16 @@ class GroupListFragment : FineractBaseFragment(), OnItemClickListener { rvGroups.adapter = adapter rvGroups.layoutManager = LinearLayoutManager(context) - viewModel.getGroups().observe(this, + } - Observer { - groupList = it - adapter.setGroupList(it) - }) + override fun onStart() { + super.onStart() + viewModel.getGroups()?.observe(this, Observer { + it?.let { + groupList = it + adapter.setGroupList(it) + } + }) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt index 6f9f159..8618704 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt +++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModel.kt @@ -4,43 +4,58 @@ import android.annotation.SuppressLint import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.couchbase.lite.Expression import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.functions.Predicate import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.* +import org.apache.fineract.couchbase.DocumentType +import org.apache.fineract.couchbase.SynchronizationManager import org.apache.fineract.data.Status import org.apache.fineract.data.datamanager.api.DataManagerAnonymous -import org.apache.fineract.data.datamanager.api.DataManagerGroups +import org.apache.fineract.data.local.PreferencesHelper import org.apache.fineract.data.models.Group import org.apache.fineract.data.models.customer.Command import org.apache.fineract.data.models.customer.Country +import org.apache.fineract.utils.DateUtils +import org.apache.fineract.utils.serializeToMap +import org.apache.fineract.utils.toDataClass /* * Created by saksham on 15/June/2019 */ -class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val dataManagerAnonymous: DataManagerAnonymous) : ViewModel() { +class GroupViewModel constructor(private val synchronizationManager: SynchronizationManager, + private val dataManagerAnonymous: DataManagerAnonymous, + private val preferencesHelper: PreferencesHelper) : ViewModel() { - lateinit var groupsList: MutableLiveData<ArrayList<Group>> + var groupsList = MutableLiveData<ArrayList<Group>>() private var viewModelJob = Job() + // Create a Coroutine scope using a job to be able to cancel when needed private val uiScope = CoroutineScope(viewModelJob + Dispatchers.IO) private var _status = MutableLiveData<Status>() val status: LiveData<Status> get() = _status - fun getGroups(): MutableLiveData<ArrayList<Group>> { - groupsList = dataManagerGroups.getGroups() + fun getGroups(): MutableLiveData<ArrayList<Group>>? { + val expression = Expression.property("documentType") + .equalTo(Expression.string(DocumentType.GROUP.value)) + .and(Expression.property("status").notEqualTo(Expression.string("null"))) + val hashMapList = synchronizationManager.getDocuments(expression) + if (hashMapList?.isEmpty() == null) { + return null + } + val list = arrayListOf<Group>() + for (map in hashMapList) { + list.add(map.toDataClass()) + } + groupsList.value = list return groupsList } fun searchGroup(groups: ArrayList<Group>, query: String, searchedGroup: (ArrayList<Group>) -> Unit) { - searchedGroup(ArrayList(Observable.fromIterable(groups).filter(object : Predicate<Group> { - override fun test(group: Group): Boolean { - return group.identifier?.toLowerCase()?.contains(query.toLowerCase()).toString().toBoolean() - } - }).toList().blockingGet())) + searchedGroup(ArrayList(Observable.fromIterable(groups).filter { group -> group.identifier?.toLowerCase()?.contains(query.toLowerCase()).toString().toBoolean() }.toList().blockingGet())) } @SuppressLint("CheckResult") @@ -59,7 +74,11 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d withContext(Dispatchers.Main) { try { _status.value = Status.LOADING - dataManagerGroups.createGroup(group).await() + group.createdBy = preferencesHelper.userName + group.createdOn = DateUtils.getCurrentDate() + group.lastModifiedBy = preferencesHelper.userName + group.lastModifiedOn = DateUtils.getCurrentDate() + synchronizationManager.saveDocument(group.identifier!!, group.serializeToMap()) _status.value = Status.DONE } catch (e: Exception) { _status.value = Status.ERROR @@ -68,12 +87,12 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d } } - fun updateGroup(identifier: String, group: Group) { + fun updateGroup(group: Group) { uiScope.launch { withContext(Dispatchers.Main) { try { _status.value = Status.LOADING - dataManagerGroups.updateGroup(identifier, group).await() + synchronizationManager.updateDocument(group.identifier!!, group.serializeToMap()) _status.value = Status.DONE } catch (e: Exception) { _status.value = Status.ERROR @@ -82,12 +101,20 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d } } - fun changeGroupStatus(identifier: String, command: Command) { + fun changeGroupStatus(identifier: String, group: Group, command: Command) { uiScope.launch { withContext(Dispatchers.Main) { try { _status.value = Status.LOADING - dataManagerGroups.changeGroupStatus(identifier, command).await() + when (command.action) { + Command.Action.ACTIVATE -> group.status = Group.Status.ACTIVE + Command.Action.REOPEN -> group.status = Group.Status.PENDING + Command.Action.CLOSE -> group.status = Group.Status.CLOSED + Command.Action.LOCK -> group.status = Group.Status.ACTIVE + else -> group.status = Group.Status.PENDING + } + synchronizationManager.updateDocument(identifier, group.serializeToMap()) + // dataManagerGroups.changeGroupStatus(identifier, command).await() _status.value = Status.DONE } catch (e: Exception) { _status.value = Status.ERROR @@ -102,7 +129,7 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d fun getCountryCode(countries: List<Country>, countryName: String): String? { for (country in countries) { - if (country.name.equals(countryName)) { + if (country.name == countryName) { return country.alphaCode } } @@ -111,7 +138,7 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d fun isCountryValid(countries: List<Country>, countryName: String): Boolean { for (country in countries) { - if (country.name.equals(countryName)) { + if (country.name == countryName) { return true } } @@ -125,6 +152,7 @@ class GroupViewModel constructor(val dataManagerGroups: DataManagerGroups, val d override fun onCleared() { super.onCleared() viewModelJob.cancel() + synchronizationManager.closeDatabase() } } \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt index f541a37..0d713f9 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt +++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouplist/GroupViewModelFactory.kt @@ -3,8 +3,9 @@ package org.apache.fineract.ui.online.groups.grouplist import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import org.apache.fineract.couchbase.SynchronizationManager import org.apache.fineract.data.datamanager.api.DataManagerAnonymous -import org.apache.fineract.data.datamanager.api.DataManagerGroups +import org.apache.fineract.data.local.PreferencesHelper import org.apache.fineract.injection.ApplicationContext import javax.inject.Inject @@ -15,11 +16,13 @@ import javax.inject.Inject class GroupViewModelFactory @Inject constructor(@ApplicationContext var context: Context, - private val dataManagerGroups: DataManagerGroups, - private val dataManagerAnonymous: DataManagerAnonymous) + private val synchronizationManager: SynchronizationManager, + private val dataManagerAnonymous: DataManagerAnonymous, + private val preferencesHelper: PreferencesHelper) : ViewModelProvider.NewInstanceFactory() { + @Suppress("UNCHECKED_CAST") override fun <T : ViewModel?> create(modelClass: Class<T>): T { - return GroupViewModel(dataManagerGroups, dataManagerAnonymous) as T + return GroupViewModel(synchronizationManager, dataManagerAnonymous, preferencesHelper) as T } } \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt b/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt index 76c601d..06893f4 100644 --- a/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt +++ b/app/src/main/java/org/apache/fineract/ui/online/groups/grouptasks/GroupTasksBottomSheetFragment.kt @@ -123,7 +123,7 @@ class GroupTasksBottomSheetFragment(val group: Group) : FineractBaseBottomSheetD command.comment = rootView.et_comment.text.toString().trim { it <= ' ' } rootView.et_comment.isEnabled = false group.identifier?.let { - viewModel.changeGroupStatus(it, command) + viewModel.changeGroupStatus(it, group, command) } } @@ -134,7 +134,7 @@ class GroupTasksBottomSheetFragment(val group: Group) : FineractBaseBottomSheetD override fun onStart() { super.onStart() - behavior?.state = BottomSheetBehavior.STATE_EXPANDED + behavior.state = BottomSheetBehavior.STATE_EXPANDED } override fun onDestroy() { diff --git a/app/src/main/java/org/apache/fineract/utils/Constants.kt b/app/src/main/java/org/apache/fineract/utils/Constants.kt index 6d9dca5..072c90f 100644 --- a/app/src/main/java/org/apache/fineract/utils/Constants.kt +++ b/app/src/main/java/org/apache/fineract/utils/Constants.kt @@ -8,4 +8,8 @@ package org.apache.fineract.utils object Constants { const val GROUP = "group" const val GROUP_ACTION = "group_action" + const val DATABASE_NAME = "fineract-cn-mobile" + const val BASIC_AUTH_KEY = "basic_auth_key" + const val GATEWAY_USER_NAME = "fineract-cn" + const val GATEWAY_PASSWORD = "password" } \ No newline at end of file diff --git a/app/src/main/java/org/apache/fineract/utils/DateUtils.java b/app/src/main/java/org/apache/fineract/utils/DateUtils.java index a582653..d9f8c25 100644 --- a/app/src/main/java/org/apache/fineract/utils/DateUtils.java +++ b/app/src/main/java/org/apache/fineract/utils/DateUtils.java @@ -61,7 +61,8 @@ public class DateUtils { SimpleDateFormat dateFormat = new SimpleDateFormat(inputFormat, Locale.ENGLISH); Date date = new Date(); try { - date = dateFormat.parse(dateString); + if (dateString != null) + date = dateFormat.parse(dateString); } catch (ParseException e) { Log.d(LOG_TAG, e.getLocalizedMessage()); } diff --git a/app/src/main/java/org/apache/fineract/utils/GsonUtils.kt b/app/src/main/java/org/apache/fineract/utils/GsonUtils.kt new file mode 100644 index 0000000..449ae0e --- /dev/null +++ b/app/src/main/java/org/apache/fineract/utils/GsonUtils.kt @@ -0,0 +1,31 @@ +package org.apache.fineract.utils + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken + +/** + * Created by Ahmad Jawid Muhammadi on 7/8/20. + */ + +//convert a data class to a map + +fun <T> T.serializeToMap(): Map<String, Any> { + return convert() +} + +//convert a map to a data class +inline fun <reified T> Map<String, Any>.toDataClass(): T { + return convert() +} + +//convert an object of type I to type O +inline fun <I, reified O> I.convert(): O { + val json = Gson().toJson(this) + return Gson().fromJson(json, object : TypeToken<O>() {}.type) +} + +fun <O> Map<String, Any>.convertToData(type: Class<O>): O { + val json = Gson().toJson(this) + return Gson().fromJson(json, TypeToken.getParameterized(type).type) +} + diff --git a/app/src/main/resources/groups.json b/app/src/main/resources/groups.json index 086d27f..85a7b12 100644 --- a/app/src/main/resources/groups.json +++ b/app/src/main/resources/groups.json @@ -27,7 +27,8 @@ "createdOn": "2018-06-26T15:09:36.672Z", "createdBy": "ranefer", "lastModifiedOn": "lastModifiedOn", - "lastModifiedBy": "lastModifiedBy" + "lastModifiedBy": "lastModifiedBy", + "type": "group" }, { "identifier": "group002", diff --git a/app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt b/app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt deleted file mode 100644 index 031a215..0000000 --- a/app/src/test/java/org/apache/fineract/online/CustomerPresenterTest.kt +++ /dev/null @@ -1,97 +0,0 @@ -package org.apache.fineract.online - -import android.content.Context -import io.reactivex.Observable -import org.apache.fineract.FakeRemoteDataSource -import org.apache.fineract.R -import org.apache.fineract.data.datamanager.contracts.ManagerCustomer -import org.apache.fineract.data.datamanager.database.DbManagerCustomer -import org.apache.fineract.data.models.customer.CustomerPage -import org.apache.fineract.exceptions.NoConnectivityException -import org.apache.fineract.ui.online.customers.customerlist.CustomersContract -import org.apache.fineract.ui.online.customers.customerlist.CustomersPresenter -import org.apache.fineract.util.RxSchedulersOverrideRule -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.`when` -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnitRunner - -@RunWith(MockitoJUnitRunner::class) -class CustomerPresenterTest { - - @Rule - @JvmField - val overrideSchedulersRule = RxSchedulersOverrideRule() - - @Mock - lateinit var mockedView: CustomersContract.View - - @Mock - lateinit var mockedCustomerDataManager: DbManagerCustomer - - lateinit var mockedManagerCustomer: ManagerCustomer - - @Mock - lateinit var mockedContext: Context - - lateinit var customerPresenter: CustomersPresenter - - lateinit var customerPage: CustomerPage - - val size = 10 - - val pageIndex: Int = 1 - - @Before - fun setup() { - customerPresenter = CustomersPresenter(mockedContext, mockedCustomerDataManager) - customerPresenter.attachView(mockedView) - customerPage = FakeRemoteDataSource.getCustomerPage() - mockedManagerCustomer = mockedCustomerDataManager - } - - @Test - fun testGetCustomerPage() { - `when`(mockedManagerCustomer.fetchCustomers(pageIndex, size)) - .thenReturn(Observable.just(customerPage)) - customerPresenter.fetchCustomers(pageIndex, size) - verify(mockedView).showProgressbar() - verify(mockedView).hideProgressbar() - verify(mockedView).showCustomers(customerPage.customers) - } - - @Test - fun testGetAccountsPageEmpty() { - `when`(mockedManagerCustomer.fetchCustomers(pageIndex, size)) - .thenReturn(Observable.just(CustomerPage(totalPages = 0))) - `when`(mockedContext.getString(R.string.empty_customer_list)) - .thenReturn("Empty customer list") - customerPresenter.fetchCustomers(pageIndex, size) - verify(mockedView).showProgressbar() - verify(mockedView).hideProgressbar() - verify(mockedView).showEmptyCustomers("Empty customer list") - } - - - @Test - fun testGetAccountsPageFail() { - val exception = NoConnectivityException() - `when`(mockedCustomerDataManager.fetchCustomers(pageIndex, size)) - .thenReturn(Observable.error(exception)) - customerPresenter.fetchCustomers(pageIndex, size) - verify(mockedView).showProgressbar() - verify(mockedView).hideProgressbar() - verify(mockedView).showNoInternetConnection() - } - - @After - fun tearDown() { - customerPresenter.detachView() - } - -} \ No newline at end of file diff --git a/app/sync-gateway-config.json b/app/sync-gateway-config.json new file mode 100644 index 0000000..b3f7592 --- /dev/null +++ b/app/sync-gateway-config.json @@ -0,0 +1,19 @@ +{ + "interface":"0.0.0.0:4984", + "log": ["*"], + "databases": { + "fineract-cn": { + "server": "ws://localhost:8091/", + "bucket": "fineract-cn-mobile", + "username": "fineract-cn", + "password": "password", + "enable_shared_bucket_access": true, + "import_docs": true, + "num_index_replicas": 0, + "users": { + "GUEST": { "disabled": false, "admin_channels": ["*"] }, + "sync_gateway": {"password": "password"} + } + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index e663451..b9a7f79 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ task clean(type: Delete) { ext { // Sdk and tools - minSdkVersion = 16 + minSdkVersion = 19 targetSdkVersion = 28 compileSdkVersion = 28 buildToolsVersion = '28.0.3' @@ -61,7 +61,7 @@ ext { hamcrestVersion = '1.3' runnerVersion = '1.1.0' rulesVersion = '1.1.0' - espressoVersion = '3.1.0' + espressoVersion = '3.2.0' sweetErrorVersion = '1.0.0' glideVersion = '3.7.0' materialStepperVersion = '3.3.0' @@ -72,4 +72,8 @@ ext { mifosPasscodeVersion = '1.0.0' easyValidationVersion = '1.0.1' version_kotlin_coroutines = '1.3.4' + couchbase = '2.7.1' + extJunit = "1.1.1" + espressoCore = "3.2.0" + espressonContribute = "3.2.0" }