Skip to content

Commit

Permalink
refactor!: simpler state events
Browse files Browse the repository at this point in the history
  • Loading branch information
Arnaud de Mouhy committed Oct 17, 2020
1 parent daa8223 commit 745630a
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 109 deletions.
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@ RadioPlayer.radioURL('https://...');
RadioPlayer.stop();
RadioPlayer.play();

// State: loading, loadingFinished
// State: error, stopped, playing, paused, buffering
RadioPlayerEvents.addEventListener('stateDidChange', (event) => {
console.log(event.state);
});
// Playback State: playing, paused, stopped
RadioPlayerEvents.addEventListener('PlaybackStateDidChange', (event) => {
console.log(event.playbackState);
});
// Metadata: {"artistName": "Example Artist", "trackName": "Example Title"}
RadioPlayerEvents.addListener('MetadataDidChange', (metadata) => {
console.log(`Artist: ${metadata.artistName}`);
Expand Down
106 changes: 59 additions & 47 deletions android/src/main/java/com/reactnativeradioplayer/RadioPlayerModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,75 +13,87 @@ import com.google.android.exoplayer2.metadata.icy.IcyInfo
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.util.EventLogger

enum class PlayerState(val state: String) {
ERROR("error"),
STOPPED("stopped"),
PLAYING("playing"),
PAUSED("paused"),
BUFFERING("buffering"),
}

class RadioPlayerModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), Player.EventListener, MetadataOutput {

private val context = reactContext
private var player: SimpleExoPlayer = SimpleExoPlayer.Builder(reactContext).build()
private val context = reactContext
private var player: SimpleExoPlayer = SimpleExoPlayer.Builder(reactContext).build()
private var playbackState: Int = Player.STATE_IDLE
private var state: PlayerState? = null

override fun getName(): String {
return "RadioPlayer"
}
override fun getName(): String {
return "RadioPlayer"
}

init {
UiThreadUtil.runOnUiThread {
player.addAnalyticsListener(EventLogger(DefaultTrackSelector(this.context)))
player.addMetadataOutput(this)
player.setThrowsWhenUsingWrongThread(true)
player.setWakeMode(WAKE_MODE_NETWORK)
player.addListener(this)
}
init {
UiThreadUtil.runOnUiThread {
player.addAnalyticsListener(EventLogger(DefaultTrackSelector(this.context)))
player.addMetadataOutput(this)
player.setThrowsWhenUsingWrongThread(true)
player.setWakeMode(WAKE_MODE_NETWORK)
player.addListener(this)
}
}

@ReactMethod
fun radioURL(uri: String) {
UiThreadUtil.runOnUiThread {
val item: MediaItem = MediaItem.fromUri(uri)
player.setMediaItem(item)
//play()
}
@ReactMethod
fun radioURL(uri: String) {
UiThreadUtil.runOnUiThread {
val item: MediaItem = MediaItem.fromUri(uri)
player.setMediaItem(item)
//play()
}
}

@ReactMethod
fun play() {
UiThreadUtil.runOnUiThread {
if (player.isPlaying) {
player.stop()
}
player.prepare()
player.play()
@ReactMethod
fun play() {
UiThreadUtil.runOnUiThread {
if (player.isPlaying) {
player.stop()
}
player.prepare()
player.play()
}
}

@ReactMethod
fun stop() {
UiThreadUtil.runOnUiThread { player.stop() }
}
@ReactMethod
fun stop() {
UiThreadUtil.runOnUiThread { player.stop() }
}

override fun onPlaybackStateChanged(state: Int) {
var stateString = "unknown"
var playbackStateString = "unknown"
when (state) {
private fun computeAndSendStateEvent() {
val previousState = this.state

when (this.playbackState) {
Player.STATE_IDLE, Player.STATE_ENDED -> {
stateString = "loadingFinished"
playbackStateString = "stopped"
this.state = PlayerState.STOPPED
}
Player.STATE_BUFFERING -> {
stateString = "loading"
playbackStateString = "paused"
this.state = PlayerState.BUFFERING
}
Player.STATE_READY -> {
stateString = "loadingFinished"
playbackStateString = "playing"
this.state = PlayerState.PLAYING
}
}

if (this.state === null || this.state === previousState) {
return
}

val stateMap = WritableNativeMap()
stateMap.putString("state", stateString)
stateMap.putString("state", this.state!!.state)
sendEvent(this.context, "StateDidChange", stateMap)
}

val playbackStateMap = WritableNativeMap()
playbackStateMap.putString("playbackState", playbackStateString)
sendEvent(this.context, "PlaybackStateDidChange", playbackStateMap)
override fun onPlaybackStateChanged(state: Int) {
this.playbackState = state
computeAndSendStateEvent()
}

private fun sendEvent(reactContext: ReactContext,
Expand All @@ -97,7 +109,7 @@ class RadioPlayerModule(reactContext: ReactApplicationContext) : ReactContextBas
var artistName = "Unknown"
var trackName = "Unknown"
for (i in 1..metadata.length()) {
val entry: Metadata.Entry = metadata.get(i-1)
val entry: Metadata.Entry = metadata.get(i - 1)
if (entry is IcyInfo) {
if (entry.title != null) {
val parts: List<String> = entry.title!!.split(" - ")
Expand Down
4 changes: 2 additions & 2 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ android {
applicationId "com.example.reactnativeradioplayer"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
versionCode 2
versionName "0.2"
multiDexEnabled true
}
splits {
Expand Down
6 changes: 4 additions & 2 deletions example/ios/RadioPlayerExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -267,10 +267,11 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEAD_CODE_STRIPPING = NO;
INFOPLIST_FILE = RadioPlayerExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.2;
OTHER_CFLAGS = (
"$(inherited)",
"-DFB_SONARKIT_ENABLED=1",
Expand All @@ -295,9 +296,10 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
INFOPLIST_FILE = RadioPlayerExample/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.2;
OTHER_CFLAGS = (
"$(inherited)",
"-DFB_SONARKIT_ENABLED=1",
Expand Down
4 changes: 2 additions & 2 deletions example/ios/RadioPlayerExample/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
Expand Down
24 changes: 9 additions & 15 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,26 @@ import RadioPlayer, {
} from 'react-native-radio-player';

export default function App() {
const [playerState, setPlayerState] = React.useState<string | undefined>();
const [playerPlaybackState, setPlayerPlaybackState] = React.useState<string | undefined>();
const [playerState, setPlayerState] = React.useState<string>('stopped');
const [metadata, setMetadata] = React.useState<RadioPlayerMetadata>();

React.useEffect(() => {
RadioPlayerEvents.addListener('StateDidChange', (eventObject) => {
setPlayerState(eventObject.state);
});
RadioPlayerEvents.addListener('PlaybackStateDidChange', (eventObject) => {
setPlayerPlaybackState(eventObject.playbackState);
});
return () => {
RadioPlayerEvents.removeListener('StateDidChange', (eventObject) => {
setPlayerState(eventObject.state);
});
RadioPlayerEvents.removeListener(
'PlaybackStateDidChange',
(eventObject) => {
setPlayerPlaybackState(eventObject.playbackState);
}
);
};
}, []);

RadioPlayerEvents.addListener('MetadataDidChange', setMetadata);
React.useEffect(() => {
RadioPlayerEvents.addListener('MetadataDidChange', setMetadata);
return () => {
RadioPlayerEvents.addListener('MetadataDidChange', setMetadata);
};
}, []);

React.useEffect(() => {
RadioPlayer.radioURL('http://stream.fr.morow.com/morow_med.aacp');
Expand Down Expand Up @@ -59,17 +54,16 @@ export default function App() {
<Button
title="Play"
onPress={play}
disabled={playerPlaybackState === 'playing' ? true : false}
disabled={playerState === 'stopped' ? false : true}
/>
<Button
title="Stop"
onPress={stop}
disabled={playerPlaybackState === 'playing' ? false : true}
disabled={playerState === 'stopped' ? true : false}
/>
</View>
<View style={styles.container}>
<Text>State: {playerState}</Text>
<Text>PlaybackState: {playerPlaybackState}</Text>
</View>
</View>
);
Expand Down
87 changes: 53 additions & 34 deletions ios/RadioPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ class RadioPlayer: RCTEventEmitter, FRadioPlayerDelegate {
var hasListeners: Bool = false;
let player: FRadioPlayer
var radioURL: URL?

var playerState: FRadioPlayerState = .urlNotSet;
var playbackState: FRadioPlaybackState = .stopped;
var state: PlayerState = .stopped;

enum PlayerState: String {
case error = "error"
case stopped = "stopped"
case playing = "playing"
case paused = "paused"
case buffering = "buffering"
}

override init() {
player = FRadioPlayer.shared
Expand All @@ -15,14 +27,18 @@ class RadioPlayer: RCTEventEmitter, FRadioPlayerDelegate {
player.enableArtwork = false
player.delegate = self
}

@objc
override static func requiresMainQueueSetup() -> Bool {
return true
}

/// Base overide for RCTEventEmitter.
///
/// - Returns: all supported events
override func supportedEvents() -> [String] {
return [
"StateDidChange",
"PlaybackStateDidChange",
"MetadataDidChange"
]
}
Expand Down Expand Up @@ -52,51 +68,54 @@ class RadioPlayer: RCTEventEmitter, FRadioPlayerDelegate {
}
player.radioURL = radioURL
}

@objc(isPlaying:withRejecter:)
func isPlaying(resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Bool {
return player.isPlaying
}

@objc(stop:withRejecter:)
func stop(resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock) -> Void {
player.stop()
}

func radioPlayer(_ player: FRadioPlayer, playerStateDidChange state: FRadioPlayerState) {
var stateString: String? = nil
switch state {
case .error:
stateString = "error"
case .loading:
stateString = "loading"
case .loadingFinished:
stateString = "loadingFinished"
case .readyToPlay:
stateString = "readyToPlay"
case .urlNotSet:
stateString = "urlNotSet"
func computeAndSendStateEvent() {
let previousState = self.state

if (self.playerState == .error) {
self.state = .error
}
if (self.playerState == .urlNotSet) {
self.state = .error
}
if (self.playerState == .loading) {
self.state = .buffering
}
if (self.playbackState == .playing && self.playerState == .loadingFinished) {
self.state = .playing
}
if (self.playbackState == .paused && self.playerState == .readyToPlay) {
self.state = .paused
}
if (self.playbackState == .stopped && self.playerState == .loadingFinished) {
self.state = .stopped
}

print("\(self.playbackState.description) + \(self.playerState.description) = \(self.state)")
if self.state == previousState {
print("Same state as previously. Skipping sending event")
return
}
print("player \(player) player state did change to \(stateString ?? "Unknown")")
if (hasListeners) {
sendEvent(withName: "StateDidChange", body: ["state": stateString])
print("Sending \"\(self.state)\" event...")
let eventBody = ["state": self.state.rawValue]
sendEvent(withName: "StateDidChange", body: eventBody)
}
}

func radioPlayer(_ player: FRadioPlayer, playerStateDidChange state: FRadioPlayerState) {
self.playerState = state
computeAndSendStateEvent();
}

func radioPlayer(_ player: FRadioPlayer, playbackStateDidChange state: FRadioPlaybackState) {
var playbackStateString: String? = nil
switch state {
case .playing:
playbackStateString = "playing"
case .paused:
playbackStateString = "paused"
case .stopped:
playbackStateString = "stopped"
}
print("player \(player) playback state did change to \(playbackStateString ?? "Unknown")")
if (hasListeners) {
sendEvent(withName: "PlaybackStateDidChange", body: ["playbackState": playbackStateString])
}
self.playbackState = state;
computeAndSendStateEvent();
}

func radioPlayer(_ player: FRadioPlayer, metadataDidChange artistName: String?, trackName: String?) {
Expand Down
Loading

0 comments on commit 745630a

Please sign in to comment.