Vadik Sirekanyan 7 лет назад
Родитель
Сommit
f361bc7763

+ 18 - 0
app/src/main/java/com/sirekanyan/knigopis/dependency/profile.kt

@@ -0,0 +1,18 @@
+package com.sirekanyan.knigopis.dependency
+
+import com.sirekanyan.knigopis.common.extensions.app
+import com.sirekanyan.knigopis.common.extensions.getRootView
+import com.sirekanyan.knigopis.feature.profile.ProfileActivity
+import com.sirekanyan.knigopis.feature.profile.ProfilePresenter
+import com.sirekanyan.knigopis.feature.profile.ProfilePresenterImpl
+import com.sirekanyan.knigopis.feature.profile.ProfileViewImpl
+
+fun ProfileActivity.providePresenter(): ProfilePresenter =
+    ProfilePresenterImpl(
+        this,
+        app.endpoint,
+        app.bookRepository,
+        app.authRepository
+    ).also { presenter ->
+        presenter.view = ProfileViewImpl(getRootView(), presenter)
+    }

+ 14 - 204
app/src/main/java/com/sirekanyan/knigopis/feature/profile/ProfileActivity.kt

@@ -3,235 +3,45 @@ package com.sirekanyan.knigopis.feature.profile
 import android.content.Context
 import android.content.Intent
 import android.os.Bundle
-import android.support.v7.widget.Toolbar
-import android.view.MenuItem
-import android.view.animation.AccelerateInterpolator
-import android.view.inputmethod.EditorInfo
 import com.sirekanyan.knigopis.R
 import com.sirekanyan.knigopis.common.BaseActivity
-import com.sirekanyan.knigopis.common.extensions.*
-import com.sirekanyan.knigopis.common.functions.createTextShareIntent
-import com.sirekanyan.knigopis.common.functions.logError
-import com.sirekanyan.knigopis.model.BookDataModel
-import com.sirekanyan.knigopis.model.dto.Profile
-import com.sirekanyan.knigopis.model.dto.User
-import io.reactivex.Observable
-import io.reactivex.rxkotlin.Observables
-import kotlinx.android.synthetic.main.profile_activity.*
-import java.util.*
-import java.util.concurrent.TimeUnit
+import com.sirekanyan.knigopis.common.functions.createProfileShareIntent
+import com.sirekanyan.knigopis.dependency.providePresenter
 
 fun Context.createProfileIntent() = Intent(this, ProfileActivity::class.java)
 
-class ProfileActivity : BaseActivity() {
+class ProfileActivity : BaseActivity(), ProfilePresenter.Router {
 
-    private val api by lazy { app.endpoint }
-    private val bookRepository by lazy { app.bookRepository }
-    private val auth by lazy { app.authRepository }
-    private val todoList = Stack<BookDataModel>()
-    private val doingList = Stack<BookDataModel>()
-    private val doneList = Stack<BookDataModel>()
-    private var userId: String? = null
-    private var profileUrl: String? = null
-    private lateinit var editOption: MenuItem
+    private val presenter by lazy(::providePresenter)
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.profile_activity)
-        initToolbar(profileToolbar)
-        profileTodoCount.text = getString(R.string.profile_text_todo, 0)
-        profileDoingCount.text = getString(R.string.profile_text_doing, 0)
-        profileDoneCount.text = getString(R.string.profile_text_done, 0)
-        mapOf(
-            profileTodoCount to todoList,
-            profileDoingCount to doingList,
-            profileDoneCount to doneList
-        ).forEach { view, list ->
-            view.setOnClickListener {
-                if (!list.isEmpty()) {
-                    showFooterBook(list.pop())
-                }
-            }
-        }
-        profileNicknameEditText.setOnEditorActionListener { _, actionId, _ ->
-            when (actionId) {
-                EditorInfo.IME_ACTION_DONE -> {
-                    updateNicknameOrExitEditMode()
-                    true
-                }
-                else -> false
-            }
-        }
-    }
-
-    private fun showFooterBook(book: BookDataModel) {
-        randomProfileBook.alpha = 1f
-        randomProfileBook.text = getString(
-            R.string.profile_text_random,
-            resources.getTitleString(book.title),
-            book.priority
-        )
-        randomProfileBook.animate()
-            .setInterpolator(AccelerateInterpolator())
-            .setDuration(1000)
-            .alpha(0f)
+        presenter.init()
     }
 
     override fun onStart() {
         super.onStart()
-        refreshProfile()
-        refreshCounters()
-    }
-
-    private fun refreshProfile() {
-        api.getProfile(auth.getAccessToken()).io2main()
-            .bind(::onRefreshProfile) {
-                logError("cannot get profile", it)
-            }
-    }
-
-    private fun onRefreshProfile(user: User) {
-        userId = user.id
-        profileUrl = user.fixedProfile
-        profileNickname.text = user.nickname.orEmpty()
-        profileAvatar.setCircleImage(user.photo)
-        editOption.isVisible = true
-    }
-
-    private fun refreshCounters() {
-        bookRepository.findCached()
-            .toSingle(listOf())
-            .map { it.filterIsInstance<BookDataModel>() }
-            .map { it.shuffled() }
-            .flatMapObservable {
-                Observables.zip(
-                    Observable.fromIterable(it),
-                    Observable.interval(5, TimeUnit.MILLISECONDS)
-                )
-            }
-            .io2main()
-            .doOnSubscribe {
-                doneList.clear()
-                doingList.clear()
-                todoList.clear()
-            }
-            .bind({ (book) ->
-                addBookToList(book)
-            }, {
-                logError("cannot get cached books", it)
-            })
+        presenter.start()
     }
 
-    @Suppress("USELESS_CAST")
-    private fun addBookToList(book: BookDataModel) {
-        when {
-            book.isFinished -> {
-                doneList.push(book)
-                profileDoneCount.text =
-                        getString(R.string.profile_text_done, doneList.size as Int)
-            }
-            book.priority > 0 -> {
-                doingList.push(book)
-                profileDoingCount.text =
-                        getString(R.string.profile_text_doing, doingList.size as Int)
-            }
-            else -> {
-                todoList.push(book)
-                profileTodoCount.text =
-                        getString(R.string.profile_text_todo, todoList.size as Int)
-            }
-        }
+    override fun onStop() {
+        super.onStop()
+        presenter.stop()
     }
 
-    private fun updateNicknameOrExitEditMode() {
-        if (profileNickname.text.toString() == profileNicknameEditText.text.toString()) {
-            quitEditMode()
-        } else {
-            updateNickname()
-        }
+    override fun shareProfile(profileUrl: String) {
+        startActivity(createProfileShareIntent(profileUrl))
     }
 
-    private fun updateNickname() {
-        val id = userId ?: return
-        api.updateProfile(
-            id,
-            auth.getAccessToken(),
-            Profile(
-                profileNicknameEditText.text.toString(),
-                profileUrl.orEmpty()
-            )
-        ).io2main()
-            .bind({
-                profileNickname.text = profileNicknameEditText.text
-                quitEditMode()
-                refreshProfile()
-            }, {
-                toast(R.string.profile_error_save)
-                logError("cannot update profile", it)
-            })
-    }
-
-    private fun initToolbar(toolbar: Toolbar) {
-        toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
-        toolbar.setNavigationOnClickListener { finish() }
-        toolbar.inflateMenu(R.menu.profile_menu)
-        toolbar.setOnMenuItemClickListener {
-            when (it.itemId) {
-                R.id.option_edit_profile -> {
-                    if (isEditMode) {
-                        updateNicknameOrExitEditMode()
-                    } else {
-                        enterEditMode()
-                        val nickname = profileNickname.text
-                        profileNicknameEditText.setText(nickname)
-                        profileNicknameEditText.setSelection(nickname.length, nickname.length)
-                    }
-                    true
-                }
-                R.id.option_share_profile -> {
-                    profileUrl?.let {
-                        startActivity(
-                            createTextShareIntent(it, getString(R.string.profile_title_share))
-                        )
-                    }
-                    true
-                }
-                R.id.option_logout_profile -> {
-                    auth.logout()
-                    finish()
-                    true
-                }
-                else -> false
-            }
-        }
-        editOption = toolbar.menu.findItem(R.id.option_edit_profile)
+    override fun exit() {
+        finish()
     }
 
     override fun onBackPressed() {
-        if (profileNickname.isVisible) {
+        if (!presenter.back()) {
             super.onBackPressed()
-        } else {
-            quitEditMode()
         }
     }
 
-    private fun enterEditMode() {
-        editOption.setIcon(R.drawable.ic_done)
-        editOption.setTitle(R.string.profile_option_save)
-        topProfileSpace.hideNow()
-        profileNicknameSwitcher.displayedChild = 1
-        showKeyboard(profileNicknameEditText)
-    }
-
-    private fun quitEditMode() {
-        editOption.setIcon(R.drawable.ic_edit)
-        editOption.setTitle(R.string.profile_option_edit)
-        hideKeyboard()
-        topProfileSpace.showNow()
-        profileNicknameSwitcher.displayedChild = 0
-    }
-
-    private val isEditMode
-        get() = profileNicknameSwitcher.displayedChild == 1
-
 }

+ 163 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/profile/ProfilePresenter.kt

@@ -0,0 +1,163 @@
+package com.sirekanyan.knigopis.feature.profile
+
+import com.sirekanyan.knigopis.common.BasePresenter
+import com.sirekanyan.knigopis.common.Presenter
+import com.sirekanyan.knigopis.common.extensions.io2main
+import com.sirekanyan.knigopis.common.functions.logError
+import com.sirekanyan.knigopis.model.BookDataModel
+import com.sirekanyan.knigopis.model.dto.Profile
+import com.sirekanyan.knigopis.model.dto.User
+import com.sirekanyan.knigopis.repository.AuthRepository
+import com.sirekanyan.knigopis.repository.BookRepository
+import com.sirekanyan.knigopis.repository.Endpoint
+import io.reactivex.Observable
+import io.reactivex.rxkotlin.Observables
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+interface ProfilePresenter : Presenter {
+
+    fun init()
+    fun start()
+    fun back(): Boolean
+
+    interface Router {
+        fun shareProfile(profileUrl: String)
+        fun exit()
+    }
+
+}
+
+class ProfilePresenterImpl(
+    private val router: ProfilePresenter.Router,
+    private val api: Endpoint,
+    private val bookRepository: BookRepository,
+    private val authRepository: AuthRepository
+) : BasePresenter<ProfileView>(),
+    ProfilePresenter,
+    ProfileView.Callbacks {
+
+    private val todoList = Stack<BookDataModel>()
+    private val doingList = Stack<BookDataModel>()
+    private val doneList = Stack<BookDataModel>()
+    private var user: User? = null
+
+    override fun init() {
+        view.setTodoCount(0)
+        view.setDoingCount(0)
+        view.setDoneCount(0)
+        view.setBooks(todoList, doingList, doneList)
+    }
+
+    override fun start() {
+        refreshProfile()
+        refreshCounters()
+    }
+
+    override fun back(): Boolean =
+        if (view.isEditMode) {
+            view.quitEditMode()
+            true
+        } else {
+            false
+        }
+
+    override fun onNavigationBackClicked() {
+        router.exit()
+    }
+
+    override fun onEditOptionClicked() {
+        view.enterEditMode()
+    }
+
+    override fun onSaveOptionClicked(nickname: String) {
+        if (view.isNicknameChanged) {
+            updateNickname(nickname)
+        } else {
+            view.quitEditMode()
+        }
+    }
+
+    override fun onShareOptionClicked() {
+        user?.fixedProfile?.let {
+            router.shareProfile(it)
+        }
+    }
+
+    override fun onLogoutOptionClicked() {
+        authRepository.clear()
+        router.exit()
+    }
+
+    private fun refreshProfile() {
+        api.getProfile(authRepository.getAccessToken())
+            .io2main()
+            .bind({ user ->
+                this.user = user
+                view.setNickname(user.nickname.orEmpty())
+                view.setAvatar(user.photo)
+                view.setEditOptionVisible(true)
+            }) {
+                logError("cannot get profile", it)
+            }
+    }
+
+    private fun refreshCounters() {
+        bookRepository.findCached()
+            .toSingle(listOf())
+            .map { it.filterIsInstance<BookDataModel>() }
+            .map { it.shuffled() }
+            .flatMapObservable {
+                Observables.zip(
+                    Observable.fromIterable(it),
+                    Observable.interval(5, TimeUnit.MILLISECONDS)
+                )
+            }
+            .io2main()
+            .doOnSubscribe {
+                doneList.clear()
+                doingList.clear()
+                todoList.clear()
+            }
+            .bind({ (book) ->
+                addBookToList(book)
+            }, {
+                logError("cannot get cached books", it)
+            })
+    }
+
+    private fun addBookToList(book: BookDataModel) {
+        when {
+            book.isFinished -> {
+                doneList.push(book)
+                view.setDoneCount(doneList.size)
+            }
+            book.priority > 0 -> {
+                doingList.push(book)
+                view.setDoingCount(doingList.size)
+            }
+            else -> {
+                todoList.push(book)
+                view.setTodoCount(todoList.size)
+            }
+        }
+    }
+
+    private fun updateNickname(nickname: String) {
+        val id = user?.id ?: return
+        api.updateProfile(
+            id,
+            authRepository.getAccessToken(),
+            Profile(nickname, user?.fixedProfile.orEmpty())
+        ).io2main()
+            .bind({
+                view.setNickname(nickname)
+                view.quitEditMode()
+                refreshProfile()
+            }, {
+                view.showSaveError()
+                logError("cannot update profile", it)
+            })
+    }
+
+}

+ 177 - 0
app/src/main/java/com/sirekanyan/knigopis/feature/profile/ProfileView.kt

@@ -0,0 +1,177 @@
+package com.sirekanyan.knigopis.feature.profile
+
+import android.support.v7.widget.Toolbar
+import android.view.MenuItem
+import android.view.View
+import android.view.animation.AccelerateInterpolator
+import android.view.inputmethod.EditorInfo
+import com.sirekanyan.knigopis.R
+import com.sirekanyan.knigopis.common.extensions.*
+import com.sirekanyan.knigopis.model.BookDataModel
+import kotlinx.android.extensions.LayoutContainer
+import kotlinx.android.synthetic.main.profile_activity.*
+import java.util.*
+
+interface ProfileView {
+
+    val isEditMode: Boolean
+    val isNicknameChanged: Boolean
+    fun setNickname(name: String)
+    fun setAvatar(avatar: String?)
+    fun setEditOptionVisible(isVisible: Boolean)
+    fun setTodoCount(count: Int)
+    fun setDoingCount(count: Int)
+    fun setDoneCount(count: Int)
+    fun enterEditMode()
+    fun quitEditMode()
+    fun showSaveError()
+    fun setBooks(
+        todo: Stack<BookDataModel>,
+        doing: Stack<BookDataModel>,
+        done: Stack<BookDataModel>
+    )
+
+    interface Callbacks {
+        fun onNavigationBackClicked()
+        fun onEditOptionClicked()
+        fun onSaveOptionClicked(nickname: String)
+        fun onShareOptionClicked()
+        fun onLogoutOptionClicked()
+    }
+
+}
+
+class ProfileViewImpl(
+    override val containerView: View,
+    private val callbacks: ProfileView.Callbacks
+) : ProfileView,
+    LayoutContainer {
+
+    private val context = containerView.context
+    private lateinit var editOption: MenuItem
+    override val isEditMode get() = profileNicknameSwitcher.displayedChild == 1
+    override val isNicknameChanged get() = profileNickname.text.toString() != profileNicknameEditText.text.toString()
+
+    init {
+        initToolbar(profileToolbar)
+
+        profileNicknameEditText.setOnEditorActionListener { view, actionId, _ ->
+            when (actionId) {
+                EditorInfo.IME_ACTION_DONE -> {
+                    callbacks.onSaveOptionClicked(view.text.toString())
+                    true
+                }
+                else -> false
+            }
+        }
+    }
+
+    override fun setNickname(name: String) {
+        profileNickname.text = name
+    }
+
+    override fun setAvatar(avatar: String?) {
+        profileAvatar.setCircleImage(avatar)
+    }
+
+    override fun setEditOptionVisible(isVisible: Boolean) {
+        editOption.isVisible = isVisible
+    }
+
+    override fun setTodoCount(count: Int) {
+        profileTodoCount.text = context.getString(R.string.profile_text_todo, count)
+    }
+
+    override fun setDoingCount(count: Int) {
+        profileDoingCount.text = context.getString(R.string.profile_text_doing, count)
+    }
+
+    override fun setDoneCount(count: Int) {
+        profileDoneCount.text = context.getString(R.string.profile_text_done, count)
+    }
+
+    override fun enterEditMode() {
+        editOption.setIcon(R.drawable.ic_done)
+        editOption.setTitle(R.string.profile_option_save)
+        topProfileSpace.hideNow()
+        profileNicknameSwitcher.displayedChild = 1
+        containerView.showKeyboard(profileNicknameEditText)
+        run {
+            val nickname = profileNickname.text
+            profileNicknameEditText.setText(nickname)
+            profileNicknameEditText.setSelection(nickname.length, nickname.length)
+        }
+    }
+
+    override fun quitEditMode() {
+        editOption.setIcon(R.drawable.ic_edit)
+        editOption.setTitle(R.string.profile_option_edit)
+        containerView.hideKeyboard()
+        topProfileSpace.showNow()
+        profileNicknameSwitcher.displayedChild = 0
+    }
+
+    override fun showSaveError() {
+        context.toast(R.string.profile_error_save)
+    }
+
+    override fun setBooks(
+        todo: Stack<BookDataModel>,
+        doing: Stack<BookDataModel>,
+        done: Stack<BookDataModel>
+    ) {
+        mapOf(
+            profileTodoCount to todo,
+            profileDoingCount to doing,
+            profileDoneCount to done
+        ).forEach { view, list ->
+            view.setOnClickListener {
+                if (!list.isEmpty()) {
+                    showFooterBook(list.pop())
+                }
+            }
+        }
+    }
+
+    private fun initToolbar(toolbar: Toolbar) {
+        toolbar.setNavigationIcon(R.drawable.ic_arrow_back)
+        toolbar.setNavigationOnClickListener { callbacks.onNavigationBackClicked() }
+        toolbar.inflateMenu(R.menu.profile_menu)
+        toolbar.setOnMenuItemClickListener {
+            when (it.itemId) {
+                R.id.option_edit_profile -> {
+                    if (isEditMode) {
+                        callbacks.onSaveOptionClicked(profileNicknameEditText.text.toString())
+                    } else {
+                        callbacks.onEditOptionClicked()
+                    }
+                    true
+                }
+                R.id.option_share_profile -> {
+                    callbacks.onShareOptionClicked()
+                    true
+                }
+                R.id.option_logout_profile -> {
+                    callbacks.onLogoutOptionClicked()
+                    true
+                }
+                else -> false
+            }
+        }
+        editOption = toolbar.menu.findItem(R.id.option_edit_profile)
+    }
+
+    private fun showFooterBook(book: BookDataModel) {
+        randomProfileBook.alpha = 1f
+        randomProfileBook.text = context.getString(
+            R.string.profile_text_random,
+            context.resources.getTitleString(book.title),
+            book.priority
+        )
+        randomProfileBook.animate()
+            .setInterpolator(AccelerateInterpolator())
+            .setDuration(1000)
+            .alpha(0f)
+    }
+
+}

+ 2 - 2
app/src/main/java/com/sirekanyan/knigopis/repository/AuthRepository.kt

@@ -15,7 +15,7 @@ interface AuthRepository {
     fun getAccessToken(): String
     fun getUserProfile(): String?
     fun saveToken(token: String)
-    fun logout()
+    fun clear()
 }
 
 class AuthRepositoryImpl(
@@ -55,7 +55,7 @@ class AuthRepositoryImpl(
         }
     }
 
-    override fun logout() {
+    override fun clear() {
         preferences.edit().remove(TOKEN_KEY).remove(ACCESS_TOKEN_KEY).apply()
     }