Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

General Maintenance #311

Merged
merged 4 commits into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.github.ashutoshgngwr.noice

import android.view.View
import android.widget.SeekBar
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.test.espresso.Espresso.onView
Expand All @@ -13,6 +12,7 @@ import androidx.test.espresso.action.MotionEvents
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.espresso.util.TreeIterables
import com.github.ashutoshgngwr.noice.widget.DurationPicker
import com.google.android.material.slider.Slider
import com.google.android.material.textfield.TextInputLayout
import org.hamcrest.Description
import org.hamcrest.Matcher
Expand All @@ -26,9 +26,10 @@ import org.hamcrest.TypeSafeMatcher
object EspressoX {

/**
* [clickOn] performs a click action on item with the given [viewId].
* [clickInItem] performs a click action on item with the given [viewId] inside currently
* matched view.
*/
fun clickOn(@IdRes viewId: Int): ViewAction {
fun clickInItem(@IdRes viewId: Int): ViewAction {
return object : ViewAction {
override fun getDescription() = "Click on view with specified id"
override fun getConstraints() = hasDescendant(withId(viewId))
Expand All @@ -40,28 +41,28 @@ object EspressoX {
}

/**
* [seekProgress] performs a seek action on a [SeekBar] with given [seekBarId] inside currently
* [slideInItem] performs a slide action on a [Slider] with given [sliderID] inside currently
* matched view.
*/
fun seekProgress(@IdRes seekBarId: Int, progress: Int): ViewAction {
fun slideInItem(@IdRes sliderID: Int, value: Float): ViewAction {
return object : ViewAction {
override fun getDescription() = "Emulate user input on a seek bar"
override fun getConstraints() =
hasDescendant(allOf(withId(seekBarId), instanceOf(SeekBar::class.java)))
hasDescendant(allOf(withId(sliderID), instanceOf(Slider::class.java)))

override fun perform(uiController: UiController, view: View) {
val seekBar = view.findViewById<SeekBar>(seekBarId)
val width = seekBar.width - seekBar.paddingStart - seekBar.paddingEnd
val height = seekBar.height - seekBar.paddingTop - seekBar.paddingBottom
val slider = view.findViewById<Slider>(sliderID)
val height = slider.height - slider.paddingTop - slider.paddingBottom

val location = intArrayOf(0, 0)
seekBar.getLocationOnScreen(location)
slider.getLocationOnScreen(location)

val xOffset = location[0].toFloat() + seekBar.paddingStart
val xStart = ((seekBar.progress.toFloat() / seekBar.max) * width) + xOffset
val xOffset = location[0].toFloat() + slider.paddingStart + slider.trackSidePadding
val range = slider.valueTo - slider.valueFrom
val xStart = (((slider.value - slider.valueFrom) / range) * slider.trackWidth) + xOffset

val x = ((progress.toFloat() / seekBar.max) * width) + xOffset
val y = location[1] + seekBar.paddingTop + (height.toFloat() / 2)
val x = (((value - slider.valueFrom) / range) * slider.trackWidth) + xOffset
val y = location[1] + slider.paddingTop + (height.toFloat() / 2)

val startCoordinates = floatArrayOf(xStart, y)
val endCoordinates = floatArrayOf(x, y)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class PresetFragmentTest {
onView(withId(R.id.list_presets)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText("test"))),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

Expand All @@ -109,7 +109,7 @@ class PresetFragmentTest {
onView(withId(R.id.list_presets)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText("test"))),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

Expand All @@ -122,7 +122,7 @@ class PresetFragmentTest {
onView(withId(R.id.list_presets)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText("test"))),
EspressoX.clickOn(R.id.button_menu)
EspressoX.clickInItem(R.id.button_menu)
)
)

Expand Down Expand Up @@ -150,7 +150,7 @@ class PresetFragmentTest {
onView(withId(R.id.list_presets)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText("test"))),
EspressoX.clickOn(R.id.button_menu)
EspressoX.clickInItem(R.id.button_menu)
)
)

Expand All @@ -174,7 +174,7 @@ class PresetFragmentTest {
onView(withId(R.id.list_presets)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText("test"))),
EspressoX.clickOn(R.id.button_menu)
EspressoX.clickInItem(R.id.button_menu)
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class SoundLibraryFragmentTest {
onView(withId(R.id.list_sound)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText(R.string.birds))),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

Expand All @@ -73,15 +73,15 @@ class SoundLibraryFragmentTest {
onView(withId(R.id.list_sound)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText(R.string.birds))),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

verify(exactly = 1) { eventBus.post(MediaPlayerService.StopPlayerEvent("birds")) }
}

@Test
fun testRecyclerViewItem_volumeSeekBar() {
fun testRecyclerViewItem_volumeSlider() {
val mockPlayer = mockk<Player>(relaxed = true) {
every { volume } returns Player.DEFAULT_VOLUME
}
Expand All @@ -96,7 +96,7 @@ class SoundLibraryFragmentTest {
onView(withId(R.id.list_sound)).perform(
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText(R.string.birds))),
EspressoX.seekProgress(R.id.seekbar_volume, expectedVolume)
EspressoX.slideInItem(R.id.slider_volume, expectedVolume.toFloat())
)
)

Expand All @@ -105,7 +105,7 @@ class SoundLibraryFragmentTest {
}

@Test
fun testRecyclerViewItem_timePeriodSeekBar() {
fun testRecyclerViewItem_timePeriodSlider() {
val mockPlayer = mockk<Player>(relaxed = true) {
every { timePeriod } returns Player.DEFAULT_TIME_PERIOD
}
Expand All @@ -118,7 +118,8 @@ class SoundLibraryFragmentTest {
val expectedTimePeriods = arrayOf(
Player.MIN_TIME_PERIOD,
Player.MAX_TIME_PERIOD,
Random.nextInt(Player.MIN_TIME_PERIOD, Player.MAX_TIME_PERIOD)
// following because step size of the slider is 10s
Random.nextInt(Player.MIN_TIME_PERIOD / 10, Player.MAX_TIME_PERIOD / 10) * 10
)

for (expectedTimePeriod in expectedTimePeriods) {
Expand All @@ -129,9 +130,7 @@ class SoundLibraryFragmentTest {
),
RecyclerViewActions.actionOnItem<SoundLibraryFragment.ViewHolder>(
hasDescendant(allOf(withId(R.id.title), withText(R.string.rolling_thunder))),
EspressoX.seekProgress(
R.id.seekbar_time_period, expectedTimePeriod - Player.MIN_TIME_PERIOD
)
EspressoX.slideInItem(R.id.slider_time_period, expectedTimePeriod.toFloat())
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ class WakeUpTimerFragmentTest {
.check(matches(not(isEnabled())))
}

@Test
fun testSelectPreset_withoutSavedPresets() {
every { Preset.readAllFromUserPreferences(any()) } returns arrayOf()
onView(withId(R.id.button_select_preset))
.check(matches(withText(R.string.select_preset)))
.perform(click())

EspressoX.waitForView(withText(R.string.preset_info__description), 100, 5)
.check(matches(isDisplayed()))
}

@Test
fun testSetTimer() {
every { Preset.readAllFromUserPreferences(any()) } returns arrayOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,17 @@ class GenerateScreenshots {
onView(withId(R.id.list_sound)).perform(
actionOnItem<SoundLibraryFragment.ViewHolder>(
ViewMatchers.hasDescendant(allOf(withId(R.id.title), withText(R.string.airplane_inflight))),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

onView(withId(R.id.list_sound)).perform(
actionOnItem<SoundLibraryFragment.ViewHolder>(
ViewMatchers.hasDescendant(allOf(withId(R.id.title), withText(R.string.airplane_inflight))),
EspressoX.seekProgress(R.id.seekbar_volume, Player.MAX_VOLUME - Player.DEFAULT_VOLUME)
EspressoX.slideInItem(
R.id.slider_volume,
Player.MAX_VOLUME.toFloat() - Player.DEFAULT_VOLUME
)
)
)

Expand All @@ -170,7 +173,7 @@ class GenerateScreenshots {
ViewMatchers.hasDescendant(
allOf(withId(R.id.title), withText(R.string.airplane_seatbelt_beeps))
),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

Expand All @@ -179,7 +182,7 @@ class GenerateScreenshots {
ViewMatchers.hasDescendant(
allOf(withId(R.id.title), withText(R.string.airplane_seatbelt_beeps))
),
EspressoX.seekProgress(R.id.seekbar_volume, Player.MAX_VOLUME)
EspressoX.slideInItem(R.id.slider_volume, Player.MAX_VOLUME.toFloat())
)
)

Expand All @@ -188,9 +191,7 @@ class GenerateScreenshots {
ViewMatchers.hasDescendant(
allOf(withId(R.id.title), withText(R.string.airplane_seatbelt_beeps))
),
EspressoX.seekProgress(
R.id.seekbar_time_period, Player.MAX_TIME_PERIOD - Player.MIN_TIME_PERIOD - 30
)
EspressoX.slideInItem(R.id.slider_time_period, Player.MAX_TIME_PERIOD.toFloat() - 300)
)
)

Expand All @@ -210,7 +211,9 @@ class GenerateScreenshots {

EspressoX.waitForView(withId(R.id.list_presets), 100, 5)
.perform(
actionOnItemAtPosition<PresetFragment.ViewHolder>(1, EspressoX.clickOn(R.id.button_play))
actionOnItemAtPosition<PresetFragment.ViewHolder>(
1, EspressoX.clickInItem(R.id.button_play)
)
)

Thread.sleep(SLEEP_PERIOD_BEFORE_SCREENGRAB)
Expand All @@ -222,7 +225,7 @@ class GenerateScreenshots {
onView(withId(R.id.list_sound)).perform(
actionOnItem<SoundLibraryFragment.ViewHolder>(
ViewMatchers.hasDescendant(allOf(withId(R.id.title), withText(R.string.birds))),
EspressoX.clickOn(R.id.button_play)
EspressoX.clickInItem(R.id.button_play)
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
*/
const val EXTRA_CURRENT_NAVIGATED_FRAGMENT = "current_fragment"

private const val TAG = "MainActivity"
private const val PREF_APP_THEME = "app_theme"
private const val APP_THEME_LIGHT = 0
private const val APP_THEME_DARK = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.TextView
import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment
Expand All @@ -17,6 +16,7 @@ import com.github.ashutoshgngwr.noice.sound.Sound
import com.github.ashutoshgngwr.noice.sound.player.Player
import com.github.ashutoshgngwr.noice.sound.player.PlayerManager
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.slider.Slider
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_sound_list.view.*
import kotlinx.android.synthetic.main.layout_list_item__sound.view.*
Expand Down Expand Up @@ -166,18 +166,17 @@ class SoundLibraryFragment : Fragment(R.layout.fragment_sound_list) {
val sound = Sound.get(soundKey)
val isPlaying = players.containsKey(soundKey)
holder.itemView.title.text = context.getString(sound.titleResId)
holder.itemView.seekbar_volume.isEnabled = isPlaying
holder.itemView.seekbar_time_period.isEnabled = isPlaying
holder.itemView.slider_volume.isEnabled = isPlaying
holder.itemView.slider_time_period.isEnabled = isPlaying
holder.itemView.button_play.isChecked = isPlaying
if (isPlaying) {
requireNotNull(players[soundKey]).also {
holder.itemView.seekbar_volume.progress = it.volume
holder.itemView.seekbar_time_period.progress = it.timePeriod - Player.MIN_TIME_PERIOD
holder.itemView.slider_volume.value = it.volume.toFloat()
holder.itemView.slider_time_period.value = it.timePeriod.toFloat()
}
} else {
holder.itemView.seekbar_volume.progress = Player.DEFAULT_VOLUME
holder.itemView.seekbar_time_period.progress =
Player.DEFAULT_TIME_PERIOD - Player.MIN_TIME_PERIOD
holder.itemView.slider_volume.value = Player.DEFAULT_VOLUME.toFloat()
holder.itemView.slider_time_period.value = Player.DEFAULT_TIME_PERIOD.toFloat()
}

holder.itemView.layout_time_period.visibility = if (sound.isLooping) {
Expand All @@ -191,29 +190,23 @@ class SoundLibraryFragment : Fragment(R.layout.fragment_sound_list) {
inner class ViewHolder(view: View, @LayoutRes layoutID: Int) : RecyclerView.ViewHolder(view) {

// set listeners in holders to avoid object recreation on view recycle
private val seekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
private val sliderChangeListener = object : Slider.OnChangeListener {

override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
if (!fromUser) {
return
}

val player = players[dataSet[adapterPosition].data] ?: return
when (seekBar.id) {
R.id.seekbar_volume -> {
player.setVolume(progress)
when (slider.id) {
R.id.slider_volume -> {
player.setVolume(value.toInt())
}
R.id.seekbar_time_period -> {
player.timePeriod = Player.MIN_TIME_PERIOD + progress
R.id.slider_time_period -> {
player.timePeriod = value.toInt()
}
}
}

// unsubscribe from events during the seek action or it will cause adapter to refresh during
// the action causing adapterPosition to become -1 (POSITION_NONE). On resubscribing,
// this will also cause an update (#onPlayerManagerUpdate) since those events are sticky.
override fun onStartTrackingTouch(seekBar: SeekBar?) = unregisterFromEventBus()
override fun onStopTrackingTouch(seekBar: SeekBar?) = registerOnEventBus()
}

init {
Expand All @@ -223,10 +216,14 @@ class SoundLibraryFragment : Fragment(R.layout.fragment_sound_list) {
}

private fun initSoundItem(view: View) {
view.seekbar_volume.max = Player.MAX_VOLUME
view.seekbar_volume.setOnSeekBarChangeListener(seekBarChangeListener)
view.seekbar_time_period.max = Player.MAX_TIME_PERIOD - Player.MIN_TIME_PERIOD
view.seekbar_time_period.setOnSeekBarChangeListener(seekBarChangeListener)
view.slider_volume.valueFrom = 0.0f
view.slider_volume.valueTo = Player.MAX_VOLUME.toFloat()
view.slider_volume.addOnChangeListener(sliderChangeListener)
view.slider_volume.setLabelFormatter { "${(it * 100).toInt() / Player.MAX_VOLUME}%" }
view.slider_time_period.valueFrom = Player.MIN_TIME_PERIOD.toFloat()
view.slider_time_period.valueTo = Player.MAX_TIME_PERIOD.toFloat()
view.slider_time_period.addOnChangeListener(sliderChangeListener)
view.slider_time_period.setLabelFormatter { "${it.toInt() / 60}m ${it.toInt() % 60}s" }
view.button_play.setOnClickListener {
val listItem = dataSet.getOrNull(adapterPosition) ?: return@setOnClickListener
if (players.containsKey(listItem.data)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ class WakeUpTimerFragment : Fragment(R.layout.fragment_wake_up_timer) {
DialogFragment().show(childFragmentManager) {
val presets = Preset.readAllFromUserPreferences(requireContext()).map { it.name }
title(R.string.select_preset)
singleChoiceItems(presets.toTypedArray(), presets.indexOf(selectedPresetName)) { choice ->
selectedPresetName = presets[choice]
notifyUpdate()
if (presets.isNotEmpty()) {
singleChoiceItems(presets.toTypedArray(), presets.indexOf(selectedPresetName)) { choice ->
selectedPresetName = presets[choice]
notifyUpdate()
}
} else {
message(R.string.preset_info__description)
positiveButton(android.R.string.ok)
}
}
}
Expand Down
Loading