Skip to content

Commit 58e50f0

Browse files
authored
feat(android): add fabric support on Android (#608)
* feat: android wip * fix: add pager to build.gradle * fix: add codeGen to srcDirs * fix: codegen names * feat: add fabric implementation * fix: remove unused file * fix: commands on old arch
1 parent b9f0351 commit 58e50f0

18 files changed

+891
-234
lines changed

android/build.gradle

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
buildscript {
2+
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
3+
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['PagerView_kotlinVersion']
4+
25
repositories {
3-
google()
4-
mavenCentral()
6+
google()
7+
jcenter()
58
}
69

710
dependencies {
8-
classpath 'com.android.tools.build:gradle:3.5.3'
11+
classpath 'com.android.tools.build:gradle:4.2.1'
12+
// noinspection DifferentKotlinGradleVersion
13+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
914
}
1015
}
1116

@@ -14,6 +19,8 @@ def isNewArchitectureEnabled() {
1419
}
1520

1621
apply plugin: 'com.android.library'
22+
apply plugin: 'kotlin-android'
23+
1724

1825
if (isNewArchitectureEnabled()) {
1926
apply plugin: 'com.facebook.react'
@@ -52,7 +59,10 @@ android {
5259
sourceSets {
5360
main {
5461
if (isNewArchitectureEnabled()) {
55-
java.srcDirs += ['src/turbo']
62+
java.srcDirs += [
63+
"src/turbo",
64+
"${project.buildDir}/generated/source/codegen/java"
65+
]
5666
} else {
5767
java.srcDirs += ['src/legacy']
5868
}
@@ -130,17 +140,19 @@ repositories {
130140
}
131141
}
132142

143+
def kotlin_version = getExtOrDefault('kotlinVersion')
133144

134145
dependencies {
135146
//noinspection GradleDynamicVersion
136-
implementation "com.facebook.react:react-native:+"
137-
// From node_modules
147+
api "com.facebook.react:react-native:+"
148+
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
149+
implementation 'androidx.viewpager2:viewpager2:1.0.0'
138150
}
139151

140152
if (isNewArchitectureEnabled()) {
141153
react {
142-
jsRootDir = file("../src/")
143-
libraryName = "PagerViewView"
154+
jsRootDir = file("../src")
155+
libraryName = "RNCViewPager"
144156
codegenJavaPackageName = "com.reactnativepagerview"
145157
}
146158
}

android/src/legacy/com/reactnativepagerview/PagerViewViewManager.java

Lines changed: 0 additions & 61 deletions
This file was deleted.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package com.reactnativepagerview
2+
3+
import android.view.View
4+
import android.view.ViewGroup
5+
import androidx.viewpager2.widget.ViewPager2
6+
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
7+
import com.facebook.infer.annotation.Assertions
8+
import com.facebook.react.bridge.ReadableArray
9+
import com.facebook.react.common.MapBuilder
10+
import com.facebook.react.uimanager.PixelUtil
11+
import com.facebook.react.uimanager.ThemedReactContext
12+
import com.facebook.react.uimanager.UIManagerModule
13+
import com.facebook.react.uimanager.ViewGroupManager
14+
import com.facebook.react.uimanager.annotations.ReactProp
15+
import com.facebook.react.uimanager.events.EventDispatcher
16+
import com.reactnativepagerview.event.PageScrollEvent
17+
import com.reactnativepagerview.event.PageScrollStateChangedEvent
18+
import com.reactnativepagerview.event.PageSelectedEvent
19+
20+
21+
class PagerViewViewManager : ViewGroupManager<NestedScrollableHost>() {
22+
private lateinit var eventDispatcher: EventDispatcher
23+
24+
override fun getName(): String {
25+
return PagerViewViewManagerImpl.NAME
26+
}
27+
28+
override fun createViewInstance(reactContext: ThemedReactContext): NestedScrollableHost {
29+
val host = NestedScrollableHost(reactContext)
30+
host.id = View.generateViewId()
31+
host.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
32+
host.isSaveEnabled = false
33+
val vp = ViewPager2(reactContext)
34+
vp.adapter = ViewPagerAdapter()
35+
//https://github.com/callstack/react-native-viewpager/issues/183
36+
vp.isSaveEnabled = false
37+
eventDispatcher = reactContext.getNativeModule(UIManagerModule::class.java)!!.eventDispatcher
38+
39+
vp.post {
40+
vp.registerOnPageChangeCallback(object : OnPageChangeCallback() {
41+
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
42+
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
43+
eventDispatcher.dispatchEvent(
44+
PageScrollEvent(host.id, position, positionOffset))
45+
}
46+
47+
override fun onPageSelected(position: Int) {
48+
super.onPageSelected(position)
49+
eventDispatcher.dispatchEvent(
50+
PageSelectedEvent(host.id, position))
51+
}
52+
53+
override fun onPageScrollStateChanged(state: Int) {
54+
super.onPageScrollStateChanged(state)
55+
val pageScrollState: String = when (state) {
56+
ViewPager2.SCROLL_STATE_IDLE -> "idle"
57+
ViewPager2.SCROLL_STATE_DRAGGING -> "dragging"
58+
ViewPager2.SCROLL_STATE_SETTLING -> "settling"
59+
else -> throw IllegalStateException("Unsupported pageScrollState")
60+
}
61+
eventDispatcher.dispatchEvent(
62+
PageScrollStateChangedEvent(host.id, pageScrollState))
63+
}
64+
})
65+
66+
eventDispatcher.dispatchEvent(PageSelectedEvent(host.id, vp.currentItem))
67+
}
68+
host.addView(vp)
69+
return host
70+
}
71+
72+
override fun addView(host: NestedScrollableHost, child: View?, index: Int) {
73+
PagerViewViewManagerImpl.addView(host, child, index)
74+
}
75+
76+
override fun getChildCount(parent: NestedScrollableHost) = PagerViewViewManagerImpl.getChildCount(parent)
77+
78+
override fun getChildAt(parent: NestedScrollableHost, index: Int): View {
79+
return PagerViewViewManagerImpl.getChildAt(parent, index)
80+
}
81+
82+
override fun removeView(parent: NestedScrollableHost, view: View) {
83+
PagerViewViewManagerImpl.removeView(parent, view)
84+
}
85+
86+
override fun removeAllViews(parent: NestedScrollableHost) {
87+
PagerViewViewManagerImpl.removeAllViews(parent)
88+
}
89+
90+
override fun removeViewAt(parent: NestedScrollableHost, index: Int) {
91+
PagerViewViewManagerImpl.removeViewAt(parent, index)
92+
}
93+
94+
override fun needsCustomLayoutForChildren(): Boolean {
95+
return PagerViewViewManagerImpl.needsCustomLayoutForChildren()
96+
}
97+
98+
@ReactProp(name = "scrollEnabled", defaultBoolean = true)
99+
fun setScrollEnabled(host: NestedScrollableHost, value: Boolean) {
100+
PagerViewViewManagerImpl.setScrollEnabled(host, value)
101+
}
102+
103+
@ReactProp(name = "initialPage", defaultInt = 0)
104+
fun setInitialPage(host: NestedScrollableHost, value: Int) {
105+
PagerViewViewManagerImpl.setInitialPage(host, value)
106+
}
107+
108+
@ReactProp(name = "orientation")
109+
fun setOrientation(host: NestedScrollableHost, value: String) {
110+
PagerViewViewManagerImpl.setOrientation(host, value)
111+
}
112+
113+
@ReactProp(name = "offscreenPageLimit", defaultInt = ViewPager2.OFFSCREEN_PAGE_LIMIT_DEFAULT)
114+
operator fun set(host: NestedScrollableHost, value: Int) {
115+
PagerViewViewManagerImpl.setOffscreenPageLimit(host, value)
116+
}
117+
118+
@ReactProp(name = "overScrollMode")
119+
fun setOverScrollMode(host: NestedScrollableHost, value: String) {
120+
PagerViewViewManagerImpl.setOverScrollMode(host, value)
121+
}
122+
123+
@ReactProp(name = "layoutDirection")
124+
fun setLayoutDirection(host: NestedScrollableHost, value: String) {
125+
PagerViewViewManagerImpl.setLayoutDirection(host, value)
126+
}
127+
128+
override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Map<String, String>> {
129+
return MapBuilder.of(
130+
PageScrollEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageScroll"),
131+
PageScrollStateChangedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageScrollStateChanged"),
132+
PageSelectedEvent.EVENT_NAME, MapBuilder.of("registrationName", "onPageSelected"))
133+
}
134+
135+
override fun receiveCommand(root: NestedScrollableHost, commandId: String?, args: ReadableArray?) {
136+
super.receiveCommand(root, commandId, args)
137+
val view = PagerViewViewManagerImpl.getViewPager(root)
138+
Assertions.assertNotNull(view)
139+
Assertions.assertNotNull(args)
140+
val childCount = view.adapter?.itemCount
141+
142+
when (commandId) {
143+
COMMAND_SET_PAGE, COMMAND_SET_PAGE_WITHOUT_ANIMATION -> {
144+
val pageIndex = args!!.getInt(0)
145+
val canScroll = childCount != null && childCount > 0 && pageIndex >= 0 && pageIndex < childCount
146+
if (canScroll) {
147+
val scrollWithAnimation = commandId == COMMAND_SET_PAGE
148+
PagerViewViewManagerImpl.setCurrentItem(view, pageIndex, scrollWithAnimation)
149+
eventDispatcher.dispatchEvent(PageSelectedEvent(root.id, pageIndex))
150+
}
151+
}
152+
COMMAND_SET_SCROLL_ENABLED -> {
153+
view.isUserInputEnabled = args!!.getBoolean(0)
154+
}
155+
else -> throw IllegalArgumentException(String.format(
156+
"Unsupported command %d received by %s.",
157+
commandId,
158+
javaClass.simpleName))
159+
}
160+
}
161+
162+
@ReactProp(name = "pageMargin", defaultInt = 0)
163+
fun setPageMargin(host: NestedScrollableHost, margin: Int) {
164+
PagerViewViewManagerImpl.setPageMargin(host, margin)
165+
}
166+
167+
companion object {
168+
private const val COMMAND_SET_PAGE = "setPage"
169+
private const val COMMAND_SET_PAGE_WITHOUT_ANIMATION = "setPageWithoutAnimation"
170+
private const val COMMAND_SET_SCROLL_ENABLED = "setScrollEnabledImperatively"
171+
}
172+
}
173+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.reactnativepagerview
2+
3+
import android.content.Context
4+
import android.content.ContextWrapper
5+
import android.view.View
6+
import com.facebook.react.bridge.ReactContext
7+
8+
9+
class Helper {
10+
companion object {
11+
// https://github.com/facebook/react-native/blob/v0.64.2/ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerHelper.java#L138
12+
fun getReactContext(view: View): ReactContext? {
13+
var context: Context = view.getContext()
14+
if (context !is ReactContext && context is ContextWrapper) {
15+
context = context.baseContext
16+
}
17+
return if (context is ReactContext) context else null;
18+
}
19+
}
20+
21+
}

0 commit comments

Comments
 (0)