Skip to content

Commit

Permalink
Add bindings for legacy ViewPager.
Browse files Browse the repository at this point in the history
  • Loading branch information
ychescale9 committed Apr 19, 2020
1 parent 896c220 commit ad35629
Show file tree
Hide file tree
Showing 19 changed files with 541 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ List of all bindings available:
* [AndroidX Preference bindings][flowbinding-preference]
* [AndroidX RecyclerView bindings][flowbinding-recyclerview]
* [AndroidX SwipeRefreshLayout bindings][flowbinding-swiperefreshlayout]
* [AndroidX ViewPager bindings][flowbinding-viewpager]
* [AndroidX ViewPager2 bindings][flowbinding-viewpager2]
* [Material Components bindings][flowbinding-material]

Expand Down Expand Up @@ -204,4 +205,5 @@ limitations under the License.
[flowbinding-preference]: flowbinding-preference/
[flowbinding-recyclerview]: flowbinding-recyclerview/
[flowbinding-swiperefreshlayout]: flowbinding-swiperefreshlayout/
[flowbinding-viewpager]: flowbinding-viewpager/
[flowbinding-viewpager2]: flowbinding-viewpager2/
1 change: 1 addition & 0 deletions buildSrc/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rootProject.ext.versions = [
fragment : '1.3.0-alpha03',
coordinatorLayout : '1.1.0',
recyclerView : '1.2.0-alpha02',
viewPager : '1.0.0',
viewPager2 : '1.1.0-alpha01',
swipeRefreshLayout: '1.1.0-rc01',
drawerLayout : '1.1.0-beta01',
Expand Down
21 changes: 21 additions & 0 deletions flowbinding-viewpager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# FlowBinding ViewPager

This module provides bindings for the **AndroidX ViewPager** library.

## Transitive Dependency

`androidx.viewpager:viewpager`

## Download

```groovy
implementation "io.github.reactivecircus.flowbinding:flowbinding-viewpager:${flowbinding_version}"
```

## Available Bindings

```kotlin
fun ViewPager.pageScrollEvents(): Flow<ViewPagerPageScrollEvent>
fun ViewPager.pageScrollStateChanges(): Flow<Int>
fun ViewPager.pageSelections(emitImmediately: Boolean = false): Flow<Int>
```
21 changes: 21 additions & 0 deletions flowbinding-viewpager/api/flowbinding-viewpager.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
public final class reactivecircus/flowbinding/viewpager/ViewPagerPageScrollEvent {
public fun <init> (Landroidx/viewpager/widget/ViewPager;IFI)V
public final fun getPosition ()I
public final fun getPositionOffset ()F
public final fun getPositionOffsetPixel ()I
public final fun getView ()Landroidx/viewpager/widget/ViewPager;
}

public final class reactivecircus/flowbinding/viewpager/ViewPagerPageScrollStateChangedFlowKt {
public static final fun pageScrollStateChanges (Landroidx/viewpager/widget/ViewPager;)Lkotlinx/coroutines/flow/Flow;
}

public final class reactivecircus/flowbinding/viewpager/ViewPagerPageScrolledFlowKt {
public static final fun pageScrollEvents (Landroidx/viewpager/widget/ViewPager;)Lkotlinx/coroutines/flow/Flow;
}

public final class reactivecircus/flowbinding/viewpager/ViewPagerPageSelectedFlowKt {
public static final fun pageSelections (Landroidx/viewpager/widget/ViewPager;Z)Lkotlinx/coroutines/flow/Flow;
public static synthetic fun pageSelections$default (Landroidx/viewpager/widget/ViewPager;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
}

35 changes: 35 additions & 0 deletions flowbinding-viewpager/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
plugins {
id 'flowbinding-plugin'
id 'com.android.library'
id 'kotlin-android'
id 'com.vanniktech.maven.publish'
id 'io.github.reactivecircus.firestorm'
id 'org.jetbrains.dokka'
}

afterEvaluate { project ->
project.tasks.dokka {
outputDirectory = "$rootDir/docs/api"
outputFormat = 'gfm'
}
}

android {
defaultConfig {
testApplicationId 'reactivecircus.flowbinding.viewpager.test'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

dependencies {
implementation project(':flowbinding-common')

implementation "androidx.viewpager:viewpager:${versions.androidx.viewPager}"
implementation "androidx.fragment:fragment:${versions.androidx.fragment}"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlinx.coroutines}"

lintChecks project(":lint-rules")

androidTestImplementation project(':testing-infra')
androidTestImplementation project(':flowbinding-viewpager:fixtures')
}
13 changes: 13 additions & 0 deletions flowbinding-viewpager/fixtures/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
id 'flowbinding-plugin'
id 'com.android.library'
id 'kotlin-android'
}

android.buildFeatures.viewBinding = true

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
implementation "androidx.viewpager:viewpager:${versions.androidx.viewPager}"
implementation "androidx.fragment:fragment:${versions.androidx.fragment}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="reactivecircus.flowbinding.viewpager.fixtures" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@file:Suppress("DEPRECATION")

package reactivecircus.flowbinding.viewpager.fixtures

import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import reactivecircus.flowbinding.viewpager.fixtures.databinding.FragmentPageItemBinding
import reactivecircus.flowbinding.viewpager.fixtures.databinding.FragmentViewpagerBinding

class ViewPagerFragment : Fragment(R.layout.fragment_viewpager) {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentViewpagerBinding.bind(view)
binding.viewPager.adapter = PagerAdapter(parentFragmentManager)
}
}

private val pages = listOf("1", "2", "3")

class PagerAdapter(
fragmentManager: FragmentManager
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getCount(): Int = pages.size

override fun getItem(position: Int): Fragment = PageItemFragment(
pages[position]
)
}

class PageItemFragment(private val pageTitle: String) : Fragment(R.layout.fragment_page_item) {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentPageItemBinding.bind(view)
binding.pageTitleTextView.text = pageTitle
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/pageTitleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />

</FrameLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
4 changes: 4 additions & 0 deletions flowbinding-viewpager/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
POM_ARTIFACT_ID=flowbinding-viewpager
POM_NAME=FlowBinding ViewPager
POM_DESCRIPTION=Kotlin Flow binding APIs for AndroidX ViewPager
POM_PACKAGING=aar
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package reactivecircus.flowbinding.viewpager

import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import androidx.viewpager.widget.ViewPager
import org.amshove.kluent.shouldEqual
import org.junit.Test
import reactivecircus.blueprint.testing.action.swipeLeftOnView
import reactivecircus.blueprint.testing.action.swipeRightOnView
import reactivecircus.flowbinding.testing.FlowRecorder
import reactivecircus.flowbinding.testing.launchTest
import reactivecircus.flowbinding.testing.recordWith
import reactivecircus.flowbinding.viewpager.fixtures.ViewPagerFragment
import reactivecircus.flowbinding.viewpager.test.R

@LargeTest
class ViewPagerPageScrollStateChangedFlowTest {

@Test
fun pageScrollStateChanges_swipe() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<Int>(testScope)
getViewById<ViewPager>(R.id.viewPager).pageScrollStateChanges().recordWith(recorder)

recorder.assertNoMoreValues()

swipeLeftOnView(R.id.viewPager)
recorder.takeValue() shouldEqual ViewPager.SCROLL_STATE_DRAGGING
recorder.takeValue() shouldEqual ViewPager.SCROLL_STATE_SETTLING
recorder.takeValue() shouldEqual ViewPager.SCROLL_STATE_IDLE
recorder.assertNoMoreValues()

cancelTestScope()

swipeRightOnView(R.id.viewPager)
recorder.assertNoMoreValues()
}
}

@Test
fun pageScrollStateChanges_programmatic() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<Int>(testScope)
val viewPager = getViewById<ViewPager>(R.id.viewPager)
viewPager.pageScrollStateChanges().recordWith(recorder)

recorder.assertNoMoreValues()

runOnUiThread { viewPager.currentItem = 1 }
// SCROLL_STATE_DRAGGING state is not emitted for programmatic page change
recorder.takeValue() shouldEqual ViewPager.SCROLL_STATE_SETTLING
recorder.takeValue() shouldEqual ViewPager.SCROLL_STATE_IDLE
recorder.assertNoMoreValues()

cancelTestScope()

runOnUiThread { viewPager.currentItem = 0 }
recorder.assertNoMoreValues()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package reactivecircus.flowbinding.viewpager

import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.viewpager.widget.ViewPager
import org.amshove.kluent.shouldBeGreaterThan
import org.amshove.kluent.shouldEqual
import org.junit.Test
import reactivecircus.blueprint.testing.action.swipeLeftOnView
import reactivecircus.blueprint.testing.action.swipeRightOnView
import reactivecircus.flowbinding.testing.FlowRecorder
import reactivecircus.flowbinding.testing.launchTest
import reactivecircus.flowbinding.testing.recordWith
import reactivecircus.flowbinding.viewpager.fixtures.ViewPagerFragment
import reactivecircus.flowbinding.viewpager.test.R

@LargeTest
class ViewPagerPageScrolledFlowTest {

@Test
fun pageScrollEvents_swipe() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<ViewPagerPageScrollEvent>(testScope)
val viewPager = getViewById<ViewPager>(R.id.viewPager)
viewPager.pageScrollEvents().recordWith(recorder)

recorder.assertNoMoreValues()

swipeLeftOnView(R.id.viewPager)
val event = recorder.takeValue()
event.view shouldEqual viewPager
event.position shouldEqual 0
event.positionOffset shouldBeGreaterThan 0f
event.positionOffsetPixel shouldBeGreaterThan 0

cancelTestScope()
recorder.clearValues()

swipeRightOnView(R.id.viewPager)
recorder.assertNoMoreValues()
}
}

@Test
fun pageScrollEvents_programmatic() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<ViewPagerPageScrollEvent>(testScope)
val viewPager = getViewById<ViewPager>(R.id.viewPager)
viewPager.pageScrollEvents().recordWith(recorder)

recorder.assertNoMoreValues()

runOnUiThread { viewPager.currentItem = 1 }
getInstrumentation().waitForIdleSync()
val event = recorder.takeValue()
event.view shouldEqual viewPager
event.position shouldEqual 0
event.positionOffset shouldBeGreaterThan 0f
event.positionOffsetPixel shouldBeGreaterThan 0

cancelTestScope()
recorder.clearValues()

runOnUiThread { viewPager.currentItem = 0 }
getInstrumentation().waitForIdleSync()
recorder.assertNoMoreValues()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package reactivecircus.flowbinding.viewpager

import androidx.test.filters.LargeTest
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import androidx.viewpager.widget.ViewPager
import org.amshove.kluent.shouldEqual
import org.junit.Test
import reactivecircus.blueprint.testing.action.swipeLeftOnView
import reactivecircus.blueprint.testing.action.swipeRightOnView
import reactivecircus.flowbinding.testing.FlowRecorder
import reactivecircus.flowbinding.testing.launchTest
import reactivecircus.flowbinding.testing.recordWith
import reactivecircus.flowbinding.viewpager.fixtures.ViewPagerFragment
import reactivecircus.flowbinding.viewpager.test.R

@LargeTest
class ViewPagerPageSelectedFlowTest {

@Test
fun pageSelections_swipe() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<Int>(testScope)
getViewById<ViewPager>(R.id.viewPager).pageSelections().recordWith(recorder)

recorder.assertNoMoreValues()

swipeLeftOnView(R.id.viewPager)
recorder.takeValue() shouldEqual 1
recorder.assertNoMoreValues()

cancelTestScope()

swipeRightOnView(R.id.viewPager)
recorder.assertNoMoreValues()
}
}

@Test
fun pageSelections_programmatic() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<Int>(testScope)
val viewPager = getViewById<ViewPager>(R.id.viewPager)
viewPager.pageSelections().recordWith(recorder)

recorder.assertNoMoreValues()

runOnUiThread { viewPager.currentItem = 1 }
recorder.takeValue() shouldEqual 1
recorder.assertNoMoreValues()

cancelTestScope()

runOnUiThread { viewPager.currentItem = 0 }
recorder.assertNoMoreValues()
}
}

@Test
fun pageSelections_emitImmediately() {
launchTest<ViewPagerFragment> {
val recorder = FlowRecorder<Int>(testScope)
val viewPager = getViewById<ViewPager>(R.id.viewPager)
viewPager.pageSelections(emitImmediately = true).recordWith(recorder)

recorder.takeValue() shouldEqual 0
recorder.assertNoMoreValues()

runOnUiThread { viewPager.currentItem = 1 }
recorder.takeValue() shouldEqual 1
recorder.assertNoMoreValues()

cancelTestScope()

runOnUiThread { viewPager.currentItem = 0 }
recorder.assertNoMoreValues()
}
}
}
1 change: 1 addition & 0 deletions flowbinding-viewpager/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<manifest package="reactivecircus.flowbinding.viewpager" />
Loading

0 comments on commit ad35629

Please sign in to comment.