Camera not working in your field app? Check permissions
A field app's camera button did nothing because two permission faults hid behind swallowed errors. What to check on your own app, and what we fixed.
If the camera button in your field app does nothing when staff tap it, the usual cause is camera permission: the app never declared it, never asked for it, or the phone has been told never to ask again. The first thing to check is what the app does when a tap fails. If the answer is nothing, you cannot tell a broken feature from a user who refused.
Here is how that looked in one field app, and what fixing it took.
Your field staff stand at a board with the app open and tap the Camera button. Nothing happens. No error, no message. They tap again. From the crew’s side it looks like a broken phone, or like they are doing it wrong.
A photograph taken at the board is the evidence behind the advertiser’s invoice. A dead camera button means proof that never reaches the system, and nobody is told. There was a second, quieter problem in the same area: a board could only be photographed while a campaign was booked on it, which is backwards for anyone surveying a new or empty board.
What was actually going on
There were two faults, and either was enough to kill the button on its own.
The first was the one actually firing. The app targeted a recent version of Android, and on that version an app has to declare in advance that it intends to open a camera. Ours never did, so every tap threw an error. We proved it by comparing the merged manifest before and after, rather than reasoning about it.
The second was waiting behind the first. The app declared that it uses the camera but never asked the user for permission. The camera library refuses to open when permission is declared but not held. On a clean install, the permission was not granted.
Both stayed invisible for the same reason: the code handling the tap quietly returned on any error. A dead button and a user who cancelled the camera looked identical.
What we changed
The declaration and the permission request were added. Errors are now shown. Each way a person can respond has its own outcome. If they refuse the camera, the app explains why it is needed and points them to the gallery. If they refuse location, which is not essential, it says what is lost and gets out of the way.
The third case is the one that mattered. Once Android records “never ask again”, the request returns instantly, forever, so the button can never work again. The app now offers to open its settings page instead.
Site photo capture was added to the phone. The server already accepted site photographs; only the phone never called it. It was checked on a real handset against production data.
What it did not fix
The log does not say how long the button was dead in the field, or how many proof photographs were lost. Nobody could count photographs that were never sent.
Nor does the fix make a person grant permission. A user who refuses everything and never opens settings still cannot take a picture. The app now tells them why.
How to check your own app
- Install the app fresh on a clean phone, tap the camera button and see whether it asks for permission.
- Refuse once and tap again. The app should explain why it needs the camera and offer another route.
- Refuse for good (Android calls this “never ask again”) and tap again. The app should offer to open its settings page, because the request will now fail instantly for ever.
- Run the same test on the newest Android version your staff use, not only on an old handset.
- Ask your supplier what the app does when something fails. If the answer is “nothing”, you cannot tell a broken feature from a user who changed their mind, and neither can your staff.
Granted, refused once and refused for good are three different situations. Test all three, not only the happy one.
Where this ends up
The camera is how a field crew proves the work in AdBoard, the system that runs for Gold Sign Media, an outdoor media operator. A tap now either opens the camera or tells the person what to do next.