Skip to content

Commit

Permalink
NT-2141: Stepper component (#1344)
Browse files Browse the repository at this point in the history
  • Loading branch information
Arkariang committed Aug 4, 2021
1 parent 6fa2555 commit 18b5cdf
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 36 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.kickstarter.screenshoot.testing.ui.components
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.platform.app.InstrumentationRegistry
import com.karumi.shot.ScreenshotTest
import com.kickstarter.ApplicationComponent
import com.kickstarter.R
import com.kickstarter.screenshoot.testing.InstrumentedApp
import com.kickstarter.ui.views.Stepper
import org.junit.Before
import org.junit.Test

class StepperShotTest : ScreenshotTest {

lateinit var component: ApplicationComponent

@Before
fun setup() {
// - Test Application
val app =
InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as InstrumentedApp
// - Test Dagger component for injecting on environment Mock Objects
component = app.component()
}

@Test
fun stepperInitializationByDefaultTest() {
val stepper = (
LayoutInflater.from(InstrumentationRegistry.getInstrumentation().targetContext).inflate(
R.layout.item_stepper, null
) as ConstraintLayout
).findViewById(R.id.stepper) as Stepper

compareScreenshot(stepper)
}

@Test
fun stepperInitializeMaxMinInitialValue() {
val stepper = (
LayoutInflater.from(InstrumentationRegistry.getInstrumentation().targetContext).inflate(
R.layout.item_stepper, null
) as ConstraintLayout
).findViewById(R.id.stepper) as Stepper

stepper.inputs.setMinimum(1)
stepper.inputs.setMaximum(9)
stepper.inputs.setInitialValue(5)

compareScreenshot(stepper)
}

@Test
fun stepperHitMax() {
val stepper = (
LayoutInflater.from(InstrumentationRegistry.getInstrumentation().targetContext).inflate(
R.layout.item_stepper, null
) as ConstraintLayout
).findViewById(R.id.stepper) as Stepper

stepper.inputs.setMinimum(1)
stepper.inputs.setMaximum(9)
stepper.inputs.setInitialValue(9)

compareScreenshot(stepper)
}

@Test
fun stepperHitMin() {
val stepper = (
LayoutInflater.from(InstrumentationRegistry.getInstrumentation().targetContext).inflate(
R.layout.item_stepper, null
) as ConstraintLayout
).findViewById(R.id.stepper) as Stepper

stepper.inputs.setMinimum(1)
stepper.inputs.setMaximum(9)
stepper.inputs.setInitialValue(1)

compareScreenshot(stepper)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.kickstarter.ui.activities

import android.os.Bundle
import android.view.View
import com.kickstarter.databinding.PlaygroundLayoutBinding
import com.kickstarter.libs.BaseActivity
import com.kickstarter.libs.qualifiers.RequiresActivityViewModel
import com.kickstarter.ui.extensions.showSnackbar
import com.kickstarter.viewmodels.PlaygroundViewModel
import rx.android.schedulers.AndroidSchedulers

@RequiresActivityViewModel(PlaygroundViewModel.ViewModel::class)
class PlaygroundActivity : BaseActivity<PlaygroundViewModel.ViewModel?>() {
private lateinit var binding: PlaygroundLayoutBinding
private lateinit var view: View

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PlaygroundLayoutBinding.inflate(layoutInflater)
view = binding.root
setContentView(view)

setStepper()
}

/**
* Set up the stepper example
*/
private fun setStepper() {
binding.stepper.inputs.setMinimum(1)
binding.stepper.inputs.setMaximum(9)
binding.stepper.inputs.setInitialValue(5)
binding.stepper.inputs.setVariance(1)

binding.stepper.outputs.display()
.compose(bindToLifecycle())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
showSnackbar(binding.stepper, "The updated value on the display is: $it")
}
}
}
58 changes: 46 additions & 12 deletions app/src/internal/res/layout/playground_layout.xml
Original file line number Diff line number Diff line change
@@ -1,16 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="match_parent"
tools:context=".ui.activities.PlaygroundActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">

<com.kickstarter.ui.toolbars.KSToolbar
style="@style/Toolbar"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp">

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">

<include layout="@layout/playground_toolbar" />
<com.kickstarter.ui.views.IconButton
android:id="@+id/back_button"
style="@style/ToolbarIconBackButton" />

</LinearLayout>
<TextView
style="@style/ToolbarTitle"
android:text="@string/playground_playground" />
</RelativeLayout>

</ScrollView>
</com.kickstarter.ui.toolbars.KSToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/grid_8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/appBar">
<com.kickstarter.ui.views.Stepper
android:id="@+id/stepper"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
23 changes: 23 additions & 0 deletions app/src/main/java/com/kickstarter/ui/extensions/TextViewExt.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.kickstarter.ui.extensions

import android.text.Editable
import android.text.Selection
import android.text.Spannable
import android.text.SpannableString
import android.text.Spanned
import android.text.TextPaint
import android.text.TextWatcher
import android.text.method.LinkMovementMethod
import android.text.style.ClickableSpan
import android.text.style.URLSpan
Expand Down Expand Up @@ -75,3 +77,24 @@ fun TextView.urlSpanWithoutUnderlines() {
}
text = spannable
}

fun TextView.getUpdatedText() {
this.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
// TODO Auto-generated method stub
}

override fun beforeTextChanged(
s: CharSequence,
start: Int,
count: Int,
after: Int
) {
// TODO Auto-generated method stub
}

override fun afterTextChanged(s: Editable) {
// TODO Auto-generated method stub
}
})
}
141 changes: 141 additions & 0 deletions app/src/main/java/com/kickstarter/ui/views/Stepper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.kickstarter.ui.views

import android.content.Context
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import com.kickstarter.databinding.StepperBinding
import rx.Observable
import rx.subjects.PublishSubject

class Stepper @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) {
private var binding: StepperBinding = StepperBinding.inflate(LayoutInflater.from(context), this, true)

interface Inputs {
/**
* Increment value to add/substract, by default 1:
* Ie: if this value is set by 2, the increment/decrement showed on the display will change by 2
*/
fun setVariance(const: Int)

/**
* Min value allowed by the stepper, by default 0, when that value reached the decrease button will be disabled
*/
fun setMinimum(min: Int)

/**
* Max value allowed by the stepper, by default 10, when that value reached the increase button will be disabled
*/
fun setMaximum(max: Int)

/**
* The initial value to be displayed, by default 0. The value need to be within min-max range or it will use the minimum instead
*/
fun setInitialValue(firstVal: Int)
}

interface Outputs {
/**
* Observable that will emmit every time the displays changes with the value present on the display
*/
fun display(): Observable<Int>
}

private var variance: Int = 1
private var minimum: Int = 0
private var maximum: Int = 10
private var initialValue: Int = 0
set(value) {
field = if (value in minimum..maximum) {
value
} else minimum
updateDisplayUI(value)
}

private var displayAmount = PublishSubject.create<Int>()

val inputs: Inputs = object : Inputs {
override fun setVariance(const: Int) {
variance = const
}

override fun setMinimum(min: Int) {
minimum = min
}

override fun setMaximum(max: Int) {
maximum = max
}

override fun setInitialValue(firstVal: Int) {
initialValue = firstVal
}
}

val outputs: Outputs = object : Outputs {
override fun display(): Observable<Int> = displayAmount
}

init {
updateDisplayUI(initialValue)
setListenerForDecreaseButton()
setListenerForIncreaseButton()
setListenerForAmountChanged()
}

/**
* Check the amount is between the min-max range before updating the UI.
* If the amount is in a valid range it updates the UI with the new value
*/
private fun updateDisplayUI(amount: Int) {
if (amount in minimum..maximum) {
binding.stepperDisplay.text = amount.toString()
}

// - update enabled state for the buttons everytime the display changes
binding.decreaseQuantity.isEnabled = amount > minimum
binding.increaseQuantity.isEnabled = amount < maximum
}

private fun setListenerForIncreaseButton() {
binding.increaseQuantity.setOnClickListener {
updateDisplayUI(increase())
}
}

private fun getDisplayInt() = binding.stepperDisplay.text.toString().toInt()

private fun setListenerForDecreaseButton() {
binding.decreaseQuantity.setOnClickListener {
updateDisplayUI(decrease())
}
}

private fun setListenerForAmountChanged() {
binding.stepperDisplay.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
displayAmount.onNext(s.toString().toInt())
}

override fun beforeTextChanged(
s: CharSequence,
start: Int,
count: Int,
after: Int
) {
}

override fun afterTextChanged(s: Editable) {
}
})
}

private fun decrease() = getDisplayInt() - variance
private fun increase() = getDisplayInt() + variance
}
1 change: 0 additions & 1 deletion app/src/main/res/layout/item_comment_composer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@
app:composer_action_button_gone="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
Loading

0 comments on commit 18b5cdf

Please sign in to comment.