Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to monitor user's movement of iphone like the "raise to speak" feature?

I want to get notified when the user raises the iphone to his face. Just like siri does. Is it possible?

Add more specific requirement: I want to darken the screen when user put the phone near his ear. I know the Proximity Sensor can be enable to implement this. But it's annoying that the screen will be darkened from time to time when user moves the finger upon the sensor. So I wonder how to avoid this case and only darken the screen when user raise the iphone to speak?

like image 928
fantaxy Avatar asked Jan 13 '23 22:01

fantaxy


2 Answers

See Using the Proximity Sensor in the UIDevice Class Reference. So, you:

  • Enable it:

    UIDevice *device = [UIDevice currentDevice];
    device.proximityMonitoringEnabled = YES;
    
  • Check if successfully enabled; observe the UIDeviceProximityStateDidChangeNotification notification if successful; if not, your device may not be capable:

    if (device.proximityMonitoringEnabled)
    {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleProximityChange:)
                                                     name:UIDeviceProximityStateDidChangeNotification
                                                   object:nil];
    }
    else
    {
         // device not capable
    }
    
  • And write your selector:

    - (void)handleProximityChange:(NSNotification *)notification
    {
        NSLog(@"%s proximityState=%d", __FUNCTION__, [[UIDevice currentDevice] proximityState]);
    }
    

To detect whether the user is holding it up to their face, I might marry the proximity sensor with the CMMotionManager and look at the gravity property to see if they're holding the phone nearly vertically. So, define a few class properties:

@property (nonatomic, strong) CMMotionManager *motionManager;
@property (nonatomic, strong) NSOperationQueue *deviceQueue;

And then you can start the CMMotionManager, looking for whether the device is held in a vertical position:

self.deviceQueue = [[NSOperationQueue alloc] init];
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 5.0 / 60.0;

UIDevice *device = [UIDevice currentDevice];

[self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryZVertical
                                                        toQueue:self.deviceQueue
                                                    withHandler:^(CMDeviceMotion *motion, NSError *error)
{
    BOOL vertical = (motion.gravity.z > -0.4 && motion.gravity.z < 0.4 & motion.gravity.y < -0.7);
    if ((vertical && !device.proximityMonitoringEnabled) || (!vertical && device.proximityMonitoringEnabled))
    {
        device.proximityMonitoringEnabled = vertical;
    }
}];

Whether these gravity thresholds make sense is a little subjective. You could also, rather than just looking to see if the phone is being held roughly vertically, look at other accelerometer data (e.g. did they raise the object or not). Seems like there are lots of ways to skin the cat.

like image 172
Rob Avatar answered Jan 19 '23 10:01

Rob


I know this is old, but I simplified the logic a bit and made a wrapper class in Swift.

You can find it here

class DeviceRaisedToEarListener: NSObject {
private let deviceQueue = NSOperationQueue()
private let motionManager = CMMotionManager()
private var vertical: Bool = false

private(set) var isRaisedToEar: Bool = false {
    didSet {
        if oldValue != self.isRaisedToEar {
            self.stateChanged?(isRaisedToEar: self.isRaisedToEar)
        }
    }
}

var stateChanged:((isRaisedToEar: Bool)->())? = nil

override init() {
    super.init()
    self.setupMotionManager()
}

private func setupMotionManager() {
    self.motionManager.deviceMotionUpdateInterval = 5.0 / 60.0
    let device = UIDevice.currentDevice()

    // Only listen for proximity changes if the device is held vertically
    self.motionManager.startDeviceMotionUpdatesUsingReferenceFrame(CMAttitudeReferenceFrame.XArbitraryZVertical, toQueue: self.deviceQueue) { (motion, error) in
        self.vertical = (motion.gravity.z > -0.4 && motion.gravity.z < 0.4 && motion.gravity.y < -0.7)
    }
}

func startListening() {
    UIDevice.currentDevice().proximityMonitoringEnabled = true
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleProximityChange:", name: UIDeviceProximityStateDidChangeNotification, object: nil)
}

func stopListening() {
    UIDevice.currentDevice().proximityMonitoringEnabled = false
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

func handleProximityChange(notification: NSNotification) {
    self.isRaisedToEar = UIDevice.currentDevice().proximityState && self.vertical
}

deinit {
    self.stopListening()
}
}


// How to use:
private func setupRaiseListener() {
    self.deviceListener.stateChanged = { [weak self] isRaisedToEar in
      println("is raised? \(isRaisedToEar)")
    }
    self.deviceListener.startListening()
}
like image 43
Mike Sprague Avatar answered Jan 19 '23 10:01

Mike Sprague