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

[Android] AppState background triggered while scanning nfc chip #437

Closed
buekera opened this issue Sep 17, 2021 · 7 comments
Closed

[Android] AppState background triggered while scanning nfc chip #437

buekera opened this issue Sep 17, 2021 · 7 comments
Labels

Comments

@buekera
Copy link

buekera commented Sep 17, 2021

Hey there, I just noticed some odd behavior while scanning nfc chips.
The moment you put your nfc chip to the phone and the scanner detects it the AppState changes from active to background back to active again.

Due to this behavior some other parts which listen to the AppStage change behave strangely. Is there any way to work around this or might this even be a bug?

OS: Android
tested nfc manager versions: 3.7.0 - 3.10.0

Code:

AppState.addEventListener("change", (nextAppState: AppStateStatus) => {
  log.debug("state changed to -> ", nextAppState);
});

[...]

try {
  setWaiting(true);
  await NfcManager.requestTechnology([NfcTech.Ndef]);

  const tag = await NfcManager.getTag();

  handleNfcScan(tag?.id as unknown as string);
  NfcManager.cancelTechnologyRequest();
} catch (error) {
  // handle error
}

So when putting a nfc chip to the scanner the following logs within a split second:

DEBUG state changed to -> background
DEBUG state changed to -> active

Regards

@buekera buekera changed the title AppState background triggered while scanning nfc chip [Android] AppState background triggered while scanning nfc chip Sep 17, 2021
@ognjenmarcheta
Copy link
Contributor

ognjenmarcheta commented Sep 21, 2021

@buekera That is happening because of how react native sees/defines background and foreground states.
React native uses your app activity as the owner of the UI thread. When something is in front of your app (alerts, dialogs, file pickers, etc.) onPause callback will be called and trigger "background" event.

That false report happens really fast, what you could do is to measure time between background/foreground and to ignore those false reports. But to measure it in background you need this package since when react native app goes to background state it is paused and code will not execute until you are back in the app again, so measuring is not possible without running some code in background. For example setTimeout or setInterval.

Package for running background code: https://github.com/ocetnik/react-native-background-timer

I've already recently reported this to react-native community repo and it was already reported couple years ago, I think in 2018 but old report was closed due to inactivity.

You can see they are not interested in this issue:
facebook/react-native#32036
react-native-community/discussions-and-proposals#399

@whitedogg13
Copy link
Member

@buekera might relate to #438 please try the latest v3.10.1. Thanks!

@NqCompiler
Copy link

I "fixed" it by measuring how long "background" state was on until "active" state was back on.
If it's less than 1 second, I assume it's from nfc reading.
I had to use reference variables because my state variable was not updated in my callback function in AppState.addEventListener("change", onChangeState).

  const blockTsRef = useRef<number>(null);

  const onChangeState = (newState) => {
    setCurrAppState(newState);
    if(newState === "background") {
      // set timestamp
      blockTsRef.current = new Date().getTime();
    }
    else if(newState === "active") {
      if(typeof blockTsRef.current === "number") {
        const tsNow = new Date().getTime();
        // calculate difference
        if(tsNow - blockTsRef.current <= 1000) {
          // reset blockTsRef
          blockTsRef.current = null;
          // don't do
          return;
        }
      }
      // do
    }
  };

@github-actions
Copy link

github-actions bot commented Dec 7, 2021

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions bot added the Stale label Dec 7, 2021
@github-actions
Copy link

This issue was closed because it has been stalled for 5 days with no activity.

@neiracausevic1
Copy link

I fixed it similarly to the solution provided above but just set the difference to 500 ms because it usually takes 20-40 ms if its the NFC triggering the state changes and it works for me.

const appState = useRef(AppState.current);
const [lastBackgroundTime, setLastBackgroundTime] = useState(null);

useEffect(() => {
    const handleAppStateChange = nextAppState => {
      if (
        appState.current.match(/inactive|background/) &&
        nextAppState === ‘active’
      ) {
        const currentTime = new Date().getTime();
        if (lastBackgroundTime && currentTime - lastBackgroundTime > 500) {
           // App was in background
        } else {
          // App state falsely triggered by NFC
          return;
        }
      }
      if (nextAppState === ‘background’) {
        setLastBackgroundTime(new Date().getTime());
      }
      appState.current = nextAppState;
    };
    // Subscribe to app state changes
    const appStateSubscription = AppState.addEventListener(
      ‘change’,
      handleAppStateChange,
    );
    return () => {
      // Clean up the subscription on unmount
      appStateSubscription.remove();
    };
  }, [lastBackgroundTime]);

@douglasjunior
Copy link

On Android, the background state means that the React Native Activity is in background, and not necessary the entire app.

To handle this, I created a package that implements the Android Lifecycle API for React Native: https://github.com/douglasjunior/react-native-applifecycle

Why Use This?

The original AppState API provided by React Native behaves differently between Android and iOS, particularly regarding the background state:

  • On iOS, the background state signifies that the entire app is in the background.
  • On Android, the background state indicates that the React Native Activity is in the background, which might not necessarily mean the entire app is in the background.

By using react-native-applifecycle, you can handle these differences seamlessly across both platforms, ensuring that the state background will be dispatched only when the entire app is in background.

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

No branches or pull requests

6 participants