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

🐛 App unable to go to sleep once camera loads #3041

Open
3 of 5 tasks
FoundersApproach opened this issue Jul 2, 2024 · 4 comments
Open
3 of 5 tasks

🐛 App unable to go to sleep once camera loads #3041

FoundersApproach opened this issue Jul 2, 2024 · 4 comments
Labels
🐛 bug Something isn't working

Comments

@FoundersApproach
Copy link

What's happening?

Once camera library loads in one screen and going back to previous screen in stack navigation then app unable to goes to sleep. May be camera reference object is unable to let app sleep ?

I did some object reference clearance in component unmount but no luck. Please help!

Reproduceable Code

import React, { useState, useEffect, useCallback } from 'react';
import { View, Text, TouchableOpacity, Alert, Platform, StyleSheet, PermissionsAndroid, Image, AppState } from 'react-native';
import {Camera, useCameraDevices, useCameraDevice,useCameraFormat, Templates, useCodeScanner } from 'react-native-vision-camera';
import TextRecognition from '@react-native-ml-kit/text-recognition';
import AppConstants from '../module/constantVairable'
import { getCameraFace, getCurrentOrientation, getDeviceHeight, getDeviceWidth, getSafeAreaInsetBottom, getSafeAreaInsetTop, getTorchOn, isOrientationPortrait, setCameraRef, setScanLog, verifyGTINChecksum } from '../api/api';
import { Gesture, GestureDetector } from 'react-native-gesture-handler'
import ImageResizer from '@bam.tech/react-native-image-resizer';
import { RFValue } from 'react-native-responsive-fontsize';
import RNFS from 'react-native-fs';
import Animated, { runOnJS } from 'react-native-reanimated';


let cameraRef = null
let capturePhotoInteval = false
let focusPointTimer = false

const ScannerFunction = (props) => {
    const device = useCameraDevice(getCameraFace());
    const [permissionGranted, setPermissionGranted] = useState(false);
    const [isInitialized, setIsInitialized] = useState(false);
    const [torch, setTorch] = useState(false);
    const [restartCamera, setRestartCamera] = useState(false);
    const [cameraActive, setCameraActive] = useState(true);

    const [tapLocation, setTapLocation] = useState(null);


    useEffect(() => {
        // let appStateListener = AppState.addEventListener('change', appStateChangeListener);
        if (Platform.OS === 'android') {
          askCameraPermission();
        }
        else {
          setPermissionGranted(true)
          if (device && device.hasTorch && getTorchOn()) {
            setTorch(true)
          }
        }
        startCaptureTimer(2)
        
        return () => {
          cameraRef = null
          console.log("ScannerFunction unmount>>>>")
          setCameraActive(false)
          setTorch(false)
          setPermissionGranted(false)
          stopCaptureTimer()
          setCameraRef(null)
        }
    }, []);

    const startCaptureTimer = (sec=1) => {
      // console.log("startCaptureTimer>>>>")
      if (capturePhotoInteval) {
        clearInterval(capturePhotoInteval);
      }

      capturePhotoInteval = setInterval(() => {
        // console.log("capturePhotoInteval")
        capturePhoto()
      }, 2 * 1000);
    }

    const stopCaptureTimer = () => {
      if (capturePhotoInteval) {
        // console.log("clear interval")
        clearInterval(capturePhotoInteval);
      }
    }

    const format1 = useCameraFormat(device, [
      { photoResolution: "max" },
    ])

    const askCameraPermission = async () => {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.CAMERA,
        {
          title: "Camera Permission",
          message: "This app needs access to your camera ",
        }
      );
      if (granted === PermissionsAndroid.RESULTS.GRANTED) {
        console.log("You can use the camera");
        setPermissionGranted(true)
      } else {
        console.log("Camera permission denied");
      }
    }

    const parseText = (line, wholeText="", photoUrl="") => {
    }

    const capturePhoto = async () => {
      
        try {
          if (props.is_active) {
            if (cameraRef) {

              const photo = await cameraRef.takePhoto({
                qualityPrioritization: "quality",
                flash: "off",
                enableAutoStabilization: true,
                enableShutterSound: false
              });

              let photoUrl = "file://"+photo.path
  
              stopCaptureTimer()
              const result = await TextRecognition.recognize(photoUrl);
              
              for (let block of result.blocks) {
                
              
                for (let line of block.lines) {
                  setScanLog(line.text)
                  parseText(line.text, result.text, photoUrl)
                }
              }
  
              startCaptureTimer(2)
              
            }
            else {
              // Alert.alert("Camera not found")
            }
          }
          
        } catch (error) {
          setCameraActive(false)
          setCameraRef(null)
          cameraRef = null
          props.onError(error)
          console.log("Error>>>>>>>>>>>>", error.message)
        }
        
    }

    const focus = useCallback((point: Point) => {
      const c = cameraRef
      if (c == null) return
      c.focus(point)
      setTapLocation({x: point.x - RFValue(35), y: point.y - RFValue(35)})
      if (focusPointTimer) {
        clearTimeout(focusPointTimer)
      }
      focusPointTimer = setTimeout(() => {
        setTapLocation(null)
      }, 2 * 1000)
    }, [])

    const gesture = Gesture.Tap()
    .onEnd(({ x, y }) => {
      runOnJS(focus)({ x, y })
    })

    const _onError = (error) => {
    }

    return(
      <View style={{flex: 1, overflow: "hidden", backgroundColor: "black"}}>
      {/* <GestureDetector gesture={_gesture}> */}
      {
        device && device.hasTorch ? (
          <TouchableOpacity 
              onPress={() => setTorch(!torch)}
              style={{position: 'absolute', zIndex: 999 ,
              top: isOrientationPortrait() ? RFValue(10) : null, 
              bottom: isOrientationPortrait() ? null : (getSafeAreaInsetTop() + getSafeAreaInsetBottom() + RFValue(10)),
              right: RFValue(10), 
              height: RFValue(35), 
              width: RFValue(35), 
              transform: [{rotate: isOrientationPortrait() ? '0deg' : '-270deg'}],
              borderRadius: RFValue(5) ,backgroundColor: "#ffffff99", 
              alignItems: 'center', justifyContent: 'center'}}>
              <Image resizeMode='contain' style={{height: '65%', width: '65%'}} 
                  source={torch ? Theme.icons.ic_camera_light_off : Theme.icons.ic_camera_light_on}></Image>
          </TouchableOpacity> 
        ) : null
      }
      
      {/* this.onStopped = this.onStopped.bind(this)
    this.onError = this.onError.bind(this) */}

      {
        device && permissionGranted && props.is_active ? (
          <View style={{flex: 1}}>
          <GestureDetector gesture={gesture}>
          <Camera
            onError={(error)=>props.onError(error)}
          orientation={getCurrentOrientation()}
          torch={torch ? "on" : "off"}
          onInitialized={() => {
            setIsInitialized(true);
            setCameraRef(cameraRef)
          }}
              style={
                isInitialized
                  ? {
                      position: "absolute",
                      top: 0,
                      left: 0,
                      right: 0,
                      bottom: 0
                    }
                  : {
                      width: 0,
                      height: 0,
                    }
               }
              device={device}
              isActive={cameraActive}
              photo={true}
              ref={(ref) => cameraRef = ref}
              
            />
            
            </GestureDetector>
            {
              tapLocation && (
                <Animated.View
                  style={[
                    styles.indicator,
                    {
                      transform: [
                        { translateX: tapLocation.x - 15 },
                        { translateY: tapLocation.y - 15 },
                      ],
                    },
                  ]}
                />
              )
            }
            

            </View>
        ) : null
      }
      </View>
    )
}
export default ScannerFunction;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'transparent',
  },
  indicator: {
    width: RFValue(70),
    height: RFValue(70),
    borderRadius: RFValue(70),
    position: 'absolute',
    borderWidth: 4,
    borderColor: Theme.colors.appThemeColor,
  },
});

Relevant log output

This issue seems different so no log helps

Camera Device

<Camera
            onError={(error)=>props.onError(error)}
          orientation={getCurrentOrientation()}
          torch={torch ? "on" : "off"}
          onInitialized={() => {
            setIsInitialized(true);
            setCameraRef(cameraRef)
          }}
              style={
                isInitialized
                  ? {
                      position: "absolute",
                      top: 0,
                      left: 0,
                      right: 0,
                      bottom: 0
                    }
                  : {
                      width: 0,
                      height: 0,
                    }
               }
              device={device}
              isActive={cameraActive}
              photo={true}
              ref={(ref) => cameraRef = ref}
              
            />

Device

iPhone 14 plus (ios 17.1.1)

VisionCamera Version

"react-native-vision-camera": "^3.9.2"

Can you reproduce this issue in the VisionCamera Example app?

Yes, I can reproduce the same issue in the Example app here

Additional information

@FoundersApproach FoundersApproach added the 🐛 bug Something isn't working label Jul 2, 2024
@maintenance-hans maintenance-hans bot changed the title App unable to go to sleep once camera loads🐛 🐛 App unable to go to sleep once camera loads Jul 2, 2024
Copy link

Guten Tag, Hans here.

Note

New features, bugfixes, updates and other improvements are all handled mostly by @mrousavy in his free time.
To support @mrousavy, please consider 💖 sponsoring him on GitHub 💖.
Sponsored issues will be prioritized.

@kevinranks
Copy link

kevinranks commented Aug 1, 2024

@FoundersApproach We're seeing a similar issue on iOS 16+. On iOS <= 15 the green dot showing the camera is active never goes away. Can you try capturing the "goBack" and then using setTimeout to set the camera in-active prior to going back?

Something like this:

const delayedGoBack = useCallback(() => {
     setCameraActive(false)
     setTimeout(() => navigation.goBack(), 10)
}, [])

Note: Would need to disable back gestures in order to make sure navigating back is always captured.

@kevinranks
Copy link

UIApplication.shared.isIdleTimerDisabled = isActive

Looks like this specific line keeps the phone from going to sleep. It doesn't appear that this is implemented on Android for VC.

@jkaufman
Copy link
Contributor

Perhaps this should just be removed? Idle management could be left to library consumers. This would benefit Expo users, especially, which uses keep-awake to allow stacked calls to hold and release wake lock / idle timer setting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants