react-native#TVEventHandler JavaScript Examples

The following examples show how to use react-native#TVEventHandler. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: VideoProgressBar.js    From react-native-tv-demo with MIT License 5 votes vote down vote up
VideoProgressBar = (props) => {
  const {duration, time, seek} = props;
  const [percentage, setPercentage] = useState(0);

  const durationRef = useRef(duration);
  const timeRef = useRef(time);

  const indicatorRef = useRef(null);
  const indicatorHandle = useNodeHandle(indicatorRef);

  useEffect(() => {
    if (time >= 0 && duration > 0) {
      setPercentage(Math.floor((time / duration) * 100));
    }
    durationRef.current = duration;
    timeRef.current = time;
  }, [duration, time]);

  useEffect(() => {
    // Listen to TV events
    const tvEventHandler = new TVEventHandler();
    tvEventHandler.enable(null, tvEventListener);
    // Clean up
    return () => {
      // Remove TV event listener
      if (tvEventHandler) {
        tvEventHandler.disable();
      }
    };
  }, []);

  function tvEventListener(component, event) {
    if (event.tag === findNodeID(indicatorRef.current)) {
      if (event.eventKeyAction === 0) {
        const currentTime = timeRef.current;
        const currentDuration = durationRef.current;
        if (event.eventType === 'left') {
          // Rewind
          if (currentTime > SEEK_STEP) {
            if (seek) {
              seek(currentTime - SEEK_STEP);
            }
          }
        } else if (event.eventType === 'right') {
          // Fast forward
          if (currentTime < currentDuration - SEEK_STEP) {
            if (seek) {
              seek(currentTime + SEEK_STEP);
            }
          }
        }
      }
    }
  }

  return (
    <View style={[props.style, styles.progressBar]}>
      <FocusableHighlight
        ref={indicatorRef}
        nextFocusLeft={indicatorHandle}
        nextFocusRight={indicatorHandle}
        nativeID={'progress_bar_indicator'}
        style={[styles.progressBarIndicator, {left: percentage + '%'}]}
        underlayColor={Style.buttonFocusedColor}
      />
      <View style={styles.progressBarContainer}>
        <View style={[styles.progressBarStatus, {width: percentage + '%'}]} />
      </View>
    </View>
  );
}
Example #2
Source File: EventsDemo.js    From react-native-tv-demo with MIT License 4 votes vote down vote up
EventsDemo = () => {
  const [tvEventStack, setTvEventStack] = useState([]);
  const [componentEventStack, setComponentEventStack] = useState([]);

  useFocusEffect(
    useCallback(() => {
      // Listen to back button
      const backHandler = BackHandler.addEventListener(
        'hardwareBackPress',
        backEventListener,
      );
      // Enabled TVEventHandler
      const tvEventHandler = new TVEventHandler();
      tvEventHandler.enable(null, tvEventListener);
      // Clean up
      return () => {
        // Remove BackHandler
        backHandler.remove();
        // Disable TVEventHandler
        tvEventHandler.disable();
      };
    }, []),
  );

  function backEventListener(event) {
    // Just add some logs here as navigation will change screen
    console.log('backEventListener received hardwareBackPress event');
    console.log(JSON.stringify(event));
  }

  function tvEventListener(component, event) {
    //console.log('tvEventListener:', event.eventType);
    setTvEventStack((oldTvEventStack) =>
      [...oldTvEventStack, JSON.stringify(event)].slice(EVENT_LINES * -1),
    );
  }

  function componentEventListener(event) {
    if (!event.eventType) {
      return;
    }
    //console.log('componentEventListener:', event.eventType);
    setComponentEventStack((oldComponentEventStack) =>
      [...oldComponentEventStack, JSON.stringify(event)].slice(
        EVENT_LINES * -1,
      ),
    );
  }

  return (
    <View style={Style.styles.right}>
      <View style={Style.styles.header}>
        <Text style={Style.styles.headerText}>{'Events Demo'}</Text>
      </View>
      <View style={Style.styles.content}>
        <View style={styles.buttons}>
          <FocusableHighlight
            nativeID={'events_left_button'}
            onPress={componentEventListener}
            onFocus={componentEventListener}
            onBlur={componentEventListener}
            style={styles.button}
            underlayColor={Style.buttonFocusedColor}>
            <Text style={styles.buttonText}>{'Left Button'}</Text>
          </FocusableHighlight>
          <FocusableHighlight
            nativeID={'events_middle_button'}
            onPress={componentEventListener}
            onFocus={componentEventListener}
            onBlur={componentEventListener}
            style={styles.button}
            hasTVPreferredFocus={true}
            underlayColor={Style.buttonFocusedColor}>
            <Text style={styles.buttonText}>{'Middle Button'}</Text>
          </FocusableHighlight>
          <FocusableHighlight
            nativeID={'events_right_button'}
            onPress={componentEventListener}
            onFocus={componentEventListener}
            onBlur={componentEventListener}
            style={styles.button}
            underlayColor={Style.buttonFocusedColor}>
            <Text style={styles.buttonText}>{'Right Button'}</Text>
          </FocusableHighlight>
        </View>
        <Text style={[styles.eventHeader, styles.tvEventHeader]}>
          TVEventHandler events
        </Text>
        <Text style={styles.eventType} numberOfLines={EventsDemo.EVENT_LINES}>
          {tvEventStack.join('\n')}
        </Text>
        <Text style={styles.eventHeader}>TouchableHighlight events</Text>
        <Text style={styles.eventType} numberOfLines={EventsDemo.EVENT_LINES}>
          {componentEventStack.join('\n')}
        </Text>
      </View>
    </View>
  );
}
Example #3
Source File: VideoDemo.js    From react-native-tv-demo with MIT License 4 votes vote down vote up
VideoDemo = () => {
  let hideOverlayTimer = null;

  const navigation = useNavigation();

  // Init Refs
  const playerRef = useRef(null);
  const playPauseButtonRef = useRef(null);

  // Context
  const [appContext, setAppContext] = useContext(AppContext);

  // State
  const [source, setSource] = useState(sampleVideoSource);
  const [videoEventStack, setVideoEventStack] = useState([]);
  const [videoDuration, setVideoDuration] = useState(0);
  const [videoTime, setVideoTime] = useState(0);
  const [isReady, setIsReady] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [isPlaying, setIsPlaying] = useState(false);

  // State Refs
  const [pausedRef, isPaused, setPaused] = useStateRef(true);
  const [overlayRef, isOverlayVisible, setIsOverlayVisible] = useStateRef(true);
  const [fullscreenRef, isFullscreen, setIsFullscreen] = useStateRef(false);

  useFocusEffect(
    useCallback(() => {
      // Listen to back button
      const backHandler = BackHandler.addEventListener(
        'hardwareBackPress',
        backHandlerListener,
      );
      // Listen to TV events
      const tvEventHandler = new TVEventHandler();
      tvEventHandler.enable(null, tvEventListener);
      // Clean up
      return () => {
        // Pause playback
        if (!isPaused()) {
          setPaused(true);
        }
        // Clean timeout
        if (hideOverlayTimer) {
          clearTimeout(hideOverlayTimer);
        }
        // Remove backHandler
        backHandler.remove();
        // Remove TV event listener
        if (tvEventHandler) {
          tvEventHandler.disable();
        }
      };
    }, []),
  );

  useFocusEffect(
    useCallback(() => {
      // Prevent react navigation to handle back button is player is fullscreen
      return navigation.addListener('beforeRemove', (e) => {
        if (isFullscreen()) {
          e.preventDefault();
        }
      });
    }, []),
  );

  useEffect(() => {
    // Toggle menu
    setAppContext({menuVisible: !isFullscreen()});
  }, [fullscreenRef.current]);

  function toggleFullscreen() {
    pushVideoEventStack(isFullscreen() ? 'exitFullscreen' : 'enterFullscreen');
    // Toggle fullscreen
    setIsFullscreen(!isFullscreen());
  }

  function setOverlayVisible() {
    console.log('setOverlayVisible', isOverlayVisible());
    if (isOverlayVisible() === false) {
      setIsOverlayVisible(true);
      if (playPauseButtonRef) {
        playPauseButtonRef.current.focus();
      }
    }
    setOverlayHiddenAfterDelay();
  }

  function setOverlayHidden() {
    if (isOverlayVisible() === true && !isPaused()) {
      setIsOverlayVisible(false);
    }
  }

  function setOverlayHiddenAfterDelay() {
    console.log('setOverlayHiddenAfterDelay');
    if (hideOverlayTimer) {
      clearTimeout(hideOverlayTimer);
    }
    hideOverlayTimer = setTimeout(setOverlayHidden, 4000);
  }

  function backHandlerListener() {
    console.log('backHandleListener => got back button event');
    if (isFullscreen()) {
      toggleFullscreen();
    }
    return true;
  }

  function tvEventListener(component, event) {
    //console.log('VideoDemo.tvEventListener()', event);
    if (event.eventKeyAction === 0) {
      // Show overlay
      setOverlayVisible();
      // Toggle play / pause
      if (event.eventType === 'playPause') {
        setPaused(!isPaused());
      }
    }
  }

  // Video Events

  function pushVideoEventStack(event, params) {
    let eventStr = event + '(' + (params ? JSON.stringify(params) : '') + ')';
    console.log('Video event: ' + eventStr);
    const eventLines = isFullscreen() ? EVENT_LINES_FULLSCREEN : EVENT_LINES;
    setVideoEventStack((oldVideoEventStack) =>
      [...oldVideoEventStack, eventStr].slice(eventLines * -1),
    );
  }

  function onReadyForDisplay() {
    setIsReady(true);
    pushVideoEventStack('onReadyForDisplay');
  }

  function onLoad(data) {
    setIsLoading(false);
    setVideoDuration(data.duration);
    pushVideoEventStack('onLoad', data);
  }

  function onLoadStart(data) {
    setIsLoading(true);
    pushVideoEventStack('onLoadStart', data);
  }

  function onPlaybackRateChange(data) {
    setIsPlaying(data.playbackRate > 0);
    pushVideoEventStack('onPlaybackRateChange', data);
  }

  function onProgress(data) {
    setVideoTime(data.currentTime);
    pushVideoEventStack('onProgress', data);
  }

  function onEnd() {
    setVideoTime(0);
    setPaused(true);
    setIsPlaying(false);
    pushVideoEventStack('onEnd');
    setOverlayVisible();
  }

  function onError(error) {
    pushVideoEventStack('onError', error);
  }

  function formatTime(time) {
    let seconds = parseInt(time, 10);
    let hours = Math.floor(seconds / 3600);
    let minutes = Math.floor((seconds - hours * 3600) / 60);
    seconds = seconds - hours * 3600 - minutes * 60;
    let timeFormat = '';
    if (hours > 0) {
      if (hours < 10) {
        hours = '0' + hours;
      }
      timeFormat += hours + ':';
    }
    if (minutes < 10) {
      minutes = '0' + minutes;
    }
    if (seconds < 10) {
      seconds = '0' + seconds;
    }
    // Fix NaN
    if (isNaN(minutes)) {
      minutes = '-';
    }
    if (isNaN(seconds)) {
      seconds = '-';
    }
    timeFormat += minutes + ':' + seconds;
    return timeFormat;
  }

  return (
    <View style={[Style.styles.right, isFullscreen() && styles.fullscreen]}>
      {!isFullscreen() && (
        <View style={Style.styles.header}>
          <Text style={Style.styles.headerText}>{'Video Demo'}</Text>
        </View>
      )}
      <View style={[Style.styles.content, isFullscreen() && styles.fullscreen]}>
        <View
          style={
            isFullscreen()
              ? styles.videoContainerFullscreen
              : styles.videoContainer
          }>
          <VideoContainer
            ref={playerRef}
            source={source}
            paused={isPaused()}
            onReadyForDisplay={onReadyForDisplay}
            onLoadStart={onLoadStart}
            onLoad={onLoad}
            onPlaybackRateChange={onPlaybackRateChange}
            onProgress={onProgress}
            onEnd={onEnd}
            onError={onError}
          />
          <View
            style={
              isOverlayVisible()
                ? styles.videoOverlayVisible
                : styles.videoOverlayHidden
            }>
            <View style={styles.videoOverlayBackground} />
            <View style={styles.videoEvents}>
              <Text style={styles.videoEventsText}>
                {'Video Events:\n' + videoEventStack.join('\n')}
              </Text>
            </View>
            <VideoProgressBar
              duration={videoDuration}
              time={videoTime}
              style={styles.progressBar}
              seek={(seconds) => {
                playerRef.current.seek(seconds);
              }}
            />
            <View style={styles.videoControls}>
              <FocusableHighlight
                nativeID={'play_pause_button'}
                ref={playPauseButtonRef}
                onPress={(e) => {
                  if (e.eventKeyAction === 0 && e.eventType === 'select') {
                    setPaused(!isPaused());
                  }
                }}
                style={styles.videoControl}
                hasTVPreferredFocus={true}
                underlayColor={Style.buttonFocusedColor}>
                <Text style={styles.videoControlText}>
                  {isPaused() ? 'Play' : 'Pause'}
                </Text>
              </FocusableHighlight>
              <FocusableHighlight
                nativeID={'fullscreen_button'}
                onPress={(e) => {
                  if (e.eventKeyAction === 0 && e.eventType === 'select') {
                    toggleFullscreen();
                  }
                }}
                style={styles.videoControl}
                underlayColor={Style.buttonFocusedColor}>
                <Text style={styles.videoControlText}>
                  {isFullscreen() ? 'Exit Fullscreen' : 'Enter Fullscreen'}
                </Text>
              </FocusableHighlight>
              <View style={styles.videoTime}>
                <Text style={styles.videoTimeText}>
                  {formatTime(videoTime) + ' / ' + formatTime(videoDuration)}
                </Text>
              </View>
            </View>
          </View>
        </View>
      </View>
    </View>
  );
}