Fix crash on restore LoginActivity (#182)

This commit is contained in:
Rafał Borcz 2018-11-11 23:24:49 +01:00 committed by Mikołaj Pich
parent cb7e70471b
commit 24f59b45c3
15 changed files with 151 additions and 133 deletions

View File

@ -164,7 +164,6 @@
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>

View File

@ -61,8 +61,4 @@ class SessionRepository @Inject constructor(
})
}
}
fun clearCache() {
cachedStudents = Single.just(emptyList())
}
}

View File

@ -12,7 +12,7 @@ import io.github.wulkanowy.utils.setOnSelectPageListener
import kotlinx.android.synthetic.main.activity_login.*
import javax.inject.Inject
class LoginActivity : BaseActivity(), LoginView, LoginSwitchListener {
class LoginActivity : BaseActivity(), LoginView {
@Inject
lateinit var presenter: LoginPresenter
@ -35,35 +35,36 @@ class LoginActivity : BaseActivity(), LoginView, LoginSwitchListener {
presenter.onAttachView(this)
}
override fun onBackPressed() {
presenter.onBackPressed { super.onBackPressed() }
}
override fun initAdapter() {
loginAdapter.fragments.putAll(mapOf(
"1" to LoginFormFragment.newInstance(),
"2" to LoginOptionsFragment.newInstance()
"1" to LoginFormFragment.newInstance(),
"2" to LoginOptionsFragment.newInstance()
))
loginViewpager.run {
adapter = loginAdapter
setOnSelectPageListener { presenter.onPageSelected(it) }
}
}
override fun switchFragment(position: Int) {
presenter.onSwitchFragment(position)
override fun switchView(index: Int) {
loginViewpager.setCurrentItem(index, false)
}
override fun switchView(position: Int) {
loginViewpager.setCurrentItem(position, false)
override fun notifyOptionsViewLoadData() {
(supportFragmentManager.fragments[1] as? LoginOptionsFragment)?.onParentLoadData()
}
fun onChildFragmentSwitchOptions() {
presenter.onChildViewSwitchOptions()
}
override fun hideActionBar() {
supportActionBar?.hide()
}
override fun loadOptionsView(index: Int) {
(loginAdapter.getItem(index) as LoginOptionsFragment).loadData()
override fun onBackPressed() {
presenter.onBackPressed { super.onBackPressed() }
}
public override fun onDestroy() {

View File

@ -4,8 +4,7 @@ import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.ui.base.BasePresenter
import javax.inject.Inject
class LoginPresenter @Inject constructor(errorHandler: ErrorHandler)
: BasePresenter<LoginView>(errorHandler) {
class LoginPresenter @Inject constructor(errorHandler: ErrorHandler) : BasePresenter<LoginView>(errorHandler) {
override fun onAttachView(view: LoginView) {
super.onAttachView(view)
@ -16,11 +15,11 @@ class LoginPresenter @Inject constructor(errorHandler: ErrorHandler)
}
fun onPageSelected(index: Int) {
if (index == 1) view?.loadOptionsView(index)
if (index == 1) view?.notifyOptionsViewLoadData()
}
fun onSwitchFragment(position: Int) {
view?.switchView(position)
fun onChildViewSwitchOptions() {
view?.switchView(1)
}
fun onBackPressed(default: () -> Unit) {

View File

@ -1,6 +0,0 @@
package io.github.wulkanowy.ui.modules.login
interface LoginSwitchListener {
fun switchFragment(position: Int)
}

View File

@ -8,9 +8,9 @@ interface LoginView : BaseView {
fun initAdapter()
fun loadOptionsView(index: Int)
fun switchView(position: Int)
fun hideActionBar()
fun switchView(index: Int)
fun notifyOptionsViewLoadData()
}

View File

@ -6,11 +6,12 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
import android.view.inputmethod.EditorInfo.IME_NULL
import android.widget.ArrayAdapter
import io.github.wulkanowy.R
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.login.LoginSwitchListener
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.utils.hideSoftInput
import io.github.wulkanowy.utils.showSoftInput
import kotlinx.android.synthetic.main.fragment_login_form.*
@ -34,22 +35,21 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
presenter.onAttachView(this)
}
override fun initInputs() {
override fun initView() {
loginSignButton.setOnClickListener {
presenter.attemptLogin(
loginNicknameEdit.text.toString(),
loginPassEdit.text.toString(),
loginSymbolEdit.text.toString(),
resources.getStringArray(R.array.endpoints_values)[loginHostEdit.selectedItemPosition]
loginNicknameEdit.text.toString(),
loginPassEdit.text.toString(),
loginSymbolEdit.text.toString(),
resources.getStringArray(R.array.endpoints_values)[loginHostEdit.selectedItemPosition]
)
}
loginPassEdit.setOnEditorActionListener { _, id, _ -> onEditAction(id) }
loginHostEdit.run {
adapter = ArrayAdapter.createFromResource(context, R.array.endpoints_keys, android.R.layout.simple_spinner_item).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
loginHostEdit.apply {
adapter = ArrayAdapter.createFromResource(context, R.array.endpoints_keys, android.R.layout.simple_spinner_item)
.apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) }
}
loginSymbolEdit.run {
@ -58,13 +58,6 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
}
}
private fun onEditAction(actionId: Int): Boolean {
return when (actionId) {
EditorInfo.IME_ACTION_DONE, EditorInfo.IME_NULL -> loginSignButton.callOnClick()
else -> false
}
}
override fun showSymbolInput() {
loginHeader.text = getString(R.string.login_header_symbol)
loginMainForm.visibility = GONE
@ -73,8 +66,8 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
showSoftKeyboard()
}
override fun switchNextView() {
(activity as LoginSwitchListener?)?.switchFragment(1)
override fun switchOptionsView() {
(activity as? LoginActivity)?.onChildFragmentSwitchOptions()
}
override fun setErrorNicknameRequired() {
@ -132,11 +125,21 @@ class LoginFormFragment : BaseFragment(), LoginFormView {
activity?.hideSoftInput()
}
override fun showLoginProgress(show: Boolean) {
loginFormContainer.visibility = if (show) GONE else VISIBLE
override fun showProgress(show: Boolean) {
loginFormProgressContainer.visibility = if (show) VISIBLE else GONE
}
override fun showContent(show: Boolean) {
loginFormContainer.visibility = if (show) VISIBLE else GONE
}
private fun onEditAction(actionId: Int): Boolean {
return when (actionId) {
IME_ACTION_DONE, IME_NULL -> loginSignButton.callOnClick()
else -> false
}
}
override fun onDestroyView() {
super.onDestroyView()
presenter.onDetachView()

View File

@ -19,27 +19,35 @@ class LoginFormPresenter @Inject constructor(
override fun onAttachView(view: LoginFormView) {
super.onAttachView(view)
view.initInputs()
view.run {
initView()
errorHandler.doOnBadCredentials = {
setErrorPassIncorrect()
showSoftKeyboard()
Timber.i("Entered wrong username or password")
}
}
}
fun attemptLogin(email: String, password: String, symbol: String, endpoint: String) {
if (!validateCredentials(email, password, symbol)) return
disposable.add(sessionRepository.getConnectedStudents(email, password, symbol, endpoint)
.observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
view?.apply {
hideSoftKeyboard()
showLoginProgress(true)
errorHandler.doOnBadCredentials = {
setErrorPassIncorrect()
showSoftKeyboard()
Timber.i("Entered wrong username or password")
}
showProgress(true)
showContent(false)
}
}
.doFinally {
view?.apply {
showProgress(false)
showContent(true)
}
sessionRepository.clearCache()
}
.doFinally { view?.showLoginProgress(false) }
.subscribe({
view?.run {
if (it.isEmpty() && !wasEmpty) {
@ -50,7 +58,7 @@ class LoginFormPresenter @Inject constructor(
setErrorSymbolIncorrect()
logRegister("No student found", false, if (symbol.isEmpty()) "nil" else symbol, endpoint)
} else {
switchNextView()
switchOptionsView()
logEvent("Found students", mapOf("students" to it.size, "symbol" to it.joinToString { student -> student.symbol }, "endpoint" to endpoint))
}
}

View File

@ -4,7 +4,9 @@ import io.github.wulkanowy.ui.base.BaseView
interface LoginFormView : BaseView {
fun initInputs()
fun initView()
fun switchOptionsView()
fun setErrorNicknameRequired()
@ -24,9 +26,9 @@ interface LoginFormView : BaseView {
fun hideSoftKeyboard()
fun showLoginProgress(show: Boolean)
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
fun showSymbolInput()
fun switchNextView()
}

View File

@ -38,28 +38,21 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView {
presenter.onAttachView(this)
}
override fun initRecycler() {
loginAdapter.run {
setOnItemClickListener { position ->
(getItem(position) as? LoginOptionsItem)?.let {
presenter.onSelectStudent(it.student)
}
}
}
loginOptionsRecycler.run {
override fun initView() {
loginAdapter.apply { setOnItemClickListener { presenter.onSelectItem(getItem(it)) } }
loginOptionsRecycler.apply {
adapter = loginAdapter
layoutManager = SmoothScrollLinearLayoutManager(context)
}
}
fun loadData() {
presenter.refreshData()
fun onParentLoadData() {
presenter.onParentViewLoadData()
}
override fun updateData(data: List<LoginOptionsItem>) {
loginAdapter.run {
updateDataSet(data, true)
}
loginAdapter.updateDataSet(data, true)
}
override fun openMainView() {
@ -69,17 +62,20 @@ class LoginOptionsFragment : BaseFragment(), LoginOptionsView {
}
}
override fun showLoginProgress(show: Boolean) {
override fun showProgress(show: Boolean) {
loginOptionsProgressContainer.visibility = if (show) VISIBLE else GONE
loginOptionsRecycler.visibility = if (show) GONE else VISIBLE
}
override fun showContent(show: Boolean) {
loginOptionsRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showActionBar(show: Boolean) {
(activity as AppCompatActivity?)?.supportActionBar?.run { if (show) show() else hide() }
(activity as? AppCompatActivity)?.supportActionBar?.run { if (show) show() else hide() }
}
override fun onDestroyView() {
super.onDestroyView()
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.login.options
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import io.github.wulkanowy.data.ErrorHandler
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.SessionRepository
@ -16,29 +17,31 @@ class LoginOptionsPresenter @Inject constructor(
override fun onAttachView(view: LoginOptionsView) {
super.onAttachView(view)
view.initRecycler()
view.initView()
}
fun refreshData() {
fun onParentViewLoadData() {
disposable.add(repository.cachedStudents
.observeOn(schedulers.mainThread)
.subscribeOn(schedulers.backgroundThread)
.doOnSubscribe { view?.showActionBar(true) }
.doFinally { repository.clearCache() }
.subscribe({
view?.updateData(it.map { student ->
LoginOptionsItem(student)
})
}, { errorHandler.proceed(it) }))
.subscribe({ view?.updateData(it.map { student -> LoginOptionsItem(student) }) }, { errorHandler.proceed(it) }))
}
fun onSelectStudent(student: Student) {
fun onSelectItem(item: AbstractFlexibleItem<*>?) {
if (item is LoginOptionsItem) {
registerStudent(item.student)
}
}
private fun registerStudent(student: Student) {
disposable.add(repository.saveStudent(student)
.subscribeOn(schedulers.backgroundThread)
.observeOn(schedulers.mainThread)
.doOnSubscribe {
view?.run {
showLoginProgress(true)
showProgress(true)
showContent(false)
showActionBar(false)
}
}

View File

@ -4,13 +4,15 @@ import io.github.wulkanowy.ui.base.BaseView
interface LoginOptionsView : BaseView {
fun updateData(data: List<LoginOptionsItem>)
fun initView()
fun initRecycler()
fun updateData(data: List<LoginOptionsItem>)
fun openMainView()
fun showLoginProgress(show: Boolean)
fun showProgress(show: Boolean)
fun showContent(show: Boolean)
fun showActionBar(show: Boolean)
}

View File

@ -4,7 +4,10 @@ import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
class LoginPresenterTest {
@ -35,16 +38,19 @@ class LoginPresenterTest {
@Test
fun onPageSelectedTest() {
presenter.onPageSelected(1)
verify(loginView).loadOptionsView(1)
verify(loginView).notifyOptionsViewLoadData()
}
@Test
fun onPageSelectedNeverTest() {
presenter.onPageSelected(0)
verify(loginView, never()).loadOptionsView(0)
verify(loginView, never()).notifyOptionsViewLoadData()
}
@Test
fun onSwitchFragmentTest() {
presenter.onSwitchFragment(4)
verify(loginView).switchView(4)
presenter.onChildViewSwitchOptions()
verify(loginView).switchView(1)
}
@Test

View File

@ -9,7 +9,11 @@ import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
class LoginFormPresenterTest {
@ -35,7 +39,7 @@ class LoginFormPresenterTest {
@Test
fun initViewTest() {
verify(loginFormView).initInputs()
verify(loginFormView).initView()
}
@Test
@ -86,10 +90,11 @@ class LoginFormPresenterTest {
presenter.attemptLogin("@", "123456", "test", "https://fakelog.cf")
verify(loginFormView).hideSoftKeyboard()
verify(loginFormView).showLoginProgress(true)
verify(repository).clearCache()
verify(loginFormView).showLoginProgress(false)
verify(loginFormView).switchNextView()
verify(loginFormView).showProgress(true)
verify(loginFormView).showProgress(false)
verify(loginFormView).showContent(false)
verify(loginFormView).showContent(true)
verify(loginFormView).switchOptionsView()
}
@Test
@ -99,9 +104,10 @@ class LoginFormPresenterTest {
presenter.attemptLogin("@", "123456", "test", "https://fakelog.cf")
verify(loginFormView).hideSoftKeyboard()
verify(loginFormView).showLoginProgress(true)
verify(repository).clearCache()
verify(loginFormView).showLoginProgress(false)
verify(loginFormView).showProgress(true)
verify(loginFormView).showProgress(false)
verify(loginFormView).showContent(false)
verify(loginFormView).showContent(true)
verify(loginFormView).showSymbolInput()
}
@ -113,9 +119,10 @@ class LoginFormPresenterTest {
presenter.attemptLogin("@", "123456", "test", "https://fakelog.cf")
verify(loginFormView, times(2)).hideSoftKeyboard()
verify(loginFormView, times(2)).showLoginProgress(true)
verify(repository, times(2)).clearCache()
verify(loginFormView, times(2)).showLoginProgress(false)
verify(loginFormView, times(2)).showProgress(true)
verify(loginFormView, times(2)).showProgress(false)
verify(loginFormView, times(2)).showContent(false)
verify(loginFormView, times(2)).showContent(true)
verify(loginFormView, times(2)).showSymbolInput()
verify(loginFormView).setErrorSymbolIncorrect()
@ -129,9 +136,10 @@ class LoginFormPresenterTest {
presenter.attemptLogin("@", "123456", "test", "https://fakelog.cf")
verify(loginFormView).hideSoftKeyboard()
verify(loginFormView).showLoginProgress(true)
verify(repository).clearCache()
verify(loginFormView).showLoginProgress(false)
verify(loginFormView).showProgress(true)
verify(loginFormView).showProgress(false)
verify(loginFormView).showContent(false)
verify(loginFormView).showContent(true)
verify(errorHandler).proceed(testException)
}
}

View File

@ -9,7 +9,9 @@ import io.reactivex.Single
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
class LoginOptionsPresenterTest {
@ -39,41 +41,40 @@ class LoginOptionsPresenterTest {
@Test
fun initViewTest() {
verify(loginOptionsView).initRecycler()
verify(loginOptionsView).initView()
}
@Test
fun refreshDataTest() {
doReturn(Single.just(listOf(testStudent))).`when`(repository).cachedStudents
presenter.refreshData()
presenter.onParentViewLoadData()
verify(loginOptionsView).showActionBar(true)
verify(loginOptionsView).updateData(listOf(LoginOptionsItem(testStudent)))
verify(repository).clearCache()
}
@Test
fun refreshDataErrorTest() {
doReturn(Single.error<List<Student>>(testException)).`when`(repository).cachedStudents
presenter.refreshData()
presenter.onParentViewLoadData()
verify(loginOptionsView).showActionBar(true)
verify(errorHandler).proceed(testException)
verify(repository).clearCache()
}
@Test
fun onSelectedStudentTest() {
doReturn(Completable.complete()).`when`(repository).saveStudent(testStudent)
presenter.onSelectStudent(testStudent)
verify(loginOptionsView).showLoginProgress(true)
presenter.onSelectItem(LoginOptionsItem(testStudent))
verify(loginOptionsView).showContent(false)
verify(loginOptionsView).showProgress(true)
verify(loginOptionsView).openMainView()
}
@Test
fun onSelectedStudentErrorTest() {
doReturn(Completable.error(testException)).`when`(repository).saveStudent(testStudent)
presenter.onSelectStudent(testStudent)
verify(loginOptionsView).showLoginProgress(true)
presenter.onSelectItem(LoginOptionsItem(testStudent))
verify(loginOptionsView).showContent(false)
verify(loginOptionsView).showProgress(true)
verify(errorHandler).proceed(testException)
}
}