Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate GPHVideoView & GPHVideoManager to new arch #131

Merged
merged 2 commits into from
Jul 24, 2023
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
2 changes: 2 additions & 0 deletions android/registration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace facebook {
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
providerRegistry->add(
concreteComponentDescriptorProvider<RTNGiphyMediaViewComponentDescriptor>());
providerRegistry->add(
concreteComponentDescriptorProvider<RTNGiphyVideoViewComponentDescriptor>());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.giphy.sdk.ui.Giphy
import com.giphyreactnativesdk.utils.getVideoPlayerFactory
import com.giphyreactnativesdk.utils.initializeVideoCache

class RTNGiphySDKModule internal constructor(context: ReactApplicationContext) :
class RTNGiphySDKModuleImpl internal constructor(context: ReactApplicationContext) :
RTNGiphySdkModuleSpec(context) {
override fun getName() = NAME

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,44 @@ import com.facebook.react.uimanager.ViewManager
@Suppress("unused")
class RTNGiphySdkPackage : TurboReactPackage() {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
return if (name == RTNGiphySDKModule.NAME) {
RTNGiphySDKModule(reactContext)
} else {
null
return when (name) {
RTNGiphySDKModuleImpl.NAME -> {
RTNGiphySDKModuleImpl(reactContext)
}

RTNGiphyVideoManagerImpl.NAME -> {
RTNGiphyVideoManagerImpl(reactContext)
}

else -> {
null
}
}
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
val viewManagers: MutableList<ViewManager<*, *>> = ArrayList()
viewManagers.add(RTNGiphyMediaViewManager())
viewManagers.add(RTNGiphyVideoViewManager())
return viewManagers
}

override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
return ReactModuleInfoProvider {
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
moduleInfos[RTNGiphySDKModule.NAME] = ReactModuleInfo(
RTNGiphySDKModule.NAME,
RTNGiphySDKModule.NAME,
moduleInfos[RTNGiphySDKModuleImpl.NAME] = ReactModuleInfo(
RTNGiphySDKModuleImpl.NAME,
RTNGiphySDKModuleImpl.NAME,
false,
false,
true,
false,
isTurboModule
)
moduleInfos[RTNGiphyVideoManagerImpl.NAME] = ReactModuleInfo(
RTNGiphyVideoManagerImpl.NAME,
RTNGiphyVideoManagerImpl.NAME,
false,
false,
true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.giphyreactnativesdk

import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod

class RTNGiphyVideoManagerImpl internal constructor(context: ReactApplicationContext) :
RTNGiphyVideoManagerSpec(context) {
override fun getName() = NAME

@ReactMethod
override fun muteAll() {
SharedVideoPlayer.mute()
}

@ReactMethod
override fun pauseAll() {
SharedVideoPlayer.pause()
}

@ReactMethod
override fun resume() {
SharedVideoPlayer.resume()
}

companion object {
const val NAME = "RTNGiphyVideoManager"
}
}
180 changes: 180 additions & 0 deletions android/src/main/java/com/giphyreactnativesdk/RTNGiphyVideoView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package com.giphyreactnativesdk

import android.content.Context
import android.util.AttributeSet
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.events.Event
import com.giphy.sdk.core.GPHCore
import com.giphy.sdk.core.models.Media
import com.giphy.sdk.ui.utils.GPHAbstractVideoPlayer
import com.giphy.sdk.ui.utils.GPHPlayerStateListener
import com.giphy.sdk.ui.utils.GPHVideoPlayerState
import com.giphy.sdk.ui.views.GPHVideoPlayerView
import com.giphyreactnativesdk.events.OnErrorEvent
import com.giphyreactnativesdk.events.OnMuteEvent
import com.giphyreactnativesdk.events.OnPlaybackStateChangeEvent
import com.giphyreactnativesdk.events.OnUnmuteEvent
import com.giphyreactnativesdk.events.RTNGiphyVideoPlaybackState
import timber.log.Timber


class RTNGiphyVideoView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : GPHVideoPlayerView(context, attrs, defStyleAttr) {
private var autoPlay: Boolean = false
private var muted = false
private var rnStateSynchronized = false

init {
videoPlayer = SharedVideoPlayer.gphPlayer
}

private fun syncRNState() {
if (rnStateSynchronized) {
return
}

updateVolume()
rnStateSynchronized = true
}

private fun isViewPlayerActive(): Boolean {
return videoPlayer?.playerView == this
}

private fun updateVolume() {
if (!isViewPlayerActive()) {
return
}

if (muted) {
if (videoPlayer?.getVolume() != 0f) videoPlayer?.setVolume(0f)
} else {
if (videoPlayer?.getVolume() != 1f) videoPlayer?.setVolume(1f)
}
}

override fun didBecomeActiveByClick() {
super.didBecomeActiveByClick()
muted = false
}

override fun prepare(media: Media, player: GPHAbstractVideoPlayer) {
super.prepare(media, player)
videoPlayer?.addListener(playerListener)
}

fun setMedia(mediaId: String?) {
if (mediaId == null) {
return
}

GPHCore.gifById(mediaId) { result, e ->
val media = result?.data ?: return@gifById
preloadFirstFrame(media)
SharedVideoPlayer.gphPlayer.loadMedia(media, view = this, autoPlay = this.autoPlay)
e?.let {
Timber.d("Error while fetching GIF: %s", e.localizedMessage)
}
}
}

fun setMuted(rnMuted: Boolean?) {
if (rnMuted == muted) {
return
}
muted = rnMuted ?: false
updateVolume()
}

fun setAutoPlay(value: Boolean?) {
if (value == autoPlay) {
return
}
autoPlay = value ?: false
}

private val playerListener: GPHPlayerStateListener = {
if (isViewPlayerActive()) {
val surfaceId = UIManagerHelper.getSurfaceId(this)
when (it) {
is GPHVideoPlayerState.Ready -> {
syncRNState()
dispatchEvent(
OnPlaybackStateChangeEvent(
surfaceId,
this.id,
RTNGiphyVideoPlaybackState.ReadyToPlay
)
)
}

is GPHVideoPlayerState.Playing -> {
dispatchEvent(
OnPlaybackStateChangeEvent(
surfaceId,
this.id,
RTNGiphyVideoPlaybackState.Playing
)
)
}

is GPHVideoPlayerState.Error -> {
dispatchEvent(
OnErrorEvent(
surfaceId,
this.id, it.details
)
)
}

is GPHVideoPlayerState.MuteChanged -> {
if (it.muted) {
dispatchEvent(OnUnmuteEvent(surfaceId, this.id))
} else {
dispatchEvent(OnMuteEvent(surfaceId, this.id))
}
}

is GPHVideoPlayerState.Unknown -> dispatchEvent(
OnPlaybackStateChangeEvent(
surfaceId,
this.id,
RTNGiphyVideoPlaybackState.Unknown
)
)

else -> {
}
}

if (videoPlayer?.paused == true) {
dispatchEvent(
OnPlaybackStateChangeEvent(
surfaceId,
this.id,
RTNGiphyVideoPlaybackState.Paused
)
)
}
} else {
rnStateSynchronized = false
}
}

private fun dispatchEvent(event: Event<*>) {
val context = this.context as ThemedReactContext
UIManagerHelper.getEventDispatcherForReactTag(context, id)?.dispatchEvent(event)
}

override fun onDestroy() {
super.onDestroy()
if (isViewPlayerActive()) {
videoPlayer?.onPause()
}
videoPlayer?.removeListener(playerListener)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.giphyreactnativesdk

import com.facebook.react.common.MapBuilder
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.giphyreactnativesdk.events.OnErrorEvent
import com.giphyreactnativesdk.events.OnMuteEvent
import com.giphyreactnativesdk.events.OnPlaybackStateChangeEvent
import com.giphyreactnativesdk.events.OnUnmuteEvent

@Suppress("unused")
@ReactModule(name = RTNGiphyVideoViewManager.NAME)
class RTNGiphyVideoViewManager :
RTNGiphyVideoViewManagerSpec<RTNGiphyVideoView>() {
override fun getName() = NAME

override fun createViewInstance(context: ThemedReactContext): RTNGiphyVideoView {
return RTNGiphyVideoView(context)
}

@ReactProp(name = "autoPlay")
override fun setAutoPlay(view: RTNGiphyVideoView?, value: Boolean) {
view?.setAutoPlay(value)
}

@ReactProp(name = "muted")
override fun setMuted(view: RTNGiphyVideoView?, value: Boolean) {
view?.setMuted(value)
}

@ReactProp(name = "mediaId")
override fun setMediaId(view: RTNGiphyVideoView?, value: String?) {
view?.setMedia(value)
}

override fun getExportedCustomDirectEventTypeConstants(): MutableMap<String, Any> {
val export = super.getExportedCustomDirectEventTypeConstants() ?: MapBuilder.newHashMap()

export[OnPlaybackStateChangeEvent.EVENT_NAME] =
MapBuilder.of("registrationName", "onPlaybackStateChanged")
export[OnMuteEvent.EVENT_NAME] =
MapBuilder.of("registrationName", "onMuteEvent")
export[OnUnmuteEvent.EVENT_NAME] =
MapBuilder.of("registrationName", "onUnmuteEvent")
export[OnErrorEvent.EVENT_NAME] =
MapBuilder.of("registrationName", "onErrorEvent")

return export
}

companion object {
const val NAME = "RTNGiphyVideoView"
}
}
34 changes: 34 additions & 0 deletions android/src/main/java/com/giphyreactnativesdk/SharedVideoPlayer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.giphyreactnativesdk

import com.giphyreactnativesdk.videoplayeradapter.VideoPlayerAdapterImpl

object SharedVideoPlayer {
private val gphPlayerDelegate = lazy {
VideoPlayerAdapterImpl(null, repeatable = true)
}
val gphPlayer by gphPlayerDelegate

fun mute() {
if (gphPlayerDelegate.isInitialized() && gphPlayer.playerView != null) {
gphPlayer.runInPlayerApplicationLooper {
gphPlayer.setVolume(0f)
}
}
}

fun pause() {
if (gphPlayerDelegate.isInitialized() && gphPlayer.playerView != null) {
gphPlayer.runInPlayerApplicationLooper {
gphPlayer.onPause()
}
}
}

fun resume() {
if (gphPlayerDelegate.isInitialized() && gphPlayer.playerView != null) {
gphPlayer.runInPlayerApplicationLooper {
gphPlayer.onResume()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.giphyreactnativesdk.events

import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.WritableMap
import com.facebook.react.uimanager.events.Event

class OnErrorEvent
constructor(surfaceId: Int, viewId: Int, private val details: String) :
Event<OnErrorEvent>(surfaceId, viewId) {

override fun getEventName() = EVENT_NAME

override fun canCoalesce(): Boolean = false

override fun getCoalescingKey(): Short = 0

override fun getEventData(): WritableMap? {
val event = Arguments.createMap()
event.putString("details", details)
return event
}

companion object {
const val EVENT_NAME = "topErrorEvent"
}
}
Loading
Loading