Skip to content

Commit 06693a4

Browse files
authored
Merge pull request #14 from patjackson52/refactor-presenters
Refactor presenters
2 parents 17302ff + 7471d82 commit 06693a4

File tree

35 files changed

+660
-263
lines changed

35 files changed

+660
-263
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
<activity
1616
android:name="com.willowtreeapps.namegame.MainActivity"
1717
android:label="@string/app_name"
18-
android:theme="@style/AppTheme.TransparentTheme">
18+
android:screenOrientation="portrait"
19+
android:theme="@style/AppTheme">
1920
<intent-filter>
2021
<action android:name="android.intent.action.MAIN" />
2122

app/src/main/java/com/willowtreeapps/namegame/NameGameApp.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class NameGameApp : Application() {
1212
super.onCreate()
1313
instance = this
1414
val navigator = AndroidNavigator()
15-
gameEngine = GameEngine(navigator, this, Dispatchers.IO)
15+
gameEngine = GameEngine(navigator, this, Dispatchers.IO, Dispatchers.Main)
1616

1717
registerActivityLifecycleCallbacks(navigator)
1818
}

app/src/main/java/com/willowtreeapps/namegame/Utils.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import android.animation.Animator
44
import android.animation.AnimatorListenerAdapter
55
import android.animation.AnimatorSet
66
import android.content.res.Resources
7+
import android.graphics.drawable.Drawable
8+
import com.bumptech.glide.load.DataSource
9+
import com.bumptech.glide.load.engine.GlideException
10+
import com.bumptech.glide.request.RequestListener
11+
import com.bumptech.glide.request.target.Target
712

813
val Int.dp: Int
914
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
@@ -19,4 +24,20 @@ fun AnimatorSet.onComplete(after: () -> Unit) {
1924
after()
2025
}
2126
})
22-
}
27+
}
28+
29+
/*
30+
* Convenience function for executing lambda after the image is displayed
31+
*/
32+
fun <T: Any> GlideRequest<T>.onComplete(after: () -> Unit): GlideRequest<T> {
33+
return this.listener(object : RequestListener<T> {
34+
override fun onLoadFailed(e: GlideException?, model: Any?, target: Target<T>?, isFirstResource: Boolean): Boolean {
35+
return true
36+
}
37+
override fun onResourceReady(resource: T?, model: Any?, target: Target<T>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
38+
after()
39+
return false
40+
}
41+
42+
})
43+
}

app/src/main/java/com/willowtreeapps/namegame/store/GameResultsFragment.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class GameResultsFragment : Fragment(), CoroutineScope, GameResultsView, MainAct
3333

3434
private fun initViews() {
3535
btn_start_over.setOnClickListener {
36-
NameGameApp.gameEngine().detachView(presenter!!)
36+
NameGameApp.gameEngine().detachView(this)
3737
presenter?.startOverTapped()
3838
}
3939
}
@@ -45,7 +45,7 @@ class GameResultsFragment : Fragment(), CoroutineScope, GameResultsView, MainAct
4545

4646
override fun onPause() {
4747
super.onPause()
48-
NameGameApp.gameEngine().detachView(presenter!!)
48+
NameGameApp.gameEngine().detachView(this)
4949
}
5050

5151
override fun showResults(viewState: GameResultsViewState) {

app/src/main/java/com/willowtreeapps/namegame/store/QuestionFragment.kt

Lines changed: 97 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.willowtreeapps.namegame.store
22

3-
import android.animation.Animator
4-
import android.animation.AnimatorListenerAdapter
53
import android.animation.AnimatorSet
64
import android.animation.ObjectAnimator
75
import android.graphics.Color
@@ -15,29 +13,27 @@ import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
1513
import com.willowtreeapps.common.QuestionViewState
1614
import com.willowtreeapps.common.ui.QuestionView
1715
import kotlinx.android.synthetic.main.fragment_question.*
18-
import kotlinx.coroutines.CoroutineScope
19-
import kotlinx.coroutines.Dispatchers
2016
import nl.dionsegijn.konfetti.models.Shape
2117
import nl.dionsegijn.konfetti.models.Size
22-
import java.lang.IllegalStateException
2318
import kotlin.coroutines.CoroutineContext
2419
import android.widget.Button
25-
import androidx.annotation.ColorRes
20+
import androidx.core.content.res.ResourcesCompat
21+
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
2622
import com.willowtreeapps.common.ui.QuestionPresenter
2723
import com.willowtreeapps.namegame.*
24+
import kotlinx.coroutines.*
2825

2926

3027
class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.IOnBackPressed {
3128

32-
private var presenter: QuestionPresenter? = null
29+
private lateinit var presenter: QuestionPresenter
3330

3431
override val coroutineContext: CoroutineContext
3532
get() = Dispatchers.Main
3633

3734
private var restoreX: Float? = null
3835
private var restoreY: Float? = null
39-
@ColorRes
40-
var lastCorrectBtn: Button? = null
36+
private var lastCorrectBtn: Button? = null
4137
private var lastSelectedBtn: Button? = null
4238

4339
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -51,26 +47,26 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
5147

5248
override fun onPause() {
5349
super.onPause()
54-
NameGameApp.gameEngine().detachView(presenter!!)
50+
NameGameApp.gameEngine().detachView(this)
5551
}
5652

5753
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
5854
initViews()
5955
}
6056

6157
private fun initViews() {
62-
button1.setOnClickListener { presenter?.namePicked(button1.text.toString()) }
63-
button2.setOnClickListener { presenter?.namePicked(button2.text.toString()) }
64-
button3.setOnClickListener { presenter?.namePicked(button3.text.toString()) }
65-
button4.setOnClickListener { presenter?.namePicked(button4.text.toString()) }
66-
btn_next.setOnClickListener { presenter?.nextTapped() }
67-
btn_end_game.setOnClickListener { presenter?.endGameTapped() }
58+
button1.setOnClickListener { presenter.namePicked(button1.text.toString()) }
59+
button2.setOnClickListener { presenter.namePicked(button2.text.toString()) }
60+
button3.setOnClickListener { presenter.namePicked(button3.text.toString()) }
61+
button4.setOnClickListener { presenter.namePicked(button4.text.toString()) }
62+
btn_next.setOnClickListener { presenter.nextTapped() }
63+
btn_end_game.setOnClickListener { presenter.endGameTapped() }
6864
}
6965

7066

7167
override fun onBackPressed(): Boolean {
72-
NameGameApp.gameEngine().detachView(presenter!!)
73-
presenter?.onBackPressed()
68+
NameGameApp.gameEngine().detachView(this)
69+
presenter.onBackPressed()
7470
return false
7571
}
7672

@@ -82,25 +78,16 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
8278
setProfileAndFadeIn(viewState)
8379
}
8480
}
85-
}
86-
87-
88-
override fun showCorrectAnswer(viewState: QuestionViewState) {
89-
hideButtonsShowNext(viewState, false)
90-
celebrate()
91-
}
9281

93-
override fun showWrongAnswer(viewState: QuestionViewState) {
94-
wrongShakeAnimation(viewState) { hideButtonsShowNext(viewState, false) }
9582
}
9683

97-
override fun showCorrectAnswerEndGame(viewState: QuestionViewState) {
98-
hideButtonsShowNext(viewState, true)
84+
override fun showCorrectAnswer(viewState: QuestionViewState, isEndGame: Boolean) {
85+
hideButtonsShowNext(viewState, isEndGame)
9986
celebrate()
10087
}
10188

102-
override fun showWrongAnswerEndGame(viewState: QuestionViewState) {
103-
wrongShakeAnimation(viewState) { hideButtonsShowNext(viewState, true) }
89+
override fun showWrongAnswer(viewState: QuestionViewState, isEndGame: Boolean) {
90+
wrongShakeAnimation(viewState) { hideButtonsShowNext(viewState, isEndGame) }
10491
}
10592

10693
private val showButtonsAnimatorSet by lazy {
@@ -115,19 +102,19 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
115102

116103
private fun wrongShakeAnimation(viewState: QuestionViewState, after: () -> Unit) {
117104
val selectedBtn = getBtnByNum(viewState.selectedBtnNum)
118-
selectedBtn.isSelected = true
119-
val animScaleX = ObjectAnimator.ofFloat(selectedBtn, View.SCALE_X, 3F, 0.5F, 1F)
120-
val animScaleY = ObjectAnimator.ofFloat(selectedBtn, View.SCALE_Y, 3F, 0.5F, 1F)
121-
val upSet = AnimatorSet()
122-
upSet.playTogether(animScaleX, animScaleY)
123-
upSet.interpolator = BounceInterpolator()
124-
upSet.duration = 500
125-
upSet.addListener(object : AnimatorListenerAdapter() {
126-
override fun onAnimationEnd(animation: Animator?) {
127-
after()
128-
}
129-
})
130-
upSet.start()
105+
if (selectedBtn != null) {
106+
selectedBtn.isSelected = true
107+
val animScaleX = ObjectAnimator.ofFloat(selectedBtn, View.SCALE_X, 3F, 0.5F, 1F)
108+
val animScaleY = ObjectAnimator.ofFloat(selectedBtn, View.SCALE_Y, 3F, 0.5F, 1F)
109+
val upSet = AnimatorSet()
110+
upSet.playTogether(animScaleX, animScaleY)
111+
upSet.interpolator = BounceInterpolator()
112+
upSet.duration = 500
113+
upSet.onComplete { after() }
114+
upSet.start()
115+
} else {
116+
after()
117+
}
131118
}
132119

133120
/**
@@ -157,8 +144,8 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
157144
set
158145
}
159146
}
160-
restoreX = correctBtn.x
161-
restoreY = correctBtn.y
147+
restoreX = correctBtn?.x
148+
restoreY = correctBtn?.y
162149
lastCorrectBtn = correctBtn
163150
lastSelectedBtn = selectedBtn
164151

@@ -170,15 +157,15 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
170157
val set = AnimatorSet()
171158
set.playTogether(anim1, anim2, anim3, anim4)
172159
set.onComplete {
173-
val btn = if (isEndGame) {
174-
btn_end_game
175-
} else {
176-
btn_next
177-
}
178-
btn.visibility = View.VISIBLE
179-
btn.alpha = 0F
180-
btn.animate().alpha(1f)
160+
val btn = if (isEndGame) {
161+
btn_end_game
162+
} else {
163+
btn_next
181164
}
165+
btn.visibility = View.VISIBLE
166+
btn.alpha = 0F
167+
btn.animate().alpha(1f)
168+
}
182169
set.start()
183170
}
184171

@@ -194,26 +181,72 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
194181
}
195182

196183
private fun fadeNextButton(after: () -> Unit) {
197-
btn_next.animate().alpha(0f).setListener(object : AnimatorListenerAdapter() {
198-
override fun onAnimationEnd(animation: Animator?) {
199-
btn_next.visibility = View.GONE
200-
after()
201-
btn_next.animate().setListener(null)
202-
}
203-
})
184+
btn_next.animate().alpha(0f).withEndAction {
185+
btn_next.visibility = View.GONE
186+
after()
187+
}
204188
}
205189

206190
private fun setProfileAndFadeIn(viewState: QuestionViewState) {
207191
with(viewState) {
208192
txt_results.text = title
209193
GlideApp.with(this@QuestionFragment).load(profileImageUrl)
210194
.transition(DrawableTransitionOptions.withCrossFade())
195+
.onComplete {
196+
showButtons()
197+
txt_timer.visibility = View.VISIBLE
198+
presenter.profileImageIsVisible()
199+
}
211200
.into(imageView)
212201
button1.text = button1Text
213202
button2.text = button2Text
214203
button3.text = button3Text
215204
button4.text = button4Text
216-
showButtons()
205+
}
206+
}
207+
208+
override fun setTimerText(viewState: QuestionViewState) {
209+
activity?.runOnUiThread {
210+
txt_timer.scaleX = 0f
211+
txt_timer.scaleY = 0f
212+
txt_timer.alpha = 1f
213+
txt_timer.text = viewState.timerText
214+
txt_timer.animate()
215+
.scaleX(1f)
216+
.scaleY(1f)
217+
.setInterpolator(FastOutSlowInInterpolator())
218+
.setDuration(500)
219+
.withEndAction {
220+
if (txt_timer != null) {
221+
txt_timer.animate()
222+
.scaleX(0f)
223+
.scaleY(0f)
224+
.duration = 500
225+
}
226+
}
227+
}
228+
}
229+
230+
override fun showTimesUp(viewState: QuestionViewState, isEndGame: Boolean) {
231+
activity?.runOnUiThread {
232+
txt_timer.scaleX = 0f
233+
txt_timer.scaleY = 0f
234+
txt_timer.text = viewState.timerText
235+
val restoreColor = txt_timer.currentTextColor
236+
txt_timer.setTextColor(ResourcesCompat.getColor(context?.resources!!, R.color.red, null))
237+
txt_timer.animate()
238+
.scaleX(1f)
239+
.scaleY(1f)
240+
.setInterpolator(FastOutSlowInInterpolator())
241+
.setDuration(500)
242+
.withEndAction {
243+
showWrongAnswer(viewState, isEndGame)
244+
txt_timer.animate().alpha(0f)
245+
.withEndAction {
246+
txt_timer.visibility = View.VISIBLE
247+
txt_timer.setTextColor(restoreColor) }
248+
}
249+
217250
}
218251
}
219252

@@ -230,12 +263,12 @@ class QuestionFragment : Fragment(), CoroutineScope, QuestionView, MainActivity.
230263
.burst(200)
231264
}
232265

233-
private fun getBtnByNum(num: Int): Button = when (num) {
266+
private fun getBtnByNum(num: Int): Button? = when (num) {
234267
1 -> button1
235268
2 -> button2
236269
3 -> button3
237270
4 -> button4
238-
else -> throw IllegalStateException("Invalid correct button index")
271+
else -> null//throw IllegalStateException("Invalid button index")
239272
}
240273

241274

app/src/main/java/com/willowtreeapps/namegame/store/StartFragment.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class StartFragment : Fragment(), CoroutineScope, StartView {
1818
override val coroutineContext: CoroutineContext
1919
get() = Dispatchers.Main
2020

21-
var presenter: StartPresenter? = null
21+
private var presenter: StartPresenter? = null
2222

2323
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
2424
return inflater.inflate(R.layout.fragment_start, container, false)
@@ -39,7 +39,7 @@ class StartFragment : Fragment(), CoroutineScope, StartView {
3939

4040
override fun onPause() {
4141
super.onPause()
42-
NameGameApp.gameEngine().detachView(presenter!!)
42+
NameGameApp.gameEngine().detachView(this)
4343
}
4444

4545
override fun hideLoading() {
@@ -53,4 +53,10 @@ class StartFragment : Fragment(), CoroutineScope, StartView {
5353
loading_spinner.visibility = View.VISIBLE
5454
}
5555
}
56+
57+
override fun showError(msg: String) {
58+
activity?.runOnUiThread {
59+
txt_error.text = msg
60+
}
61+
}
5662
}

0 commit comments

Comments
 (0)