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
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ import com.swmansion.rnscreens.bottomsheet.useSingleDetent
import com.swmansion.rnscreens.bottomsheet.useThreeDetents
import com.swmansion.rnscreens.bottomsheet.useTwoDetents
import com.swmansion.rnscreens.bottomsheet.usesFormSheetPresentation
import com.swmansion.rnscreens.events.ScreenAnimationDelegate
import com.swmansion.rnscreens.events.ScreenDismissedEvent
import com.swmansion.rnscreens.events.ScreenEventDelegate
import com.swmansion.rnscreens.events.ScreenEventEmitter
import com.swmansion.rnscreens.ext.recycle
import com.swmansion.rnscreens.transition.ExternalBoundaryValuesEvaluator
import com.swmansion.rnscreens.utils.DeviceUtils
Expand Down Expand Up @@ -366,7 +367,14 @@ class ScreenStackFragment :
}
animatorSet.play(alphaAnimator).with(slideAnimator)
}
animatorSet.addListener(ScreenEventDelegate(this))
animatorSet.addListener(
ScreenAnimationDelegate(
this,
ScreenEventEmitter(this.screen),
if (enter) ScreenAnimationDelegate.AnimationType.ENTER
else ScreenAnimationDelegate.AnimationType.EXIT
)
)
return animatorSet
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.swmansion.rnscreens.events

import android.animation.Animator
import android.util.Log
import com.swmansion.rnscreens.ScreenStackFragmentWrapper

// The goal is to make this universal delegate for handling animation progress related logic.
// At this moment this class works only with form sheet presentation.
class ScreenAnimationDelegate(
private val wrapper: ScreenStackFragmentWrapper,
private val eventEmitter: ScreenEventEmitter?,
private val animationType: AnimationType,
) : Animator.AnimatorListener {
enum class AnimationType {
ENTER,
EXIT
}

private var currentState: LifecycleState = LifecycleState.INITIALIZED

private fun progressState() {
currentState =
when (currentState) {
LifecycleState.INITIALIZED -> LifecycleState.START_DISPATCHED
LifecycleState.START_DISPATCHED -> LifecycleState.END_DISPATCHED
LifecycleState.END_DISPATCHED -> LifecycleState.END_DISPATCHED
}
}

override fun onAnimationStart(animation: Animator) {
if (currentState === LifecycleState.INITIALIZED) {
progressState()

// These callbacks do not work as expected from this call site, TODO: investigate it.
// To fix it quickly we emit required events manually
// wrapper.onViewAnimationStart()

when (animationType) {
AnimationType.ENTER -> eventEmitter?.dispatchOnWillAppear()
AnimationType.EXIT -> eventEmitter?.dispatchOnWillDisappear()
}

val isExitAnimation = animationType === AnimationType.EXIT
eventEmitter?.dispatchTransitionProgress(
0.0f,
isExitAnimation,
isExitAnimation,
)
}
}

override fun onAnimationEnd(animation: Animator) {
if (currentState === LifecycleState.START_DISPATCHED) {
progressState()
animation.removeListener(this)

// wrapper.onViewAnimationEnd()

when (animationType) {
AnimationType.ENTER -> eventEmitter?.dispatchOnAppear()
AnimationType.EXIT -> eventEmitter?.dispatchOnDisappear()
}

val isExitAnimation = animationType === AnimationType.EXIT
eventEmitter?.dispatchTransitionProgress(
1.0f,
isExitAnimation,
isExitAnimation,
)

wrapper.screen.endRemovalTransition()
}
}

override fun onAnimationCancel(animation: Animator) = Unit

override fun onAnimationRepeat(animation: Animator) = Unit

private enum class LifecycleState {
INITIALIZED,
START_DISPATCHED,
END_DISPATCHED,
}

companion object {
const val TAG = "ScreenEventDelegate"
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.swmansion.rnscreens.events

import com.facebook.react.uimanager.UIManagerHelper
import com.swmansion.rnscreens.Screen
import com.swmansion.rnscreens.ScreenFragment

// TODO: Consider taking weak ref here or accepting screen as argument in every method
// to avoid reference cycle.
class ScreenEventEmitter(val screen: Screen) {
val reactEventDispatcher
get() = screen.reactEventDispatcher

val reactSurfaceId
get() = UIManagerHelper.getSurfaceId(screen)

fun dispatchOnWillAppear() =
reactEventDispatcher?.dispatchEvent(ScreenWillAppearEvent(reactSurfaceId, screen.id))

fun dispatchOnAppear() =
reactEventDispatcher?.dispatchEvent(ScreenAppearEvent(reactSurfaceId, screen.id))

fun dispatchOnWillDisappear() =
reactEventDispatcher?.dispatchEvent(ScreenWillDisappearEvent(reactSurfaceId, screen.id))

fun dispatchOnDisappear() =
reactEventDispatcher?.dispatchEvent(ScreenDisappearEvent(reactSurfaceId, screen.id))

fun dispatchOnDismissed() =
reactEventDispatcher?.dispatchEvent(ScreenDismissedEvent(reactSurfaceId, screen.id))

fun dispatchTransitionProgress(progress: Float, isExitAnimation: Boolean, isGoingForward: Boolean) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure of isGoingForward semantics here. This is something to retrospect later.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created ticket on board for this

val sanitizedProgress = progress.coerceIn(0.0f, 1.0f)
val coalescingKey = ScreenFragment.getCoalescingKey(sanitizedProgress)
reactEventDispatcher?.dispatchEvent(ScreenTransitionProgressEvent(reactSurfaceId, screen.id, sanitizedProgress, isExitAnimation, isGoingForward, coalescingKey))
}
}
Loading