tlenclos / react-native-audio-streaming

iOS & Android react native module to play an audio stream, with background support and media controls
MIT License
781 stars 255 forks source link

[ANDROID] App Crashing In Combination with React Native Navigation #117

Open SydneyTR opened 6 years ago

SydneyTR commented 6 years ago

I am getting an error on Android (Google Pixel SDK25 8.0.0) when I run an app app that has React Native Navigation in combination with React Native Audio Streaming.

I get the following error:

09-30 16:41:40.535 3828-3828/? E/AndroidRuntime: FATAL EXCEPTION: main Process: com.xm, PID: 3828 java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Class java.lang.Object.getClass()' on a null object reference at com.audioStreaming.ReactNativeAudioStreamingModule.getClassActivity(ReactNativeAudioStreamingModule.java:43) at com.audioStreaming.Signal.setData(Signal.java:66) at com.audioStreaming.ReactNativeAudioStreamingModule.onServiceConnected(ReactNativeAudioStreamingModule.java:78) at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1453) at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1481) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

The MainApplication.java is build up like this:

` package com.xm;

import android.app.Application;

import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; import com.facebook.react.shell.MainReactPackage; import com.facebook.soloader.SoLoader; import com.reactnativenavigation.NavigationApplication; import com.audioStreaming.ReactNativeAudioStreamingPackage;

import java.util.Arrays; import java.util.List;

public class MainApplication extends NavigationApplication {

@Override
public boolean isDebug() {
    // Make sure you are using BuildConfig from your own application
    return BuildConfig.DEBUG;
}

protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new ReactNativeAudioStreamingPackage()
    );
}

@Override
public List<ReactPackage> createAdditionalReactPackages() {
    return getPackages();
}

} `

When I remove the line new ReactNativeAudioStreamingPackage() the app will work. What am i doing wrong here or how can i fix it?

bbdroid commented 6 years ago

I am getting this issue too. :( Is there any fixes for this?

BunHouth commented 6 years ago

i also get the same problem. I have solve it by

android/src/main/java/com/audioStreaming/ReactNativeAudioStreamingModule.java

public Class<?> getClassActivity() {
 -    if (this.clsActivity == null) {
 -      this.clsActivity = getCurrentActivity().getClass();
 +    Activity activity = getCurrentActivity();
 +    if (this.clsActivity == null && activity != null) {
 +        this.clsActivity = activity.getClass();
      }
      return this.clsActivity;
    }
callmejm commented 6 years ago

I have same issue , anyone solved it ?

mrsydster commented 6 years ago

@JayricMok: I solved my problem by applying the fixed code from from @BunHouth in;

android/src/main/java/com/audioStreaming/ReactNativeAudioStreamingModule.java

public Class<?> getClassActivity() {
 -    if (this.clsActivity == null) {
 -      this.clsActivity = getCurrentActivity().getClass();
 +    Activity activity = getCurrentActivity();
 +    if (this.clsActivity == null && activity != null) {
 +        this.clsActivity = activity.getClass();
      }
      return this.clsActivity;
    }
bbdroid commented 6 years ago

But this breaks control panel on notification.

callmejm commented 6 years ago

@mrsydster @BunHouth I applied , still having same issue

BunHouth commented 6 years ago

@JayricMok can you show me your issue.

callmejm commented 6 years ago

@BunHouth i facing this issue https://github.com/tlenclos/react-native-audio-streaming/issues/122

BunHouth commented 6 years ago

@JayricMok Are you also using wix-react-native-navigation ? if you not using react-native-navigation it should be work without any error. is your project is available on github?.

callmejm commented 6 years ago

@BunHouth i using react-navigation https://reactnavigation.org/docs/intro/

BunHouth commented 6 years ago

@JayricMok it should not problem with that navigation. can you show me your error?

mrsydster commented 6 years ago

@bdavid68 @JayricMok: Ahh the reason I am not getting that issue is because I disabled the notification from Audio Streaming. I am using Music Control instead for the notification control.

callmejm commented 6 years ago

@mrsydster can you explain more about how you mix the music control with audio streaming ?

SydneyTR commented 6 years ago

I use Audio Streaming for steaming the audio (obviously) and than I replaced the notifications control with Music Control. This is my code for the player:

/** @flow */

import React from 'react';
import Reflux from 'reflux';
import {
  Image,
  StyleSheet,
  ActivityIndicator,
  View,
  Text,
  DeviceEventEmitter,
  TouchableOpacity,
  Platform,
} from 'react-native';
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';

import { ReactNativeAudioStreaming } from 'react-native-audio-streaming';
import MusicControl from 'react-native-music-control';

export default class Player extends Reflux.Component {
  _onPress: Function;
  _initializeMusicControl: Function;
  _updateMusicControlTitle: Function;
  _updateMusicControlState: Function;
  _playerStop: Function;
  _playerStart: Function;
  subscription: Object;

  constructor(props: Object) {
    super(props);
    this.state = {
      streamUrl: "[INSERT YOUR STREAMING URL HERE]",
      status: "STOPPED",
      artist: "",
      song: "",
      onair: "",
    };
    this._onPress = this._onPress.bind(this);
  }
  componentDidMount() {
    this.subscription = DeviceEventEmitter.addListener(
      'AudioBridgeEvent', (evt) => {
        // We just want meta update for song name
        if (evt.status === "METADATA_UPDATED" && evt.key === 'StreamTitle') {            
            this.setState({
              onair: evt.value,
              artist: evt.value.split(' - ')[0],
              song: evt.value.split(' - ')[1],
            });
        } else if (evt.status != "METADATA_UPDATED") {
            this.setState(evt);
        }
      }
    );
    ReactNativeAudioStreaming.getStatus((error, msg) => {
      (error) ? console.log(error) : this.setState({status: msg.status})
    });
    this._initializeMusicControl();
  }
  _initializeMusicControl() {
    MusicControl.enableBackgroundMode(true)
    MusicControl.enableControl('closeNotification', true, {when: 'never'})
    MusicControl.on('play', this._playerStart.bind(this))
    MusicControl.on('pause', this._playerStop.bind(this))
    this._updateMusicControlTitle(true)
    this._updateMusicControlState()
  }

  _updateMusicControlTitle(initial) {
    // Android does not support changing title via MusicControl.updatePlayback,
    // so we need to send all information via setNowPlaying every time.
    if (Platform.OS === 'android' || initial) {
      MusicControl.setNowPlaying({
        artist: this.state.artist,
        title: this.state.song, 
        artwork: Platform.select({
          // https://github.com/tanguyantoine/react-native-music-control/issues/46
          ios: resolveAssetSource(require('../images/artwork.png')).uri,
          android: require('../images/artwork.png'),
        }),
        // iOS determines playback state depending on 'speed', so we need to set
        // it in the initial setNowPlaying for correct initial state to be set.
        speed: this._musicControlState() == MusicControl.STATE_STOPPED ? 0 : 1,
      });
    } else {
      MusicControl.updatePlayback({
        artist: this.state.artist,
        title: this.state.song,
      });
    }
  }
  _musicControlState() {
    switch (this.state.status) {
      case "PLAYING":
      case "STREAMING":
        return MusicControl.STATE_PLAYING;
      case "BUFFERING":
        return MusicControl.STATE_BUFFERING;
    }
    return MusicControl.STATE_STOPPED;
  }

  _updateMusicControlState() {
    state = this._musicControlState();
    if (state == MusicControl.STATE_STOPPED) {
      MusicControl.enableControl('play', true);
      MusicControl.enableControl('pause', false);
    } else {
      MusicControl.enableControl('play', false);
      MusicControl.enableControl('pause', true);
    }
    MusicControl.updatePlayback({state: state});
  }

  componentDidUpdate(prevProps: Object, prevState: Object) {
    if (prevState.song != this.state.song) {
      this._updateMusicControlTitle(false);
    }
    if (prevState.status != this.state.status) {
      this._updateMusicControlState();
    }
  }

  componentWillUnmount() {
    this.subscription.remove();
    MusicControl.resetNowPlaying();
    ReactNativeAudioStreaming.stop();
  }

  _playerStart() {
    ReactNativeAudioStreaming.play(this.state.streamUrl, { showIniOSMediaCenter: false, showInAndroidNotifications: false});
  }

  _playerStop() {
    ReactNativeAudioStreaming.stop();
  }

  _onPress() {
    switch (this.state.status) {
      case "PAUSED":
        ReactNativeAudioStreaming.resume();
        break;
      case "STOPPED":
      case "ERROR":
        this._playerStart();
        break;
      case "PLAYING":
      case "STREAMING":
      case "BUFFERING":
        this._playerStop();
        break;
    }
  }

  render() { 
    return (
      <View style={styles.controlElements}>
        <PlayingBlock styleBlock={styles.nowBlock}
                      styleTitle={styles.nowTitle}
                      styleSubTitle={styles.nowSubTitle}
                      title="Now playing:"
                      text={this.state.onair}/>
        <TouchableOpacity onPress={this._onPress}>
          <PlayButton status={this.state.status}/>
        </TouchableOpacity>
      </View>
    )
  }
}

class PlayingBlock extends React.Component {
  render() {
    var title = " ";
    var text = " ";
    if (this.props.text) {
      title = this.props.title;
      text = this.props.text;
    }
    return (
      <View style={this.props.styleBlock}>
        <Text style={this.props.styleTitle} numberOfLines={2}>
          {title}
        </Text>
        <Text style={this.props.styleSubTitle} numberOfLines={2}>
          {text}
        </Text>
      </View>
    );
  }
}

class PlayButton extends React.Component {
  render() {
    var button;
    switch (this.props.status) {
      case "PLAYING":
      case "STREAMING":
        button = <Image style={styles.playButton}
            source={require('../images/pause.png')}/>;
        break;
      case "PAUSED":
      case "STOPPED":
      case "ERROR":
        button = <Image style={styles.playButton}
            source={require('../images/play.png')}/>;
        break;
      case "BUFFERING":
      case "BUFFERING_START":
      case "START_PREPARING":
        button = <ActivityIndicator
            animating={true} style={styles.playButton}/>;
        break;
    }
    return button
  }
}

const styles = StyleSheet.create({
  controlElements: {
    alignItems: 'center',
    justifyContent: 'space-between',
    flexDirection: 'column',
  },
  playButton: {
    width: 22,
    height: 29,
    margin: 40,
  },
  nowBlock: {
    alignItems: 'center',
  },
  nowTitle: {
    fontSize: 15,
    paddingBottom: 5, 
    color: '#00418c',
    fontFamily: 'FiraSans-BoldItalic'
  },
  nowSubTitle: {
    fontSize: 24,
    color: '#00418c',
    textAlign: 'center',
    fontFamily: 'FiraSans-BoldItalic'
  },
});
callmejm commented 6 years ago

@SydneyTR where to put your code ? put it inside react-native-audio-streaming folder ?

mrsydster commented 6 years ago

@JayricMok: No this is just a component I wrote for the app, this can go into one of your screens, on which you want to display your player

callmejm commented 6 years ago

@SydneyTR now im using this modules Player , maybe i should try your Player , will let you know how, thanks btw.

callmejm commented 6 years ago

@SydneyTR in your _playerStart function i have to set the showIniOSMediaCenter and showInAndroidNotifications to true, if not when i click play app will crash

callmejm commented 6 years ago

@SydneyTR how you disabled the notification from Audio Streaming ?