Code Implementation
Step 1. Login Contract
- Defines the interface between the View (Activity) and Presenter.
- View interface defines methods for UI updates (e.g., show/hide dialogue, etc).
- Presenter interface defines methods for business logic (e.g., signInWithInternet).
interface LoginContract {
interface View : BaseView {
fun dismissDialog()
fun setCampaignAdapter()
fun showLoginAlertDialog(message: String)
fun showProgressDialog()
fun showBiometricDialog(user: User)
fun startHomeScreen(user: User)
fun startMetadataService()
fun showSnackBarLong(msg: String)
}
interface Presenter : BasePresenter<View> {
fun doSync()
fun signInWithInternet(username: String, password: String, versionName: String)
fun signInOffline(username: String, password: String)
fun saveUser(user: User)
fun getUserByUsername(username: String): User?
fun rememberUser(remember: Boolean)
fun isUserRemembered(): Boolean
fun getLastUser(): User?
fun removeUser()
fun getCampaigns(): List<Campaign>
fun downloadCampaigns()
fun setVaccinatorType(vaccinatorType: String)
fun removeVaccinatorType()
fun getVaccinatorType(): String
fun isVaccinatorTypeCampaign(): Boolean
fun getCampaignByName(name: String): Campaign
fun saveCampaign(campaign: Campaign)
fun requestForMetadataService()
fun isLocationsPresent(): Boolean
}
}
Step 2. Presenter
The LoginPresenterImpl contains the business logic for handling user login. It interacts with the model and updates the LoginView.
class LoginPresenterImpl : LoginContract.Presenter {
private var view: LoginContract.View? = null
override fun doSync() {
ApiService.getMetadata(object : OnResponseReceivedListener {
override fun onSuccessReceived(response: Any?) {
val isSaved = Utils.saveMetadata((response as BaseResponse).params.metadata)
if (isSaved)
view?.showToast(view?.getResource(R.string.app_synced))
else
view?.showToast(view?.getResource(R.string.metadata_error))
view?.dismissDialog()
}
override fun onFailureReceived(errorMessage: String?, responseCode: Int) {
view?.showToast(view?.getResource(R.string.app_sync_failed))
view?.dismissDialog()
}
})
}
override fun signInOffline(username: String, password: String) {
val user = getUserByUsername(username)
if (user != null) {
val decryptedPassword = SecurityUtils.decrypt(user.password, user.username)
if (decryptedPassword.equals(password, ignoreCase = false)) {
user.username = username
if (isPrivileged(BIOMETRIC_VERIFICATION,user))
view?.showBiometricDialog(user)
else
view?.startHomeScreen(user)
} else
view?.showLoginAlertDialog(view?.getResource(R.string.login_username_password_invalid)!!)
} else
view?.showLoginAlertDialog(view?.getResource(R.string.login_username_password_invalid)!!)
}
override fun signInWithInternet(username: String, password: String, versionName: String) {
ApiService.doLogin(
getPayload(username, password, versionName),
object : OnResponseReceivedListener {
override fun onSuccessReceived(response: Any?) {
view?.dismissDialog()
syncMetadataAfterSignInWithInternet(username)
}
override fun onFailureReceived(errorMessage: String?, responseCode: Int) {
view?.dismissDialog()
view?.showToast(errorMessage)
}
})
}
override fun isLocationsPresent(): Boolean = !LocationDbHelper.isEmpty()
override fun getUserByUsername(username: String): User? =
UserDBHelper.getUserByUsername(username)
override fun saveUser(user: User) = DevicePreferences.saveUser(user)
override fun rememberUser(remember: Boolean) = DevicePreferences.rememberUser(remember)
override fun isUserRemembered(): Boolean = DevicePreferences.isUserRemembered()
override fun getLastUser(): User? = DevicePreferences.getLastUser()
override fun removeUser() = DevicePreferences.removeUser()
override fun takeView(view: LoginContract.View) {
this.view = view
}
override fun dropView() {
view = null }}
Step 3.Implement the Activity
The Activity serves as the concrete implementation of the View interface and integrates with the Presenter.
class LoginActivity : BaseActivity(), LoginContract.View, BiometricCallback {
@Inject
lateinit var presenter: LoginContract.Presenter
var username = ""
var password = ""
lateinit var user: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
(application as Application).component.inject(this)
presenter.takeView(this)
init()
setListeners()
}
btnSignIn.setOnClickListener {
Utils.hideKeyboard(this)
username = etUserName.text!!.trim().toString()
password = etPassword.text!!.trim().toString()
if (presenter.getVaccinatorType().isNotEmpty())
if (presenter.isVaccinatorTypeCampaign())
if (campaignDropdown.text.toString().isNotEmpty())
presenter.signInOffline(username, password)
else
showToast(getResource(R.string.login_no_campaigns_found))
else
presenter.signInOffline(username, password)
else
showSnackBarLong(getResource(R.string.login_select_activity_type))
}
}
override fun showLoginAlertDialog(message: String) {
dialog.showAlertDialogYesNo(
this,
null,
message,
getString(R.string.yes),
getString(R.string.no),
object : DialogListener {
override fun onPositiveButtonClick() {
if (isInternetAvailable(this@LoginActivity)) {
dialog.showProgressAlertDialog(
this@LoginActivity,
getResource(R.string.dialog_please_wait)
)
presenter.signInWithInternet(
username,
password,
packageManager.getPackageInfo(packageName, 0).versionName
)
} else
showToast(getResource(R.string.no_internet))
}
override fun onNegativeButtonClick() {}
})
}
override fun showProgressDialog() {
dialog.showProgressAlertDialog(this, getResource(R.string.dialog_please_wait))
}
override fun dismissDialog() = dialog.dismiss()
override fun showToast(message: String?) =
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
override fun showSnackBarLong(msg: String) =
Snackbar.make(mainLayout, msg, Snackbar.LENGTH_LONG).show()
override fun onDestroy() {
presenter.dropView()
super.onDestroy()
}