Skip to content
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
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,62 @@ jobs:
run: |
yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"

build-android-newarch:
runs-on: ubuntu-latest
env:
TURBO_CACHE_DIR: .turbo/android-newarch
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Setup
uses: ./.github/actions/setup

- name: Cache turborepo for Android new arch
uses: actions/cache@v3
with:
path: ${{ env.TURBO_CACHE_DIR }}
key: ${{ runner.os }}-turborepo-android-newarch-${{ hashFiles('yarn.lock') }}
restore-keys: |
${{ runner.os }}-turborepo-android-newarch-

- name: Check turborepo cache for Android new arch
run: |
TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android:fabric --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android:fabric').cache.status")

if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
echo "turbo_cache_hit=1" >> $GITHUB_ENV
fi

- name: Install JDK
if: env.turbo_cache_hit != 1
uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: '17'

- name: Finalize Android SDK
if: env.turbo_cache_hit != 1
run: |
/bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"

- name: Cache Gradle
if: env.turbo_cache_hit != 1
uses: actions/cache@v3
with:
path: |
~/.gradle/wrapper
~/.gradle/caches
key: ${{ runner.os }}-gradle-newarch-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-newarch-

- name: Build example for Android new arch
env:
JAVA_OPTS: "-XX:MaxHeapSize=6g"
run: |
yarn turbo run build:android:fabric --cache-dir="${{ env.TURBO_CACHE_DIR }}"

build-ios:
runs-on: macos-15
env:
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ dependencies {
if (isNewArchitectureEnabled()) {
react {
jsRootDir = file("../src/")
libraryName = "RCTTabView"
libraryName = "RNCTabView"
codegenJavaPackageName = "com.rcttabview"
}
}
81 changes: 81 additions & 0 deletions android/src/main/java/com/rcttabview/RCTTabViewImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.rcttabview

import android.content.res.ColorStateList
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.common.MapBuilder

data class TabInfo(
val key: String,
val title: String,
val badge: String,
val activeTintColor: Int?
)

class RCTTabViewImpl {
fun getName(): String {
return NAME
}

companion object {
const val NAME = "RNCTabView"
}

fun setItems(view: ReactBottomNavigationView, items: ReadableArray) {
val itemsArray = mutableListOf<TabInfo>()
for (i in 0 until items.size()) {
items.getMap(i).let { item ->
itemsArray.add(
TabInfo(
key = item.getString("key") ?: "",
title = item.getString("title") ?: "",
badge = item.getString("badge") ?: "",
activeTintColor = if (item.hasKey("activeTintColor")) item.getInt("activeTintColor") else null
)
)
}
}
view.updateItems(itemsArray)
}

fun setSelectedPage(view: ReactBottomNavigationView, key: String) {
view.items?.indexOfFirst { it.key == key }?.let {
view.selectedItemId = it
}
}

fun setLabeled(view: ReactBottomNavigationView, flag: Boolean?) {
view.setLabeled(flag)
}

fun setIcons(view: ReactBottomNavigationView, icons: ReadableArray?) {
view.setIcons(icons)
}

fun setBarTintColor(view: ReactBottomNavigationView, color: Int?) {
view.setBarTintColor(color)
}

fun setRippleColor(view: ReactBottomNavigationView, rippleColor: Int?) {
if (rippleColor != null) {
val color = ColorStateList.valueOf(rippleColor)
view.setRippleColor(color)
}
}

fun setActiveTintColor(view: ReactBottomNavigationView, color: Int?) {
view.setActiveTintColor(color)
}

fun setInactiveTintColor(view: ReactBottomNavigationView, color: Int?) {
view.setInactiveTintColor(color)
}

fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any>? {
return MapBuilder.of(
PageSelectedEvent.EVENT_NAME,
MapBuilder.of("registrationName", "onPageSelected"),
TabLongPressEvent.EVENT_NAME,
MapBuilder.of("registrationName", "onTabLongPress")
)
}
}
2 changes: 1 addition & 1 deletion android/src/main/java/com/rcttabview/RCTTabViewPackage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.ArrayList
class RCTTabViewPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
val viewManagers: MutableList<ViewManager<*, *>> = ArrayList()
viewManagers.add(RCTTabViewViewManager())
viewManagers.add(RCTTabViewManager(reactContext))
return viewManagers
}

Expand Down
87 changes: 87 additions & 0 deletions android/src/main/jni/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.13)
set(CMAKE_VERBOSE_MAKEFILE ON)

set(LIB_LITERAL RNCTabView)
set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL})

set(LIB_ANDROID_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
set(LIB_COMMON_DIR ${LIB_ANDROID_DIR}/../common/cpp)
set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/build/generated/source/codegen/jni)
set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})

add_compile_options(
-fexceptions
-frtti
-std=c++20
-Wall
-Wpedantic
-Wno-gnu-zero-variadic-macro-arguments
)

file(GLOB LIB_CUSTOM_SRCS CONFIGURE_DEPENDS *.cpp ${LIB_COMMON_DIR}/react/renderer/components/${LIB_LITERAL}/*.cpp)
file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS ${LIB_ANDROID_GENERATED_JNI_DIR}/*.cpp ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp)

add_library(
${LIB_TARGET_NAME}
SHARED
${LIB_CUSTOM_SRCS}
${LIB_CODEGEN_SRCS}
)

target_include_directories(
${LIB_TARGET_NAME}
PUBLIC
.
${LIB_COMMON_DIR}
${LIB_ANDROID_GENERATED_JNI_DIR}
${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
)

# https://github.com/react-native-community/discussions-and-proposals/discussions/816
# This if-then-else can be removed once this library does not support version below 0.76
if (REACTNATIVE_MERGED_SO)
target_link_libraries(
${LIB_TARGET_NAME}
fbjni
jsi
reactnative
)
else()
target_link_libraries(
${LIB_TARGET_NAME}
fbjni
folly_runtime
glog
jsi
react_codegen_rncore
react_debug
react_render_componentregistry
react_render_core
react_render_debug
react_render_graphics
react_render_imagemanager
react_render_mapbuffer
react_utils
react_nativemodule_core
rrc_image
turbomodulejsijni
rrc_view
yoga
)
endif()

target_compile_options(
${LIB_TARGET_NAME}
PRIVATE
-DLOG_TAG=\"ReactNative\"
-fexceptions
-frtti
-std=c++20
-Wall
)

target_include_directories(
${CMAKE_PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
13 changes: 13 additions & 0 deletions android/src/main/jni/RNCTabView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <ReactCommon/JavaTurboModule.h>
#include <ReactCommon/TurboModule.h>
#include <jsi/jsi.h>
#include <react/renderer/components/RNCTabView/RNCTabViewComponentDescriptor.h>

namespace facebook::react {
JSI_EXPORT
std::shared_ptr<TurboModule> RNCTabView_ModuleProvider(
const std::string &moduleName,
const JavaTurboModule::InitParams &params);
}
133 changes: 133 additions & 0 deletions android/src/newarch/RCTTabViewManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package com.rcttabview

import android.content.Context
import android.view.View
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.PixelUtil.toDIPFromPixel
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.viewmanagers.RNCTabViewManagerDelegate
import com.facebook.react.viewmanagers.RNCTabViewManagerInterface
import com.facebook.yoga.YogaMeasureMode
import com.facebook.yoga.YogaMeasureOutput


@ReactModule(name = RCTTabViewImpl.NAME)
class RCTTabViewManager(context: ReactApplicationContext) :
SimpleViewManager<ReactBottomNavigationView>(),
RNCTabViewManagerInterface<ReactBottomNavigationView> {

private val contextInner: ReactApplicationContext = context
private val delegate: RNCTabViewManagerDelegate<ReactBottomNavigationView, RCTTabViewManager> =
RNCTabViewManagerDelegate(this)
private val tabViewImpl: RCTTabViewImpl = RCTTabViewImpl()

override fun createViewInstance(context: ThemedReactContext): ReactBottomNavigationView {
val view = ReactBottomNavigationView(context)
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
view.onTabSelectedListener = { data ->
data.getString("key")?.let {
eventDispatcher?.dispatchEvent(PageSelectedEvent(viewTag = view.id, key = it))
}
}

view.onTabLongPressedListener = { data ->
data.getString("key")?.let {
eventDispatcher?.dispatchEvent(TabLongPressEvent(viewTag = view.id, key = it))
}
}
return view

}

override fun getName(): String {
return tabViewImpl.getName()
}

override fun setItems(view: ReactBottomNavigationView?, value: ReadableArray?) {
if (view != null && value != null)
tabViewImpl.setItems(view, value)
}

override fun setSelectedPage(view: ReactBottomNavigationView?, value: String?) {
if (view != null && value != null)
tabViewImpl.setSelectedPage(view, value)
}

override fun setIcons(view: ReactBottomNavigationView?, value: ReadableArray?) {
if (view != null)
tabViewImpl.setIcons(view, value)
}

override fun setLabeled(view: ReactBottomNavigationView?, value: Boolean) {
if (view != null)
tabViewImpl.setLabeled(view, value)
}

override fun setRippleColor(view: ReactBottomNavigationView?, value: Int?) {
if (view != null && value != null)
tabViewImpl.setRippleColor(view, value)
}

override fun setBarTintColor(view: ReactBottomNavigationView?, value: Int?) {
if (view != null && value != null)
tabViewImpl.setBarTintColor(view, value)
}

override fun setActiveTintColor(view: ReactBottomNavigationView?, value: Int?) {
if (view != null && value != null)
tabViewImpl.setActiveTintColor(view, value)
}

override fun setInactiveTintColor(view: ReactBottomNavigationView?, value: Int?) {
if (view != null && value != null)
tabViewImpl.setInactiveTintColor(view, value)
}

override fun getDelegate(): ViewManagerDelegate<ReactBottomNavigationView> {
return delegate
}

public override fun measure(
context: Context?,
localData: ReadableMap?,
props: ReadableMap?,
state: ReadableMap?,
width: Float,
widthMode: YogaMeasureMode?,
height: Float,
heightMode: YogaMeasureMode?,
attachmentsPositions: FloatArray?
): Long {
val view = ReactBottomNavigationView(context ?: contextInner)
val measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
view.measure(measureSpec, measureSpec)

return YogaMeasureOutput.make(
toDIPFromPixel(view.measuredWidth.toFloat()),
toDIPFromPixel(view.measuredHeight.toFloat())
)
}

// iOS Methods

override fun setTranslucent(view: ReactBottomNavigationView?, value: Boolean) {
}

override fun setIgnoresTopSafeArea(view: ReactBottomNavigationView?, value: Boolean) {
}

override fun setDisablePageAnimations(view: ReactBottomNavigationView?, value: Boolean) {
}

override fun setSidebarAdaptable(view: ReactBottomNavigationView?, value: Boolean) {
}

override fun setScrollEdgeAppearance(view: ReactBottomNavigationView?, value: String?) {
}
}
Loading
Loading