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

Expo SDK 52 CNG Support #68

Open
samducker opened this issue Sep 2, 2024 · 7 comments
Open

Expo SDK 52 CNG Support #68

samducker opened this issue Sep 2, 2024 · 7 comments
Assignees

Comments

@samducker
Copy link

Hi I am trying to run the latest beta version of this library, however when I shake only the expo dev client appears.

Could you clarify if this is expected behaviour or if I should get both the expo dev client appearing and also the shake event from react native shake.

@Doko-Demo-Doa
Copy link
Owner

Hi @samducker I just made a quick test and it seems to work normally. You may have to run npx expo prebuild --clean and rebuild the app as it involves native code.

image

@Doko-Demo-Doa
Copy link
Owner

Also, the shake behavior of Android and iOS are different: On iOS, you can just fling the phone in one direction and the event fires. On Android, you can do it gently, but back and forth, for the events to be fired.

@samducker
Copy link
Author

Hey @Doko-Demo-Doa unfortunately it doesn't work for me with the same implementation as you in my route layout file.

I'm on latest expo SDK with expo-router, continuous native generation mode and expo dev client.

Could you confirm if you are using latest Expo SDK with the dev client installed?

Do you have any debugging advice also?

@Doko-Demo-Doa
Copy link
Owner

@samducker Can you create the minimum example so I can reproduce it?
In fact this lib must be used with Expo CNG because it involves native code.

I'm using latest stable Expo version, which is 51 at the moment.

image

@samducker
Copy link
Author

Hi @Doko-Demo-Doa I'm sorry to report, I couldn't get this library to work maybe it was clashing with expo dev client I'm not really sure.

I produced this custom hook instead which seems to be working well,perhaps you could wrap the expo-sensors library in a future release into this library for expo users.

Sharing incase anyone else is having expo related issues for a solution.

import { useEffect, useCallback, useState } from 'react';
import { DeviceMotion } from 'expo-sensors';

export default function useShakeDetector() {
  const [isShaking, setIsShaking] = useState(false);

  const handleShake = useCallback(() => {
    console.log('Shake detected');
  }, []);

  useEffect(() => {
    const SHAKE_THRESHOLD = 800;
    const SHAKE_COOLDOWN = 1000;
    const UPDATE_INTERVAL = 100;
    let lastUpdate = 0;
    let lastShake = 0;
    let lastX = 0,
      lastY = 0,
      lastZ = 0;

    const subscription = DeviceMotion.addListener((deviceMotionData) => {
      const { x, y, z } = deviceMotionData.accelerationIncludingGravity;
      const currentTime = Date.now();

      if (currentTime - lastUpdate > UPDATE_INTERVAL) {
        const diffTime = currentTime - lastUpdate;
        lastUpdate = currentTime;

        const speed = (Math.abs(x + y + z - lastX - lastY - lastZ) / diffTime) * 10000;

        if (speed > SHAKE_THRESHOLD && currentTime - lastShake > SHAKE_COOLDOWN) {
          lastShake = currentTime;
          setIsShaking(true);
          handleShake();
          setTimeout(() => setIsShaking(false), 300);
        }

        lastX = x;
        lastY = y;
        lastZ = z;
      }
    });

    DeviceMotion.setUpdateInterval(UPDATE_INTERVAL);

    return () => {
      subscription && subscription.remove();
    };
  }, [handleShake]);

  return isShaking;
}

@Kasendwa
Copy link

Hi @samducker thanks for this. Which of these variables can one adjust to reduce on the sensitivity of this hook? I notice that on Android, even just putting the phone down triggers a shake.

@djalmajr
Copy link

I also noticed this and needed to make some adjustments. I asked GPT to make some changes to this as well. It works relatively well. Here is the result:

import { DeviceMotion } from "expo-sensors";
import { useEffect, useState } from "react";

/**
 * Configuration options for the shake detection
 */
interface UseShakeOptions {
  /** Threshold for shake detection sensitivity (default: 800) */
  threshold?: number;
  /** Minimum time (in ms) between shake detections (default: 1000) */
  debounceMs?: number;
  /** Update interval for device motion (default: 100) */
  updateInterval?: number;
  /** Number of movements required for shake detection (default: 2) */
  requiredMovements?: number;
}

/**
 * A hook that detects device shake events using the device motion
 *
 * @param callback - Function to be called when a shake is detected
 * @param options - Configuration options for shake detection
 * @param options.threshold - Sensitivity threshold (lower = more sensitive)
 * @param options.debounceMs - Minimum time between shake detections
 * @param options.updateInterval - Update interval for device motion
 * @param options.requiredMovements - Number of movements required for shake
 *
 * @example
 * ```tsx
 * useShake(() => {
 *   console.log('Device was shaken!');
 * }, {
 *   threshold: 1200,
 *   debounceMs: 500,
 *   updateInterval: 50,
 *   requiredMovements: 2
 * });
 * ```
 *
 * @returns Whether the device is currently shaking
 */
function useShake(
  callback?: () => void,
  {
    threshold = 800,
    debounceMs = 1000,
    updateInterval = 100,
    requiredMovements = 2,
  }: UseShakeOptions = {},
) {
  const [isShaking, setIsShaking] = useState(false);

  useEffect(() => {
    let movementCount = 0;
    let lastDirection = 0;
    let lastUpdate = 0;
    let lastShake = 0;
    let lastX = 0;
    let lastY = 0;
    let lastZ = 0;

    const subscription = DeviceMotion.addListener((data) => {
      const { x, y, z } = data.accelerationIncludingGravity;
      const currentTime = Date.now();

      if (currentTime - lastUpdate > updateInterval) {
        const diffTime = currentTime - lastUpdate;
        const speed = (Math.abs(x + y + z - lastX - lastY - lastZ) / diffTime) * 10000;
        const direction = Math.sign(x + y + z - lastX - lastY - lastZ);

        if (speed > threshold) {
          if (direction !== lastDirection && currentTime - lastUpdate > 50) {
            movementCount++;
            lastDirection = direction;
          }

          if (movementCount >= requiredMovements && currentTime - lastShake > debounceMs) {
            lastShake = currentTime;
            setIsShaking(true);
            setTimeout(() => (setIsShaking(false), (movementCount = 0)), 300);
            callback?.();
          }
        } else if (currentTime - lastUpdate > 300) {
          movementCount = 0;
        }

        lastUpdate = currentTime;
        lastX = x;
        lastY = y;
        lastZ = z;
      }
    });

    DeviceMotion.setUpdateInterval(updateInterval);

    return () => subscription?.remove();
  }, [debounceMs, requiredMovements, threshold, updateInterval, callback]);

  return isShaking;
}

export { useShake };

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

No branches or pull requests

4 participants