Skip to content

Commit

Permalink
feat(android): setup BottomSheetBehavior
Browse files Browse the repository at this point in the history
  • Loading branch information
lodev09 committed Mar 27, 2024
1 parent 9ea6253 commit f4aa908
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 20 deletions.
1 change: 1 addition & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,6 @@ dependencies {
implementation "com.facebook.react:react-native:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.google.android.material:material:1.11.0"
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.lodev09.truesheet

import android.util.Log
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.ScrollView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.bottomsheet.BottomSheetBehavior

class TrueSheetBottomSheetBehavior<T : ViewGroup>() : BottomSheetBehavior<T>() {

private fun isInsideSheet(scrollView: ScrollView, event: MotionEvent): Boolean {
val x = event.x
val y = event.y

val position = IntArray(2)
scrollView.getLocationOnScreen(position)

val nestedX = position[0]
val nestedY = position[1]

val boundRight = nestedX + scrollView.width
val boundBottom = nestedY + scrollView.height

return (x > nestedX && x < boundRight && y > nestedY && y < boundBottom) ||
event.action == MotionEvent.ACTION_CANCEL
}

// TODO: Pass scrollview explicitly here
override fun onInterceptTouchEvent(parent: CoordinatorLayout, child: T, event: MotionEvent): Boolean {
val isDownEvent = (event.actionMasked == MotionEvent.ACTION_DOWN)
val expanded = state == STATE_EXPANDED

if (isDownEvent && expanded){
val container = child.getChildAt(0) as ViewGroup
val content = (container.getChildAt(0) as ViewGroup).getChildAt(0) as ViewGroup

for(i in 0 until content.childCount){
val contentChild = content.getChildAt(i)
val scrolled = (contentChild is ScrollView && contentChild.scrollY > 0)

if(!scrolled) continue

if (isInsideSheet(contentChild as ScrollView, event)) {
return false
}
}
}

return super.onInterceptTouchEvent(parent, child, event)
}

companion object {
const val TAG = "TrueSheetView"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.lodev09.truesheet

import android.annotation.SuppressLint
import android.content.Context
import android.view.MotionEvent
import android.view.View
Expand Down Expand Up @@ -91,6 +92,7 @@ internal class TrueSheetRootViewGroup(context: Context?) : ReactViewGroup(contex
return super.onInterceptTouchEvent(event)
}

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
mJSTouchDispatcher.handleTouchEvent(event, mEventDispatcher)
mJSPointerDispatcher?.handleMotionEvent(event, mEventDispatcher, false)
Expand Down
54 changes: 39 additions & 15 deletions android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package com.lodev09.truesheet

import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.ViewStructure
import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import android.widget.RelativeLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.uimanager.PixelUtil
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.EventDispatcher
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog

class TrueSheetView(context: Context) : ViewGroup(context), LifecycleEventListener {
class TrueSheetView : ViewGroup, LifecycleEventListener {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

private val sheetRootView: TrueSheetRootViewGroup?
private var sheetDialog: BottomSheetDialog?

private lateinit var sheetBehavior: BottomSheetBehavior<*>
private lateinit var sheetBehavior: TrueSheetBottomSheetBehavior<ViewGroup>

private val reactContext: ThemedReactContext
get() = context as ThemedReactContext
Expand All @@ -29,6 +34,7 @@ class TrueSheetView(context: Context) : ViewGroup(context), LifecycleEventListen
reactContext.addLifecycleEventListener(this)
sheetRootView = TrueSheetRootViewGroup(context)
sheetDialog = BottomSheetDialog(context)
sheetBehavior = TrueSheetBottomSheetBehavior()
}

override fun dispatchProvideStructure(structure: ViewStructure) {
Expand Down Expand Up @@ -116,28 +122,46 @@ class TrueSheetView(context: Context) : ViewGroup(context), LifecycleEventListen
}

private fun setupSheetDialog(height: Int) {
val frameLayout = FrameLayout(context)
frameLayout.addView(sheetRootView)
val layout = RelativeLayout(context)
layout.addView(sheetRootView)

sheetDialog?.setContentView(layout)

sheetDialog?.setContentView(frameLayout)
val viewGroup = layout.parent as ViewGroup
val params = viewGroup.layoutParams as CoordinatorLayout.LayoutParams

sheetBehavior = BottomSheetBehavior.from(frameLayout.parent as View).apply {
sheetBehavior.apply {
addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
override fun onSlide(bottomSheet: View, slideOffset: Float) { }
override fun onStateChanged(bottomSheet: View, newState: Int) {
// TODO
when (newState) {
BottomSheetBehavior.STATE_HIDDEN -> {
dismiss()
}
BottomSheetBehavior.STATE_COLLAPSED -> {}
BottomSheetBehavior.STATE_DRAGGING -> {}
BottomSheetBehavior.STATE_EXPANDED -> {}
BottomSheetBehavior.STATE_HALF_EXPANDED -> {}
BottomSheetBehavior.STATE_SETTLING -> {}
}
}
})

// virtually disables 'third' breakpoint
isFitToContents = true
isFitToContents = false
halfExpandedRatio = 0.8f
isHideable = true
// skipCollapsed = true
peekHeight = height

Log.d(TAG, height.toString())

// TODO: Account for ScrollView content
peekHeight = 1652 // height
}

params.behavior = sheetBehavior
}

fun present() {
sheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
sheetDialog?.show()
}

Expand All @@ -146,7 +170,7 @@ class TrueSheetView(context: Context) : ViewGroup(context), LifecycleEventListen
}

companion object {
const val NAME = "TrueSheetView"
const val TAG = "TrueSheetView"
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.lodev09.truesheet

import android.view.LayoutInflater
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.ViewGroupManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

@ReactModule(name = TrueSheetViewModule.NAME)
@ReactModule(name = TrueSheetViewModule.TAG)
class TrueSheetViewModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String = NAME
override fun getName(): String = TAG

private fun withTrueSheetView(tag: Int, closure: (trueSheetView: TrueSheetView) -> Unit) {
UiThreadUtil.runOnUiThread {
try {
val manager = UIManagerHelper.getUIManagerForReactTag(reactApplicationContext, tag)
val view = manager?.resolveView(tag)
if (view == null) {
Log.d(NAME, "TrueSheetView with tag $tag not found")
Log.d(TAG, "TrueSheetView with tag $tag not found")
return@runOnUiThread
}

if (view is TrueSheetView) {
closure(view)
} else {
Log.d(NAME, "View is not of type TrueSheetView")
Log.d(TAG, "View is not of type TrueSheetView")
}
} catch (e: Exception) {
e.printStackTrace()
Expand All @@ -53,6 +53,6 @@ class TrueSheetViewModule(reactContext: ReactApplicationContext) : ReactContextB
}

companion object {
const val NAME = "TrueSheetView"
const val TAG = "TrueSheetView"
}
}

0 comments on commit f4aa908

Please sign in to comment.