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

requestAndCheckMotionAuthorization doesn't respond on second use #197

Closed
HuaHub opened this issue Apr 18, 2017 · 29 comments
Closed

requestAndCheckMotionAuthorization doesn't respond on second use #197

HuaHub opened this issue Apr 18, 2017 · 29 comments
Labels
sidelined No further action will be taken on this issue

Comments

@HuaHub
Copy link

HuaHub commented Apr 18, 2017

Hi, requestAndCheckMotionAuthorization doesn't seem to give any response the second time it is called. Are you able to reproduce?

Cordova 6.5.0
Platform iOS 4.3.1
iOS 10.3

@dpa99c
Copy link
Owner

dpa99c commented Apr 19, 2017

In short: yes I can reproduce it but I think there's nothing that can be done about it. It's a limitation due to the way iOS handles the motion permission (which is differently than all the other permissions).

There is no direct iOS API to request/check for this permission, so it must be done indirectly: motion permission is requested by requesting activity updates and the current motion authorization status is queried by querying pedometer data.

Like many iOS permissions, if the user denies permission when the dialog is shown, the app cannot request that permission again - subsequent calls will have no effect. This is intended behaviour by Apple, the rationale being that the user intended to deny permission and therefore the app shouldn't be allowed to bother the user again with more requests for a permission they chose to deny. The permission request fails silently so it's not even possible for the plugin to tell if the dialog was successfully displayed or not - it can only tell that the dialog was successfully requested.

If you press deny in the permission dialog, the next time that requestAndCheckMotionAuthorization() is called, the plugin calls the motion manager API to request activity updates which should callback to the asynchronous handler but, by placing breakpoints in the code, I can see that while the motion manager request for activity updates is being made by the plugin, the asynchronous handler is never called, nor is there any exception raised. The result is that the operation fails silently with the plugin unaware because the underlying iOS API makes no callback and raises no error.

In the case of motion permission, this is compounded by the fact that both requesting and checking are carried out in one operation, therefore if the user denies permission, not only will the permission dialog not be shown again when requested, but plugin is also unable to check the current status.
One thing I will try when I have time, is to try calling the pedometer API indepedently of the motion manager API to see if a status can be returned, but I have a hunch that the same silent failing will occur.

@dpa99c dpa99c added the investigate Issue requires investigation label Apr 19, 2017
@dpa99c
Copy link
Owner

dpa99c commented Apr 19, 2017

@HuaHub Do you have access to an iPhone 6/6s/7 to test on?

I have a problem testing the checking of the pedometer API because it requires a specific chipset only present on the iPhone 6 or above. It cannot be tested in the Simulator and I have only an iPhone 5C and an iPad Air 2 (which do not have the required chipset).

If so, I will create a branch which contains version of the plugin which exposes the check for motion authorization (using pedometer API) without first requesting motion permission (using motion manager API), i.e. getMotionAuthorizationStatus().

This won't directly solve your problem - requestAndCheckMotionAuthorization() will still fail silently, but it may allow you to independently check the current authorization status before making the request.
And of course, on an iPad or iPhone 5, calling getMotionAuthorizationStatus() will invoke the error handler since the pedometer API is not available due to the lack of the new chipset.

@HuaHub
Copy link
Author

HuaHub commented Apr 19, 2017

Yes, I understand that Apple won't allow you to ask for the permission twice if the user has denied the request. As you mentioned, I would only like to check the current status of what the user has replied.

I do not currently have an iPhone 6 to test on, maybe someone else does?

@dpa99c
Copy link
Owner

dpa99c commented Apr 20, 2017

I've done some more looking into this:

Firstly, just to clarify. the motion tracking authorization status is available to any iOS device which supports Pedometer Event Tracking: that is iPhone 5s or above (but not any iPad).

I've created a new version of the plugin (3.6.0) on the dev branch which separates requesting and checking of motion tracking authorization. This version adds a new getMotionAuthorizationStatus() to explicitly check the status without requesting it.

There's a corresponding dev branch of the example project which supports the new plugin API.

Note that I'm still unable to fully test this as I have an iPhone 5C, but an iPhone 5S or above is required to test the functionality which requires Pedometer Event Tracking, so if anyone has such as device, I'd appreciate if you could checkout the dev branch of the example project and build it: to do this, once you've cloned the project, add the iOS platform (cordova platform add ios) and prepare it (cordova prepare ios) which should pull down the dev version of the plugin. You should then be able to run the project either via XCode or the Cordova CLI (cordova run ios).

What I'd specifically like to test is if the first time you request motion access you press "Deny", that the plugin is able to detect this: the motion authorization status should be displayed as "DENIED". And subsequent calls to requestMotionAuthorization() should result in the error handler being called (in the example app this will log an error to the JS console).

@dpa99c dpa99c added TODO Something needs doing and removed investigate Issue requires investigation labels Apr 23, 2017
@HuaHub
Copy link
Author

HuaHub commented Apr 29, 2017

Hi again Dave,

Thank you for the great work with seperating getMotionAuthorizationStatus() and requesting permission for it. Do you still need to test it on an iPhone 6? I have one available now. If not, can you make a release, or is it possible to use the dev version as plugin (if yes, how?).

By the way:
It seems that the first try on requestAndCheckMotionAuthorization always gives the status "not_determined" when it is allowed, and no response if it is denied (?). Are you experiencing this as well?

@dpa99c
Copy link
Owner

dpa99c commented May 3, 2017

It seems that the first try on requestAndCheckMotionAuthorization always gives the status "not_determined" when it is allowed, and no response if it is denied (?). Are you experiencing this as well?

Thanks for the info, no that's not the result I was expecting. I'm trying to get my hands on an iPhone 6 for testing this myself - ideally what I need to do is connect it up to XCode and run through the Objective-C with the step-through debugger to see exactly what's going on.

is it possible to use the dev version as plugin (if yes, how?).

In the meantime, you can install the dev version directly from the git repo:

cordova plugin add https://github.com/dpa99c/cordova-diagnostic-plugin#dev

@HuaHub
Copy link
Author

HuaHub commented May 4, 2017

Thanks, please let me now when you have tested it!

@HuaHub
Copy link
Author

HuaHub commented May 4, 2017

I just installed the plugin from the dev branch, the new function getMotionAuthorizationStatus() returns that "Pedometer tracking not available on this device", even though I have an iPhone 6. Is this a bug or do I have to allow Pedometer usage or something?

@dpa99c
Copy link
Owner

dpa99c commented May 8, 2017

I still haven't managed to get hold of an iPhone 6 to test this.

getMotionAuthorizationStatus() calls checkMotionAuthorization() which in turn calls isMotionAuthorizationStatusAvailable() which returns true if the iOS API indicates pedometer tracking is available.

The message that you're getting is motionAuthorizationStatusUnavailableMessage, which is returned if isMotionAuthorizationStatusAvailable returns false.

What this suggest to me is that the pedometer event tracking API cannot be queried until motion tracking authorization has been granted.

Could you confirm this for me by doing the following:

  • Request and grant motion access by calling requestMotionAuthorization()
  • Then try again calling getMotionAuthorizationStatus() and see what the result is.
  • If, after granting motion access, getMotionAuthorizationStatus() returns granted, this indicates the pedometer API can only be queried after motion tracking has been authorized. If that's the case, it means it is not possible to check the motion tracking authorization status with having first requested motion authorization. In which case, this separation of requesting and checking cannot be done, and therefore it is better to return to the original implementation of requestAndCheckMotionAuthorization() (as currently on master branch).
  • However, if after granting motion access, getMotionAuthorizationStatus() still returns not_available, then either there's a bug in my logic or the iOS API is behaving in some unexpected way. In this case, I think it will have to wait until I can get my hands on an iPhone 6 and hook it up to XCode for step-through debugging.

@fcamblor
Copy link

fcamblor commented Jul 27, 2017

Hi guys,

Just hit the same issue as @HuaHub, and I have an iPhone 6S and 5c available for tests :-)

I just installed dev branch locally (more precisely, commit 96326ae) and made some iPhone 6S tests (see test scenario below) on a completely fresh app (I mean, without having provided any permission at the beginning of the test scenario).

As far as I understood :

  • It seems like the authorization status returned is not accurate (always returning status string .. I guess there is maybe a bad mapping somewhere)
  • getMotionAuthorizationStatus is triggering a user request, I wouldn't have expected it (I'm used to call the Location status API from your plugin and it doesn't involves a request)
  • When authorization is DENIED, no callback is called when calling requestMotionAuthorization(). Unfortunately, since the getMotionAuthorizationStatus() doesn't return an accurate status, we're not able to prevent calling requestMotionAuthorization() when status is denied.
  • It seems like there are regularly errors in CMErrorDomain Code:105 when requestMotionAuthorization() is called

Here are the detailed scenario. I tried to provide as much informations as possible ... but tell me if you're missing something and I'll try to help as best as I can.
Note that I tried to provide some timestamps in safari console outputs (viewable in screenshots) to help you correlate JS code execution with XCode logs.

Scenario 1 : calling getMotionAuthorizationStatus() first and multiple times after REFUSAL
JS Code executed :

cordova.plugins.diagnostic.isMotionAvailable(console.info, console.error)
> (i) true
cordova.plugins.diagnostic.isMotionAuthorizationStatusAvailable(console.info, console.error)
> (i) true
cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
> (i) status
console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED")
> (i) 27/07/2017 13:03:16 - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED
console.log(new Date()+" - Got 'status' string in success callback after that")
> (i) 27/07/2017 13:03:23 - Got 'status' string in success callback after that
cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
> (i) status
console.log(new Date()+" - Got 'status' string in success callback again")
> (i) 27/07/2017 13:04:27 - Got 'status' string in success callback again
cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
console.log(new Date()+" - Here, I never entered any callback, ever but had an error in XCode console")
> (i) 27/07/2017 13:04:54 - Here, I never entered any callback, ever but had an error in XCode console

XCode console output :

2017-07-27 13:02:25.831809+0200 XXXXXX APP[1313:629291] Bluetooth state changed: State unknown, update imminent.
2017-07-27 13:02:25.851858+0200 XXXXXX APP[1313:629291] Bluetooth state changed: Bluetooth is currently powered on and available to use.
2017-07-27 13:02:25.870771+0200 XXXXXX APP[1313:629291] Location authorization status changed to: not_determined
2017-07-27 13:03:14.714133+0200 XXXXXX APP[1313:629537] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:03:14.714255+0200 XXXXXX APP[1313:629537] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:03:14.714468+0200 XXXXXX APP[1313:629537] Motion access is authorized
2017-07-27 13:03:14.714551+0200 XXXXXX APP[1313:629537] Motion access is status
2017-07-27 13:03:16.383513+0200 XXXXXX APP[1313:629291] Thu Jul 27 2017 13:03:16 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED
2017-07-27 13:03:23.078266+0200 XXXXXX APP[1313:629291] Thu Jul 27 2017 13:03:23 GMT+0200 (CEST) - Got 'status' string in success callback after that
2017-07-27 13:03:53.625005+0200 XXXXXX APP[1313:629737] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:03:53.625125+0200 XXXXXX APP[1313:629737] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:03:53.625320+0200 XXXXXX APP[1313:629737] Motion access is authorized
2017-07-27 13:03:53.625390+0200 XXXXXX APP[1313:629737] Motion access is status
2017-07-27 13:04:27.252953+0200 XXXXXX APP[1313:629291] Thu Jul 27 2017 13:04:27 GMT+0200 (CEST) - Got 'status' string in success callback again
2017-07-27 13:04:37.747804+0200 XXXXXX APP[1313:629792] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:04:37.747921+0200 XXXXXX APP[1313:629792] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:04:37.748086+0200 XXXXXX APP[1313:629792] Motion access is authorized
2017-07-27 13:04:37.771442+0200 XXXXXX APP[1313:629785] CoreLocation: Error occurred while trying to retrieve motion state update: CMErrorDomain Code:105
2017-07-27 13:04:54.642582+0200 XXXXXX APP[1313:629291] Thu Jul 27 2017 13:04:54 GMT+0200 (CEST) - Here, I never entered any callback, ever but had an error in XCode console

Screenshot :
screen_shot_2017-07-27_at_13_05_25

Scenario 2 : calling requestMotionAuthorization() first and multiple times after REFUSAL
JS Code executed :

cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED")
> (i) 27/07/2017 13:09:54 - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED
console.log(new Date()+" - Once refused, I never entered any callback ever")
> (i) 27/07/2017 13:10:05 - Once refused, I never entered any callback ever
console.log(new Date()+" - Retrying a call to requestMotionAuthorization() to see how if same behaviour..")
> (i) 27/07/2017 13:10:12 - Retrying a call to requestMotionAuthorization() to see how if same behaviour..
cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
console.log(new Date()+" - Again, no callback called")
> (i) 27/07/2017 13:10:37 - Again, no callback called
cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
console.log(new Date()+" - When getMotionAuthorizationStatus() was called, same 'status' string returned as in Scenario #1")
> (i) 27/07/2017 13:10:37 - When getMotionAuthorizationStatus() was called, same 'status' string returned as in Scenario #1

XCode console output :

2017-07-27 13:09:42.508812+0200 XXXXXX APP[1320:630654] Bluetooth state changed: State unknown, update imminent.
2017-07-27 13:09:42.528927+0200 XXXXXX APP[1320:630654] Bluetooth state changed: Bluetooth is currently powered on and available to use.
2017-07-27 13:09:42.557089+0200 XXXXXX APP[1320:630654] Location authorization status changed to: not_determined
2017-07-27 13:09:53.002854+0200 XXXXXX APP[1320:631054] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:09:53.002973+0200 XXXXXX APP[1320:631054] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:09:53.003142+0200 XXXXXX APP[1320:631054] Motion access is authorized
2017-07-27 13:09:53.024116+0200 XXXXXX APP[1320:631054] CoreLocation: Error occurred while trying to retrieve motion state update: CMErrorDomain Code:105
2017-07-27 13:09:54.197906+0200 XXXXXX APP[1320:630654] Thu Jul 27 2017 13:09:54 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED
2017-07-27 13:10:05.683894+0200 XXXXXX APP[1320:630654] Thu Jul 27 2017 13:10:05 GMT+0200 (CEST) - Once refused, I never entered any callback ever
2017-07-27 13:10:12.866048+0200 XXXXXX APP[1320:630654] Thu Jul 27 2017 13:10:12 GMT+0200 (CEST) - Retrying a call to requestMotionAuthorization() to see how if same behaviour..
2017-07-27 13:10:21.219913+0200 XXXXXX APP[1320:631172] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:10:21.220034+0200 XXXXXX APP[1320:631172] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:10:21.220226+0200 XXXXXX APP[1320:631172] Motion access is authorized
2017-07-27 13:10:37.314817+0200 XXXXXX APP[1320:630654] Thu Jul 27 2017 13:10:37 GMT+0200 (CEST) - Again, no callback called
2017-07-27 13:10:44.230310+0200 XXXXXX APP[1320:631189] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:10:44.230423+0200 XXXXXX APP[1320:631189] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:10:44.230588+0200 XXXXXX APP[1320:631189] Motion access is authorized
2017-07-27 13:10:44.230658+0200 XXXXXX APP[1320:631189] Motion access is status
2017-07-27 13:10:51.663117+0200 XXXXXX APP[1320:630654] Thu Jul 27 2017 13:10:51 GMT+0200 (CEST) - When getMotionAuthorizationStatus() was called, same 'status' string returned as in Scenario #1

Screenshot :
screen_shot_2017-07-27_at_13_11_04

Scenario 3 : calling getMotionAuthorizationStatus() first and multiple times after ACCEPTANCE
JS Code executed :

cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
> (i) status
console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED")
> (i) 27/07/2017 13:14:18 - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED
console.log(new Date()+" - Got 'status' string in success callback after that")
> (i) 27/07/2017 13:14:26 - Got 'status' string in success callback after that
cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
> (i) status
console.log(new Date()+" - Got 'status' string in success callback again")
> (i) 27/07/2017 13:14:43 - Got 'status' string in success callback again
cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
> (i) status
console.log(new Date()+" - Got 'status' string in success callback")
> (i) 27/07/2017 13:15:00 - Got 'status' string in success callback

XCode console output :

2017-07-27 13:13:31.920812+0200 XXXXXX APP[1327:632040] Bluetooth state changed: State unknown, update imminent.
2017-07-27 13:13:31.947127+0200 XXXXXX APP[1327:632040] Bluetooth state changed: Bluetooth is currently powered on and available to use.
2017-07-27 13:13:31.988308+0200 XXXXXX APP[1327:632040] Location authorization status changed to: not_determined
2017-07-27 13:14:17.325108+0200 XXXXXX APP[1327:632134] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:14:17.325224+0200 XXXXXX APP[1327:632134] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:14:17.325392+0200 XXXXXX APP[1327:632134] Motion access is authorized
2017-07-27 13:14:17.325468+0200 XXXXXX APP[1327:632134] Motion access is status
2017-07-27 13:14:18.532751+0200 XXXXXX APP[1327:632040] Thu Jul 27 2017 13:14:18 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED
2017-07-27 13:14:26.799454+0200 XXXXXX APP[1327:632040] Thu Jul 27 2017 13:14:26 GMT+0200 (CEST) - Got 'status' string in success callback after that
2017-07-27 13:14:35.836820+0200 XXXXXX APP[1327:632408] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:14:35.836936+0200 XXXXXX APP[1327:632408] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:14:35.837128+0200 XXXXXX APP[1327:632408] Motion access is authorized
2017-07-27 13:14:35.837198+0200 XXXXXX APP[1327:632408] Motion access is status
2017-07-27 13:14:43.767927+0200 XXXXXX APP[1327:632040] Thu Jul 27 2017 13:14:43 GMT+0200 (CEST) - Got 'status' string in success callback again
2017-07-27 13:14:52.841111+0200 XXXXXX APP[1327:632487] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:14:52.841225+0200 XXXXXX APP[1327:632487] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:14:52.841390+0200 XXXXXX APP[1327:632487] Motion access is authorized
2017-07-27 13:14:52.872504+0200 XXXXXX APP[1327:632427] CoreLocation: Sending an un-cached message without first clearing the previously cached value
2017-07-27 13:14:52.873259+0200 XXXXXX APP[1327:632427] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:14:52.873324+0200 XXXXXX APP[1327:632427] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:14:52.873875+0200 XXXXXX APP[1327:632427] Motion access is authorized
2017-07-27 13:15:00.024129+0200 XXXXXX APP[1327:632040] Thu Jul 27 2017 13:15:00 GMT+0200 (CEST) - Got 'status' string in success callback

Screenshot :
pasted_image_27_07_2017__13_15

Scenario 4 : calling requestMotionAuthorization() first and multiple times after ACCEPTANCE
JS Code executed :

cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
> (i) status
console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED")
> (i) 27/07/2017 13:17:29 - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED
cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
> (i) status
console.log(new Date()+" - Got 'status' string in success callback")
> (i) 27/07/2017 13:17:44 - Got 'status' string in success callback
cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
> (i) status
console.log(new Date()+" - Got 'status' string in success callback")
> (i) 27/07/2017 13:17:59 - Got 'status' string in success callback

XCode console output :

2017-07-27 13:17:17.803343+0200 XXXXXX APP[1334:633232] Bluetooth state changed: State unknown, update imminent.
2017-07-27 13:17:17.825820+0200 XXXXXX APP[1334:633232] Bluetooth state changed: Bluetooth is currently powered on and available to use.
2017-07-27 13:17:17.856296+0200 XXXXXX APP[1334:633232] Location authorization status changed to: not_determined
2017-07-27 13:17:27.061586+0200 XXXXXX APP[1334:633381] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:17:27.061708+0200 XXXXXX APP[1334:633381] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:17:27.061895+0200 XXXXXX APP[1334:633381] Motion access is authorized
2017-07-27 13:17:27.078983+0200 XXXXXX APP[1334:633508] CoreLocation: Sending an un-cached message without first clearing the previously cached value
2017-07-27 13:17:27.080736+0200 XXXXXX APP[1334:633508] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:17:27.080808+0200 XXXXXX APP[1334:633508] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:17:27.080930+0200 XXXXXX APP[1334:633508] Motion access is authorized
2017-07-27 13:17:29.498694+0200 XXXXXX APP[1334:633232] Thu Jul 27 2017 13:17:29 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED
2017-07-27 13:17:36.950287+0200 XXXXXX APP[1334:633381] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:17:36.950402+0200 XXXXXX APP[1334:633381] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:17:36.950566+0200 XXXXXX APP[1334:633381] Motion access is authorized
2017-07-27 13:17:36.970607+0200 XXXXXX APP[1334:633508] CoreLocation: Sending an un-cached message without first clearing the previously cached value
2017-07-27 13:17:36.975167+0200 XXXXXX APP[1334:633508] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:17:36.975239+0200 XXXXXX APP[1334:633508] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:17:36.975381+0200 XXXXXX APP[1334:633508] Motion access is authorized
2017-07-27 13:17:44.084255+0200 XXXXXX APP[1334:633232] Thu Jul 27 2017 13:17:44 GMT+0200 (CEST) - Got 'status' string in success callback
2017-07-27 13:17:52.857443+0200 XXXXXX APP[1334:633381] CoreLocation: Error on message reply (Connection invalid)
2017-07-27 13:17:52.857557+0200 XXXXXX APP[1334:633381] CoreLocation: Unable to parse message when checking for availability!
2017-07-27 13:17:52.857724+0200 XXXXXX APP[1334:633381] Motion access is authorized
2017-07-27 13:17:52.857795+0200 XXXXXX APP[1334:633381] Motion access is status
2017-07-27 13:17:59.121677+0200 XXXXXX APP[1334:633232] Thu Jul 27 2017 13:17:59 GMT+0200 (CEST) - Got 'status' string in success callback

Screenshot :
pasted_image_27_07_2017__13_18

Regards,

@HuaHub
Copy link
Author

HuaHub commented Aug 15, 2017

Any luck?

@dpa99c
Copy link
Owner

dpa99c commented Aug 17, 2017

Note the commits related to separation of checking and requesting motion tracking authorization have been moved from the dev branch to motion_rework

@dpa99c
Copy link
Owner

dpa99c commented Aug 17, 2017

@fcamblor Apologies in delay in replying and thanks for the comprehensive feedback.

It seems like the authorization status returned is not accurate (always returning status string .. I guess > there is maybe a bad mapping somewhere)

Yes, looks like you are correct. Since I haven't had an iPhone 6+ to actually test with, I hadn't noticed, but it seems I accidentally passed the string "status" instead of the variable status as the return value here.

I'll push a commit to fix this. Will also review your other feedback ASAP.

@dpa99c
Copy link
Owner

dpa99c commented Aug 17, 2017

@fcamblor

getMotionAuthorizationStatus is triggering a user request, I wouldn't have expected it (I'm used to call the Location status API from your plugin and it doesn't involves a request)

Do you mean by "user request" that calling getMotionAuthorizationStatus asks the user for permission (i.e. "Motion & Fitness Tracking")? If so, then it's a show stopper for the separation of requesting and checking of motion authorization.

Just to recap, on the master branch of this plugin, requestAndCheckMotionAuthorization() both requests and checks motion authorization at the same time.

The motion_rework branch attempts to separate the requesting and checking, however if calling getMotionAuthorizationStatus results in asking the user for permission, it means that this separation is not possible since calling queryPedometerDataFromDate is resulting in the same user permission request as startActivityUpdatesToQueue.

@dpa99c dpa99c added sidelined No further action will be taken on this issue and removed TODO Something needs doing labels Sep 5, 2017
@jQrgen
Copy link

jQrgen commented Sep 18, 2017

+1

@fcamblor
Copy link

Do you mean by "user request" that calling getMotionAuthorizationStatus asks the user for permission (i.e. "Motion & Fitness Tracking")? If so, then it's a show stopper for the separation of requesting and checking of motion authorization.

Yep that's what I noted, see start of Scenario 1 :

cordova.plugins.diagnostic.isMotionAvailable(console.info, console.error)
> (i) true
cordova.plugins.diagnostic.isMotionAuthorizationStatusAvailable(console.info, console.error)
> (i) true
cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
> (i) status
console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED")

I tried to integrate your latest commit (506f670) on my project and I noted that compared to 96326ae, calling cordova.plugins.diagnostic.requestLocationAuthorization() never triggered any request for Location anymore (whereas before, it was working)
Wondering if you didn't made a regression in motion_rework branch.

@fcamblor
Copy link

Executed same scenario as before again, with the new 506f670 commit

Scenario 1 : calling getMotionAuthorizationStatus() first and multiple times after REFUSAL

> cordova.plugins.diagnostic.isMotionAvailable(console.info, console.error)
[Info] true
> cordova.plugins.diagnostic.isMotionAuthorizationStatusAvailable(console.info, console.error)
[Info] true
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED")
[Log] Wed Sep 27 2017 23:50:50 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED
> console.log(new Date()+" - Got 'authorized' string in success callback after that")
[Log] Wed Sep 27 2017 23:51:06 GMT+0200 (CEST) - Got 'authorized' string in success callback after that
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Got 'authorized' string in success callback again")
[Log] Wed Sep 27 2017 23:51:17 GMT+0200 (CEST) - Got 'authorized' string in success callback again
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
> console.log(new Date()+" - Here, I never entered any callback, ever but had an error in XCode console : [Activity] Error occurred while trying to retrieve motion state update: CMErrorDomain Code:105")
[Log] Wed Sep 27 2017 23:51:35 GMT+0200 (CEST) - Here, I never entered any callback, ever but had an error in XCode console : [Activity] Error occurred while trying to retrieve motion state update: CMErrorDomain Code:105

Some conclusions after this scenario :

  • isMotionAvailable and isMotionAuthorizationStatusAvailable are both returning true when motion authorization is not request. Second one seems weird (I would have expected false)
  • getMotionAuthorizationStatus shows an authorization prompt and returns authorized when I REFUSE the authorization prompt => sounds really weird
  • subsequent getMotionAuthorizationStatus calls don't show the authorization prompt (sounds legit) but keep returning authorized which is not the good status actually
  • requestMotionAuthorization call once we previously got the authorization prompt will never call any callback (neither success callback nor success callback)

Scenario 2 : calling requestMotionAuthorization() first and multiple times after REFUSAL

> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
> console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED")
[Log] Wed Sep 27 2017 23:59:32 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED
> console.log(new Date()+" - Once refused, I never entered any callback ever")
[Log] Wed Sep 27 2017 23:59:36 GMT+0200 (CEST) - Once refused, I never entered any callback ever
> console.log(new Date()+" - Retrying a call to requestMotionAuthorization() to see how if same behaviour..")
[Log] Wed Sep 27 2017 23:59:42 GMT+0200 (CEST) - Retrying a call to requestMotionAuthorization() to see how if same behaviour..
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
> console.log(new Date()+" - Again, no callback called")
[Log] Wed Sep 27 2017 23:59:52 GMT+0200 (CEST) - Again, no callback called
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - When getMotionAuthorizationStatus() was called, same 'authorized' string returned as in Scenario #1")
[Log] Thu Sep 28 2017 00:00:08 GMT+0200 (CEST) - When getMotionAuthorizationStatus() was called, same 'authorized' string returned as in Scenario #1

No new conclusions than previously here :

  • requestMotionAuthorization shows an authorization prompt which sounds OK ... however once the prompt is asked, no callback is called (neither success callback nor error callback)
  • getMotionAuthorizationStatus keeps returning authorized whereas I REFUSED the permission

Scenario 3 : calling getMotionAuthorizationStatus() first and multiple times after ACCEPTANCE

> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED")
[Log] Thu Sep 28 2017 00:04:02 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED
> console.log(new Date()+" - Got 'authorized' string in success callback after that")
[Log] Thu Sep 28 2017 00:04:12 GMT+0200 (CEST) - Got 'authorized' string in success callback after that
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Got 'authorized' string in success callback again")
[Log] Thu Sep 28 2017 00:04:25 GMT+0200 (CEST) - Got 'authorized' string in success callback again
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Got 'authorized' string in success callback")
[Log] Thu Sep 28 2017 00:04:37 GMT+0200 (CEST) - Got 'authorized' string in success callback

Everything sounds OK here

Scenario 4 : calling requestMotionAuthorization() first and multiple times after ACCEPTANCE

> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED")
[Log] Thu Sep 28 2017 00:07:37 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Got 'authorized' string in success callback")
[Log] Thu Sep 28 2017 00:07:50 GMT+0200 (CEST) - Got 'authorized' string in success callback
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Got 'authorized' string in success callback")
[Log] Thu Sep 28 2017 00:08:01 GMT+0200 (CEST) - Got 'authorized' string in success callback

Here again, everything sounds OK

@dpa99c
Copy link
Owner

dpa99c commented Sep 28, 2017

@fcamblor Really appreciate your testing this and being so detailed in your results.

getMotionAuthorizationStatus shows an authorization prompt and returns authorized when I REFUSE the authorization prompt => sounds really weird

This is the deal breaker: it means that calling queryPedometerDataFromDate prompts for authorization the same as calling startActivityUpdatesToQueue.

It means there's no native way (at least that I know of) to query the authorization status without first requesting authorization (i.e. you cannot check if NOT_REQUESTED vs GRANTED/DENIED).

It is of course possible to detect GRANTED vs DENIED after authorization has been intially requested (e.g. the user changes permission to DENIED in Settings or denies in permission request dialog). This is what requestAndCheckMotionAuthorization() on the master branch is currently doing.

One possibility would be for the plugin to store its own internal persistent flag which indicates whether requestAndCheckMotionAuthorization() has been called since the app was installed. If it has, then the status must be GRANTED or DENIED. If not, we can assume it is NOT_REQUESTED. The logic would be:

function getMotionAuthorizationStatus(){
    if(!requestAndCheckMotionAuthorizationHasBeenCalled){
        return NOT_REQUESTED;
    }else{
        return requestAndCheckMotionAuthorization(); // GRANTED or DENIED
    }
}

Does that make sense?

@fcamblor
Copy link

Yep, at least the "least worst option" to my POV :-)

I'd maybe change your snippet to

    if(!requestAndCheckMotionAuthorizationHasBeenCalled){
        return NOT_REQUESTED;
    } else {
        requestAndCheckMotionAuthorizationHasBeenCalled = true;
        return requestAndCheckMotionAuthorization(); // GRANTED or DENIED
    }

BTW, another important issue in my tests are the returned status doesn't seem accurate (today, authorized is returned even when I denied the authorization, which sounds weird).

@fcamblor
Copy link

Moreover, I'm not sure if this is the topic here (or if I should file a dedicated issue for this), but on version 3.6.7, when I call requestAndCheckMotionAuthorization and I REFUSE motion authorization, no callback is called (neither success callback nor error callback) :

// Just installed my application, so no authorization has been granted yet
> cordova.plugins.diagnostic.requestAndCheckMotionAuthorization(console.info, console.error)
// here, I choose "REFUSE" on the popup prompt and ...
// ...
// nothing happens, nothing is written into the console, ever

The only workaround would be to put a timeout to effectively resolve (or reject) a promise wrapper around requestAndCheckMotionAuthorization() call but heh ... that would be ugly (and determining timeout duration is complicated ... on one hand it shouldn't be too fast to let the user take time to answer the question and on the other hand, it shouldn't be too long as once the answer has given a DENY answer, nothing will happen until this timeout)

@dpa99c dpa99c closed this as completed in bbe4220 Oct 7, 2017
@dpa99c
Copy link
Owner

dpa99c commented Oct 7, 2017

A bit premature...

@dpa99c dpa99c reopened this Oct 7, 2017
@dpa99c
Copy link
Owner

dpa99c commented Oct 7, 2017

@fcamblor I think I've finally sorted this out. Please try the version on the dev branch:

cordova plugin add https://github.com/dpa99c/cordova-diagnostic-plugin#dev

I've updated the dev branch of the example project to test/validate the plugin implementation and it's looking good to me:

git clone -b dev https://github.com/dpa99c/cordova-diagnostic-plugin-example

@fcamblor
Copy link

fcamblor commented Oct 7, 2017

Cool !

Will have a look at it today, will give you my feedback :)

@fcamblor
Copy link

fcamblor commented Oct 7, 2017

Just made my tests... it seems OK to me, this is a nice work @dpa99c 👍

Scenario 1 : calling getMotionAuthorizationStatus/requestMotionAuthorization() first and multiple times after REFUSAL

> cordova.plugins.diagnostic.isMotionAvailable(console.info, console.error)
[Info] true
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] not_requested
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] not_requested
> console.log(new Date()+" - Yeah ! \\o/")
[Log] Sat Oct 07 2017 14:57:50 GMT+0200 (CEST) - Yeah ! \o/
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Info] denied
> console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED. Then I got 'denied' status in successcallback which sounds OK :)")
[Log] Sat Oct 07 2017 14:57:56 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I REFUSED. Then I got 'denied' status in successcallback  which sounds OK :)
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] denied
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Error] requestMotionAuthorization() has already been called and can only be called once after app installation

Which completly OK for me :)

Scenario 2 : calling getMotionAuthorizationStatus/requestMotionAuthorization() first and multiple times after ACCEPTANCE

> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] not_requested
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Info] authorized
> console.log(new Date()+" - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED. Then I got 'authorized' status in successcallback which sounds OK :)")
[Log] Sat Oct 07 2017 15:05:56 GMT+0200 (CEST) - Here, I got a popup asking me if my app was allowed to track motion on my device. I ACCEPTED. Then I got 'authorized' status in successcallback  which sounds OK :)
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] authorized
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Error] requestMotionAuthorization() has already been called and can only be called once after app installation

Here again, sounds OK.

To me, actual dev behaviour is perfect :-)

@dpa99c
Copy link
Owner

dpa99c commented Oct 7, 2017

@fcamblor Thanks for getting back so quickly. OK, I'm going to move ahead and publish it as v3.7.0 to npm. If any bugs do should up, let me know and I'll push out a 3.7.1 patch release.

@dpa99c
Copy link
Owner

dpa99c commented Oct 7, 2017

OK, published as [email protected]

@dpa99c dpa99c closed this as completed Oct 7, 2017
@fcamblor
Copy link

fcamblor commented Oct 7, 2017

Thanks for the quick release (and the hard work) :-)

@fcamblor
Copy link

fcamblor commented Oct 9, 2017

@dpa99c Just hit an edge case we didn't discussed earlier I guess.

In the Settings > Privacy > Motion & Fitness setting, we can have 2 different permissions : a "global" one and a "per app" one
img_0006

In my previous scenario, I always covered the "per app" permission case (when I was "denying" the permission, it was impacted on the "per app" level)

Now, if I take a "globally DENIED" Motion & Fitness permission, I mean :
img_0008

Then, in that case :

> cordova.plugins.diagnostic.isMotionAvailable(console.info, console.error)
[Info] true
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] not_requested
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Info] denied
> cordova.plugins.diagnostic.requestMotionAuthorization(console.info, console.error)
[Error] requestMotionAuthorization() has already been called and can only be called once after app installation
> cordova.plugins.diagnostic.getMotionAuthorizationStatus(console.info, console.error)
[Info] denied

It seems legit, however, no prompt has been shown to the user at any step of code above, while getMotionAuthorizationStatus() returns a not_requested status.

My concern is : I'm wondering if there shouldn't be a dedicated status for this particular case (something like not_requestable) because currently, I implemented something like what you mentioned into the doc, I mean :

if(status === cordova.plugins.diagnostic.motionStatus.NOT_REQUESTED){
    cordova.plugins.diagnostic.requestMotionAuthorization(successCallback, errorCallback);
}

However, this code will encourage calling requestMotionAuthorization() multiple times as we won't have any possibility to know if we're on the "not requested because globally not allowed" or on the "not requested because not allowed for the app" case.
Thus, in the former case, we're going to hit requestMotionAuthorization()'s errorCallback for subsequent calls, unless we handle ourselves a flag telling if we already made a first requestMotionAuthorization() call or not.

not sure if I'm 100% clear here, don't hesitate to ask for precisions

@dpa99c
Copy link
Owner

dpa99c commented Oct 9, 2017

The problem here is that Apple has not given us sufficient tools to do the job.

There's no way to tell if, after calling requestMotionAuthorization() results in a denied status that this was because the user:

  • was shown the native permissions dialog and refused permission
    • in which case, we can never ask for permission again: the dialog will not be shown, user must manually enable for our app via Settings
  • or if, as in your example above, this was because the user had the global Fitness Tracking switch turned off
    • in which case, the user was never shown a dialog, so we are allowed to ask for permission again

I'm also not sure how much of an edge case this is, i.e. how many iOS users actually keep the global Fitness Tracking switch off.

Anyway, the solution will be to allow an extra parameter to be passed to requestMotionAuthorization() which enables you to bypass the check for "call only once" and you can call it again, i.e.:

requestMotionAuthorization(success, error, true); // allows multiple calls
requestMotionAuthorization(success, error, false); // allows only 1 call, then errors

I'll publish a patch version with this shortly.

However, I also recommend if permission is denied, using a dialog to explain both cases to user, e.g.:

This app has not be granted permission to use Motion Tracking and it needs permission because (blah, blah, blah).
If you pressed the "Don't Allow" button just now, you'll need to go into the Settings app and enable permission for this app manually.
If you've got the Fitness and Tracking switch turned off, you'll need to (blah, blah, blah).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
sidelined No further action will be taken on this issue
Projects
None yet
Development

No branches or pull requests

4 participants