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

A really simple example that demos some navigable touchables without any dependencies #39

Open
silencer07 opened this issue Dec 5, 2023 · 22 comments
Labels
documentation Improvements or additions to documentation

Comments

@silencer07
Copy link

Hi,

Thank you so much for this library. TV computing has been an underserved platform that deserves love and apps! This framework helps to the realization of that.

I just have a small problem and maybe this is due to me being a new user of this framework. I tried integrating the components in this library and I cannot figure out what is the problem.

Of course I tried reading the example code, and I tried my hardest to mimic what it is doing and I am not successful :( and I don't know why.

I am thinking maybe a small example that is not fancy. Maybe a 3 touchable boxes that is beside one another which you can navigate using tv remote. no outside dependencies, just a bare react native initiated using CLI.

This would help newcomers like me to grasp what this framework requires and how we can make it work within our apps.

Thank you very much and more power to you guys!

P.S. I realized late I am using the standard react native version in my app. do this framework require a forked version like what I see in the example? i.e. "react-native": "npm:react-native-tvos@0.71.11-0",

@pierpo
Copy link
Member

pierpo commented Dec 5, 2023

Hey!

About the simple example... that might be a good idea.
We wanted to show a complete example and let you extract what you need.

The thing is: setting up the remote control is not that simple. It needs a few tweaks to be able to catch the native keyevents and plug them to the lib.

The lib's API is kind of simple regarding that, but handling it for multiplatform on the consumer's side isn't that simple.

Can you indicate the problems you encounter? Where do you miss the events?

P.S. I realized late I am using the standard react native version in my app. do this framework require a forked version like what I see in the example? i.e. "react-native": "npm:react-native-tvos@0.71.11-0",

You can manage without the TV fork because we don't use anything related... except on Apple tvOS 😉
You'll need to fork to build on this platform, unfortunately.

@silencer07
Copy link
Author

silencer07 commented Dec 5, 2023

Can you indicate the problems you encounter? Where do you miss the events?

Basically my node is not getting even the initial focus even though I properly wrapped it in SpatialNavigationRoot, SpatialNavigationNode and DefaultFocus. I even copy pasted the remote configuration from sample data as well because I thought that is where I am getting it wrong.

TIA!

P.S. I love the complete example. However I think a simple example will help quick troubleshooting for the future users of this framework <3

@pierpo
Copy link
Member

pierpo commented Dec 5, 2023

Oh, this is weird. The fact that you don't even get the initial default focus shows that the issue is not related to the remote!

Do you have any code snippets to share? I'm very curious about this.

About the simplified example, that's kind of what we wanted to do with the tutorial, but it looks like we failed haha

@pierpo pierpo added documentation Improvements or additions to documentation and removed feature request labels Dec 5, 2023
@silencer07
Copy link
Author

silencer07 commented Dec 5, 2023

This is the sample code. I truncated it because my code is bad and this is a hobby project I am trying to get deployed before christmas 🗡️

<SpatialNavigationRoot isActive={isFocused}>
      <SafeAreaView style={styles.container}>
        <View
          style={{
            flexBasis: 1,
            flexGrow: 2,
            flexDirection: "row",
          }}
        >
          <View
            style={{
              flex: 4,
              position: "relative",
              padding: 5,
            }}
          >
            <DefaultFocus>
              <SpatialNavigationNode isFocusable>
                {({ isFocused }) => (
                  <View
                    style={{
                      alignItems: "center",
                      justifyContent: "space-evenly",
                      flexDirection: "row",
                      paddingHorizontal: "25%",
                      width: "100%",
                    }}
                  >
                    <DefaultFocus>
                      <CustomButton
                        style={styles.nextButton}
                        disabled={isEmpty(songs)}
                        onPress={() =>
                          videoPlayerRef.current?.next(currentSongId!)
                        }
                      >
                        <Text
                          style={{
                            color: "lightgray",
                            textAlign: "center",
                            fontSize: 12,
                          }}
                        >
                          Next song
                        </Text>
                      </CustomButton>
                    </DefaultFocus>
                    <CustomButton
                      style={styles.linkButton}
                      onPress={() =>
                        navigation.navigate(RootRoutes.Instructions)
                      }
                    >
                      <Text
                        style={{
                          color: "black",
                          textAlign: "center",
                          fontSize: 12,
                        }}
                      >
                        Link Remote
                      </Text>
                    </CustomButton>
                    <CustomButton
                      style={styles.resetButton}
                      onPress={onResetButtonPress}
                    >
                      <Text
                        style={{
                          color: "white",
                          textAlign: "center",
                          fontSize: 12,
                        }}
                      >
                        Reset
                      </Text>
                    </CustomButton>
                  </View>
                )}
              </SpatialNavigationNode>
            </DefaultFocus>
            .....

Custom Button

const highlightedStyle = {
  borderWidth: 2,
  borderColor: "lightgray",
  borderStyle: "solid",
};

export function CustomButton(props: TouchableOpacityProps) {
  return (
    <SpatialNavigationNode
      onFocus={props.onFocus as () => void}
      onBlur={props.onBlur as () => void}
      onSelect={props.disabled ? (props.onPress as () => void) : undefined}
      orientation="horizontal"
      isFocusable={true}
    >
      {({ isFocused }) => {
        let style: Record<string, any> = props.style || {};
        if (isFocused) {
          style = { ...style, ...highlightedStyle };
        }
        return (
          <TouchableOpacity style={style} {...props}>
            {props.children}
          </TouchableOpacity>
        );
      }}
    </SpatialNavigationNode>
  );
}

I copy-pasted the remote configuration and made sure to put it inside the entrypoint of my project
import "../components/configureRemoteControl";

Thank you very much!

P.S. I suppose the framework has no problem with absolutely-positioned components right? However in this snippet I already removed it. However I plan to restore the absolute positioning.

Side Note: I am manually doing a full APK build, and installing the said APK manually via a thumb drive to my Xiaomi TV Box 2nd Gen. I believe it has Google TV OS version 12 already :D

@silencer07 silencer07 changed the title A really simple example that demos a navigable touchables without any dependencies A really simple example that demos some navigable touchables without any dependencies Dec 5, 2023
@manjunath-nuveb
Copy link

manjunath-nuveb commented Dec 6, 2023

@silencer07
<SpatialNavigationRoot isActive={isFocused}>
when are you setting isFocused to true?

@silencer07
Copy link
Author

@manjunath-nuveb it is from reacat navigation const isFocused = useIsFocused();

Just assume it is always true for the sake of code snippet :D

@pierpo
Copy link
Member

pierpo commented Dec 6, 2023

@manjunath-nuveb  that's a good check indeed 😊 thanks!

@silencer07 I don't really understand what could be wrong here 🤔

I would remove the TouchableOpacity from the buttons. It might mess up with the lib because the whole lib relies on ignoring native focus 😄

Also, can you try a simpler example in your codebase and check whether it works or not?
First, I'd make a simpler button :

const Button = ({ onSelect, label }) => (
 <SpatialNavigationNode
      onSelect={onSelect}
      orientation="horizontal"
      isFocusable={true}
    >
      {({ isFocused }) => {
        return (
          <Text style={{ backgroundColor: isFocused ? 'white' : 'black' }}>{label}</Text>
        );
      }}
    </SpatialNavigationNode>
)

Then, I'd use it in the most simple page:

<SpatialNavigationRoot>
  <SpatialNavigationNode>
    <Button
      onPress={() => console.log('A')}
      label="A"
    />
    <Button
      onPress={() => console.log('B')}
      label="B"
    />
  </SpatialNavigationNode>
</SpatialNavigationRoot>

@silencer07
Copy link
Author

Thanks @pierpo. I would definitely try this on friday and get back to you. I decided to take a two day break for now :D

I really love how you are so engage in this!

@silencer07
Copy link
Author

Hi @pierpo. I have tried what you suggested and the default focus works now!

However it seems that remote does not work, i.e. I cannot move the focus to the next button.

I think I need to change something in the remoteConfiguration sample code I copy pasted here but I cannot pinpoint it. Any clue?

P.S. yep I guess I will still need the simple sample code after all

@pierpo
Copy link
Member

pierpo commented Dec 11, 2023

Can you share the remote configuration that you used? Are you working on AndroidTV?

@silencer07
Copy link
Author

silencer07 commented Dec 11, 2023

Hi @pierpo. this is what I did basically

I copy-pasted the remote configuration from example and made sure to put it inside the entrypoint of my project

import "../components/configureRemoteControl";

I am manually doing a full APK build, and installing the said APK manually via a thumb drive to my Xiaomi TV Box 2nd Gen. I believe it has Google TV OS version 12 already :D

@pierpo
Copy link
Member

pierpo commented Dec 11, 2023

OK, did you properly install react-native-keyevent? This is an important step ;)
Also, don't forget to copy remote-control/RemoteControlManager.android.ts. The remote-control/RemoteControlManager.ts is for web.

Install steps for react-native-keyevent:
https://github.com/kevinejohn/react-native-keyevent

@silencer07
Copy link
Author

Oh my! I was able to make it work. You are right the react-native-keyevent needs integrated native code.

since I am using expo, I just needed to use https://github.com/ChronSyn/react-native-keyevent-expo-config-plugin and configure it!

This is lovely! I think I am unstuck now!

P.S. your remote configuration is a sensible default, I am thinking maybe we should integrate it to the framework itself.

@silencer07
Copy link
Author

silencer07 commented Dec 12, 2023

Alright. After fixing the code, my next problem is "onSelect" event not working

This is my custom button now

const highlightedStyle = {
  borderWidth: 2,
  borderColor: "lightgray",
  borderStyle: "solid",
};

export function CustomButton(props: TouchableOpacityProps) {
  return !Platform.isTV ? (
    <TouchableOpacity {...props}>{props.children}</TouchableOpacity>
  ) : (
    <SpatialNavigationNode
      onFocus={props.onFocus as () => void}
      onBlur={props.onBlur as () => void}
      onSelect={!props.disabled ? (props.onPress as () => void) : undefined}
      isFocusable={true}
    >
      {({ isFocused }) => {
        let style: Record<string, any> = props.style || {};
        if (isFocused) {
          style = { ...style, ...highlightedStyle };
        }
        return <View style={style}>{props.children}</View>;
      }}
    </SpatialNavigationNode>
  );
}

and this is how I use it

<CustomButton
  style={styles.linkButton}
  onPress={() =>
    navigation.navigate(RootRoutes.Instructions)
  }
>
  <Text
    style={{
      color: "black",
      textAlign: "center",
      fontSize: 12,
    }}
  >
    Link Remote
  </Text>
</CustomButton>

weirdly enough nothing happens when you press the center button of my remote

EDIT: even enter key of my generic bluetooth keyboard does not work as well


Update: I tried debugging it by adding an Alert message on configureRemoteControl.remoteControlListener

 const remoteControlListener = (keyEvent: SupportedKeys) => {
      Alert.alert("keyEvent", `keyEvent: ${JSON.stringify(keyEvent)}`);
      callback(mapping[keyEvent]);
    };

same with RemoteControlManager.handleKeyDown of RemoteControlManager.android.ts

private handleKeyDown = (keyEvent: { keyCode: number }) => {
    Alert.alert("keyEvent", `keyEvent: ${JSON.stringify(keyEvent)}`);
    .....

And it seems that the center button of my remote is not registering a keyEvent. Up, Down, Left, Right are all registered properly. Is this a manufacturer-specific bug or bug of react-native-keyevent itself?

@pierpo
Copy link
Member

pierpo commented Dec 12, 2023

Oh, this is probably related to react-native-keyevent indeed. I can't help with that. You should add breakpoints in the native code to see if the enter key is handled?

@silencer07
Copy link
Author

Gotcha, I will just file a new issue in the said library and get back to you. Let's leave this issue open until we found a solution @pierpo

@silencer07
Copy link
Author

silencer07 commented Dec 13, 2023

@pierpo

I did some more testing, and it seems that doing a long press on center button of remote actually register an "enter" key event.

while this unblocks my further development, It would be nice if there is a workaround I can do to register short press as long press for the mean time :)


Update: Seems that user needs to interact with a system dialog to make the short press work. in my case when I finished asking the user to grant location, I do not need to long press anymore!

@silencer07
Copy link
Author

silencer07 commented Jan 8, 2024

@pierpo someone pointed me on this one: kevinejohn/react-native-keyevent#52

ticket that i filed in react-native-keyevent library: kevinejohn/react-native-keyevent#80

I will play around and see if this works. if yes, I think we will need a patch-packaged version of react-native-keyevent

@ionictest2017
Copy link

ionictest2017 commented Jan 18, 2024

<SpatialNavigationRoot>
  <SpatialNavigationNode>
    <Button
      onPress={() => console.log('A')}
      label="A"
    />
    <Button
      onPress={() => console.log('B')}
      label="B"
    />
  </SpatialNavigationNode>
</SpatialNavigationRoot>

I have tried this simple code in the given example project but it is not working in the Android TV emulator while the focus is working with TouchableOpacity

also, I have put the code for initialization in my React native TV app root file

import { init } from '@noriginmedia/norigin-spatial-navigation';

init({
nativeMode:true,
debug:true,
visualDebug:true
// options
});

Please help me, I am trying to implement a menu with the movie swimlane in my React Native tv app

@silencer07
Copy link
Author

silencer07 commented Jan 23, 2024

@pierpo someone pointed me on this one: kevinejohn/react-native-keyevent#52

ticket that i filed in react-native-keyevent library: kevinejohn/react-native-keyevent#80

I will play around and see if this works. if yes, I think we will need a patch-packaged version of react-native-keyevent

@pierpo seems that there is an additional code needed in the android native side for DPAD_CENTER of a remote to work

ChronSyn/react-native-keyevent-expo-config-plugin#4

I am thinking maybe the example could be updated?

@pierpo
Copy link
Member

pierpo commented Jan 30, 2024

@silencer07 sorry I had not answered. Thank you for your work on this 😊
We're keeping the issue open and we'll add this to the example indeed.

We just switched the example to Expo so it's convenient to have your patch 😁
(And we added a patch already to support Expo 50)

@manjunath-nuveb
Copy link

My issue is similar to this. but happens only when virtual keyboard is opened and closed
kevinejohn/react-native-keyevent#83

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

4 participants