Skip to main content
Unveilydocs

Camera & Gallery

Pick an image from the device camera or photo gallery and pass it to your web page.

Overview

When the user takes a photo or selects an image from the gallery, the SDK delivers the result to your JS callback. EXIF orientation is corrected automatically.

FeatureAndroidiOS
Camera capture✓ (Android 7.0+)
Gallery pick✓ (Android 7.0+)

Supported Plan: Basic and above

Prerequisites

Android

The camera permission ships commented out in AndroidManifest.xml. Uncomment it to enable camera capture. (Gallery pick uses the system document picker and needs no extra permission.)

<uses-permission android:name="android.permission.CAMERA" />

iOS

Add the following usage descriptions to your Info.plist.

<key>NSCameraUsageDescription</key>
<string>Camera access is required to take photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo library access is required to select images.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Photo library access is required to save captured photos.</string>

Triggering File Selection

unveilyBridge.openFileSelector(cb) is a core method. cb is a callback function-name string that receives the result. Omit it to use the default 'onFileResult'.

// Open the camera / gallery chooser dialog
window.unveilyBridge.openFileSelector("onFileResult");

You can pass image-processing options as a JSON string in the second argument.

window.unveilyBridge.openFileSelector("onFileResult", JSON.stringify({
  maxDimension: 1600,     // Max pixels on the long edge (downscaled, aspect kept)
  quality: 0.8,           // JPEG compression quality (0–1)
  maxBytes: 2000000       // Max file size in bytes
}));
OptionTypeDescription
maxDimensionnumberMax pixels on the long edge. Downscaled proportionally if exceeded
qualitynumberJPEG compression quality (0–1)
maxBytesnumberMax byte size of the resulting image

unveilyBridge is injected into the WebView automatically. No initialization is required.


Handling the Callback

When the user completes a selection, the SDK calls your callback with positional arguments (base64OrUri, isSuccess). The first argument is the image data (a Base64 string on Android); the second is the success flag.

function onFileResult(base64OrUri, isSuccess) {
  if (!isSuccess) {
    console.log("Selection failed or cancelled:", base64OrUri);
    return;
  }

  // Use as-is if already a URI/data URI, otherwise wrap raw Base64 in a data URI
  const src = /^(https?:|file:|content:|data:)/.test(base64OrUri)
    ? base64OrUri
    : `data:image/jpeg;base64,${base64OrUri}`;

  document.getElementById("preview").src = src;
}

Callback parameters

ParameterTypeDescription
base64OrUristringImage data on success (raw Base64 on Android); error message on failure
isSuccessbooleanWhether the selection succeeded

EXIF orientation data in camera photos is corrected automatically by the SDK. Images captured in portrait mode are delivered right-side up without any 90-degree rotation.


Error Handling

When isSuccess === false, the first argument contains a message describing the situation.

SituationisSuccess
User cancelledfalse
Camera/gallery permission deniedfalse
Image processing failedfalse

Server Image Upload

On Android, the value passed to the callback is a plain Base64 string with no data URI prefix. You can decode it on the server directly without any preprocessing.

function onFileResult(base64OrUri, isSuccess) {
  if (!isSuccess) return;
  fetch('/api/upload', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ base64: base64OrUri })
  });
}
const sharp = require('sharp');

app.post('/api/upload', async (req, res) => {
  const { base64 } = req.body;
  const buffer = Buffer.from(base64, 'base64');

  const filename = `${Date.now()}.jpg`;
  await sharp(buffer).resize(800).jpeg({ quality: 85 }).toFile(`uploads/${filename}`);

  res.json({ url: `/uploads/${filename}` });
});
[HttpPost("upload")]
public async Task<IActionResult> Upload([FromBody] UploadRequest request)
{
    var bytes = Convert.FromBase64String(request.Base64);
    var filename = $"{Guid.NewGuid()}.jpg";
    var path = Path.Combine("wwwroot/uploads", filename);

    await System.IO.File.WriteAllBytesAsync(path, bytes);
    return Ok(new { url = $"/uploads/{filename}" });
}
@PostMapping("/api/upload")
public ResponseEntity<?> upload(@RequestBody UploadRequest request) {
    byte[] bytes = Base64.getDecoder().decode(request.getBase64());

    String filename = UUID.randomUUID() + ".jpg";
    Path path = Paths.get("uploads/" + filename);
    Files.write(path, bytes);

    return ResponseEntity.ok(Map.of("url", "/uploads/" + filename));
}
<?php
$data = json_decode(file_get_contents('php://input'), true);
$imageData = base64_decode($data['base64']);

$filename = uniqid() . '.jpg';
file_put_contents('uploads/' . $filename, $imageData);

echo json_encode(['url' => '/uploads/' . $filename]);
<%
Dim base64Data, filename
base64Data = Request.Form("base64")

' Decode Base64 (using MSXML)
Dim xmlObj
Set xmlObj = CreateObject("MSXML2.DOMDocument")
xmlObj.LoadXML "<root>" & base64Data & "</root>"
Dim base64Node
Set base64Node = xmlObj.selectSingleNode("root")

filename = "uploads/" & Replace(CStr(Now), ":", "-") & ".jpg"
' Handle file saving (using Scripting.FileSystemObject, etc.)

Response.ContentType = "application/json"
Response.Write "{""url"": ""/uploads/" & filename & """}"
%>

On this page